tailcfg: introduce HostinfoView

Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
Maisem Ali 2022-02-14 21:20:41 -08:00 committed by Maisem Ali
parent 2ff481ff10
commit 53998e26a6
2 changed files with 201 additions and 0 deletions

View File

@ -8,6 +8,7 @@ package tailcfg
import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"reflect"
@ -19,6 +20,7 @@ import (
"tailscale.com/types/key"
"tailscale.com/types/opt"
"tailscale.com/types/structs"
"tailscale.com/types/views"
"tailscale.com/util/dnsname"
)
@ -454,6 +456,128 @@ type Hostinfo struct {
// require changes to Hostinfo.Equal.
}
// View returns a read-only accessor for the Hostinfo object.
func (hi *Hostinfo) View() HostinfoView { return HostinfoView{hi} }
// HostinfoView is a read-only accessor for Hostinfo.
// See Hostinfo.
type HostinfoView struct {
// It is named distinctively to make you think of how dangerous it is to escape
// to callers. You must not let callers be able to mutate it.
ж *Hostinfo
}
func (v HostinfoView) MarshalJSON() ([]byte, error) {
return json.Marshal(v.ж)
}
func (v *HostinfoView) UnmarshalJSON(b []byte) error {
if v.ж != nil {
return errors.New("HostinfoView is already initialized")
}
if len(b) == 0 {
return nil
}
hi := &Hostinfo{}
if err := json.Unmarshal(b, hi); err != nil {
return err
}
v.ж = hi
return nil
}
// Valid reports whether the underlying value is not nil.
func (v HostinfoView) Valid() bool { return v.ж != nil }
// AsStruct returns a deep-copy of the underlying value.
func (v HostinfoView) AsStruct() *Hostinfo { return v.ж.Clone() }
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) Equal(h2 HostinfoView) bool { return v.ж.Equal(h2.ж) }
func (v HostinfoView) RoutableIPs() views.IPPrefixSlice {
return views.IPPrefixSliceOf(v.ж.RoutableIPs)
}
func (v HostinfoView) RequestTags() views.StringSlice {
return views.StringSliceOf(v.ж.RequestTags)
}
func (v HostinfoView) Services() ServiceSlice {
return ServiceSliceOf(v.ж.Services)
}
func (v HostinfoView) NetInfo() NetInfoView { return v.ж.NetInfo.View() }
// ServiceSlice is a read-only accessor for a slice of Services
type ServiceSlice struct {
// It is named distinctively to make you think of how dangerous it is to escape
// to callers. You must not let callers be able to mutate it.
ж []Service
}
// ServiceSliceOf returns a ServiceSlice for the provided slice.
func ServiceSliceOf(x []Service) ServiceSlice { return ServiceSlice{x} }
// Len returns the length of the slice.
func (v ServiceSlice) Len() int { return len(v.ж) }
// At returns the Service at index `i` of the slice.
func (v ServiceSlice) At(i int) Service { return v.ж[i] }
// Append appends the underlying slice values to dst.
func (v ServiceSlice) Append(dst []Service) []Service {
return append(dst, v.ж...)
}
// AsSlice returns a copy of underlying slice.
func (v ServiceSlice) AsSlice() []Service {
return v.Append(v.ж[:0:0])
}
// NetInfoView is a read-only accessor for NetInfo.
// See NetInfo.
type NetInfoView struct {
// It is named distinctively to make you think of how dangerous it is to escape
// to callers. You must not let callers be able to mutate it.
ж *NetInfo
}
// Valid reports whether the underlying value is not nil.
func (v NetInfoView) Valid() bool { return v.ж != nil }
// AsStruct returns a deep-copy of the underlying value.
func (v NetInfoView) AsStruct() *NetInfo { return v.ж.Clone() }
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 }
func (v NetInfoView) String() string { return v.ж.String() }
// DERPLatencyForEach calls fn for each value in the DERPLatency map.
func (v NetInfoView) DERPLatencyForEach(fn func(k string, v float64)) {
for k, v := range v.ж.DERPLatency {
fn(k, v)
}
}
// NetInfo contains information about the host's network state.
type NetInfo struct {
// MappingVariesByDestIP says whether the host's NAT mappings
@ -532,6 +656,9 @@ func (ni *NetInfo) portMapSummary() string {
return prefix + conciseOptBool(ni.UPnP, "U") + conciseOptBool(ni.PMP, "M") + conciseOptBool(ni.PCP, "C")
}
// View returns a read-only accessor for the NetInfo object.
func (ni *NetInfo) View() NetInfoView { return NetInfoView{ni} }
func conciseOptBool(b opt.Bool, trueVal string) string {
if b == "" {
return "_"

74
types/views/views.go Normal file
View File

@ -0,0 +1,74 @@
// Copyright (c) 2022 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.
// Package views provides read-only accessors for commonly used
// value types.
package views
import (
"inet.af/netaddr"
"tailscale.com/net/tsaddr"
)
// StringSlice is a read-only accessor for a slice of strings.
type StringSlice struct {
// It is named distinctively to make you think of how dangerous it is to escape
// to callers. You must not let callers be able to mutate it.
ж []string
}
// StringSliceOf returns a StringSlice for the provided slice.
func StringSliceOf(x []string) StringSlice { return StringSlice{x} }
// Len returns the length of the slice.
func (v StringSlice) Len() int { return len(v.ж) }
// At returns the string at index `i` of the slice.
func (v StringSlice) At(i int) string { return v.ж[i] }
// AppendTo appends the underlying slice values to dst.
func (v StringSlice) AppendTo(dst []string) []string {
return append(dst, v.ж...)
}
// AsSlice returns a copy of underlying slice.
func (v StringSlice) AsSlice() []string {
return v.AppendTo(v.ж[:0:0])
}
// IPPrefixSlice is a read-only accessor for a slice of netaddr.IPPrefix.
type IPPrefixSlice struct {
// It is named distinctively to make you think of how dangerous it is to escape
// to callers. You must not let callers be able to mutate it.jd
ж []netaddr.IPPrefix
}
// IPPrefixSliceOf returns a IPPrefixSlice for the provided slice.
func IPPrefixSliceOf(x []netaddr.IPPrefix) IPPrefixSlice { return IPPrefixSlice{x} }
// Len returns the length of the slice.
func (v IPPrefixSlice) Len() int { return len(v.ж) }
// At returns the IPPrefix at index `i` of the slice.
func (v IPPrefixSlice) At(i int) netaddr.IPPrefix { return v.ж[i] }
// Append appends the underlying slice values to dst.
func (v IPPrefixSlice) AppendTo(dst []netaddr.IPPrefix) []netaddr.IPPrefix {
return append(dst, v.ж...)
}
// AsSlice returns a copy of underlying slice.
func (v IPPrefixSlice) AsSlice() []netaddr.IPPrefix {
return v.AppendTo(v.ж[:0:0])
}
// PrefixesContainsIP reports whether any IPPrefix contains IP.
func (v IPPrefixSlice) ContainsIP(ip netaddr.IP) bool {
return tsaddr.PrefixesContainsIP(v.ж, ip)
}
// PrefixesContainsFunc reports whether f is true for any IPPrefix in the slice.
func (v IPPrefixSlice) ContainsFunc(f func(netaddr.IPPrefix) bool) bool {
return tsaddr.PrefixesContainsFunc(v.ж, f)
}