mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
hostinfo, tailcfg: split Hostinfo.OSVersion into separate fields
Stop jamming everything into one string. Fixes #5578 Change-Id: I7dec8d6c073bddc7dc5f653e3baf2b4bf6b68378 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
708b7bff3d
commit
d5e7e3093d
@ -12,6 +12,7 @@
|
|||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -31,25 +32,68 @@ func New() *tailcfg.Hostinfo {
|
|||||||
hostname, _ := os.Hostname()
|
hostname, _ := os.Hostname()
|
||||||
hostname = dnsname.FirstLabel(hostname)
|
hostname = dnsname.FirstLabel(hostname)
|
||||||
return &tailcfg.Hostinfo{
|
return &tailcfg.Hostinfo{
|
||||||
IPNVersion: version.Long,
|
IPNVersion: version.Long,
|
||||||
Hostname: hostname,
|
Hostname: hostname,
|
||||||
OS: version.OS(),
|
OS: version.OS(),
|
||||||
OSVersion: GetOSVersion(),
|
OSVersion: GetOSVersion(),
|
||||||
Desktop: desktop(),
|
Container: lazyInContainer.Get(),
|
||||||
Package: packageTypeCached(),
|
Distro: condCall(distroName),
|
||||||
GoArch: runtime.GOARCH,
|
DistroVersion: condCall(distroVersion),
|
||||||
GoVersion: runtime.Version(),
|
DistroCodeName: condCall(distroCodeName),
|
||||||
DeviceModel: deviceModel(),
|
Env: string(GetEnvType()),
|
||||||
Cloud: string(cloudenv.Get()),
|
Desktop: desktop(),
|
||||||
|
Package: packageTypeCached(),
|
||||||
|
GoArch: runtime.GOARCH,
|
||||||
|
GoVersion: runtime.Version(),
|
||||||
|
DeviceModel: deviceModel(),
|
||||||
|
Cloud: string(cloudenv.Get()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-nil on some platforms
|
// non-nil on some platforms
|
||||||
var (
|
var (
|
||||||
osVersion func() string
|
osVersion func() string
|
||||||
packageType func() string
|
packageType func() string
|
||||||
|
distroName func() string
|
||||||
|
distroVersion func() string
|
||||||
|
distroCodeName func() string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func condCall[T any](fn func() T) T {
|
||||||
|
var zero T
|
||||||
|
if fn == nil {
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
return fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
lazyInContainer = &lazyAtomicValue[opt.Bool]{f: ptrTo(inContainer)}
|
||||||
|
)
|
||||||
|
|
||||||
|
func ptrTo[T any](v T) *T { return &v }
|
||||||
|
|
||||||
|
type lazyAtomicValue[T any] struct {
|
||||||
|
// f is a pointer to a fill function. If it's nil or points
|
||||||
|
// to nil, then Get returns the zero value for T.
|
||||||
|
f *func() T
|
||||||
|
|
||||||
|
once sync.Once
|
||||||
|
v T
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *lazyAtomicValue[T]) Get() T {
|
||||||
|
v.once.Do(v.fill)
|
||||||
|
return v.v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *lazyAtomicValue[T]) fill() {
|
||||||
|
if v.f == nil || *v.f == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v.v = (*v.f)()
|
||||||
|
}
|
||||||
|
|
||||||
// GetOSVersion returns the OSVersion of current host if available.
|
// GetOSVersion returns the OSVersion of current host if available.
|
||||||
func GetOSVersion() string {
|
func GetOSVersion() string {
|
||||||
if s, _ := osVersionAtomic.Load().(string); s != "" {
|
if s, _ := osVersionAtomic.Load().(string); s != "" {
|
||||||
@ -179,22 +223,23 @@ func getEnvType() EnvType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// inContainer reports whether we're running in a container.
|
// inContainer reports whether we're running in a container.
|
||||||
func inContainer() bool {
|
func inContainer() opt.Bool {
|
||||||
if runtime.GOOS != "linux" {
|
if runtime.GOOS != "linux" {
|
||||||
return false
|
return ""
|
||||||
}
|
}
|
||||||
var ret bool
|
var ret opt.Bool
|
||||||
|
ret.Set(false)
|
||||||
lineread.File("/proc/1/cgroup", func(line []byte) error {
|
lineread.File("/proc/1/cgroup", func(line []byte) error {
|
||||||
if mem.Contains(mem.B(line), mem.S("/docker/")) ||
|
if mem.Contains(mem.B(line), mem.S("/docker/")) ||
|
||||||
mem.Contains(mem.B(line), mem.S("/lxc/")) {
|
mem.Contains(mem.B(line), mem.S("/lxc/")) {
|
||||||
ret = true
|
ret.Set(true)
|
||||||
return io.EOF // arbitrary non-nil error to stop loop
|
return io.EOF // arbitrary non-nil error to stop loop
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
lineread.File("/proc/mounts", func(line []byte) error {
|
lineread.File("/proc/mounts", func(line []byte) error {
|
||||||
if mem.Contains(mem.B(line), mem.S("fuse.lxcfs")) {
|
if mem.Contains(mem.B(line), mem.S("fuse.lxcfs")) {
|
||||||
ret = true
|
ret.Set(true)
|
||||||
return io.EOF
|
return io.EOF
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -8,48 +8,58 @@
|
|||||||
package hostinfo
|
package hostinfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
"tailscale.com/version/distro"
|
"tailscale.com/version/distro"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
osVersion = osVersionFreebsd
|
osVersion = lazyOSVersion.Get
|
||||||
|
distroName = distroNameFreeBSD
|
||||||
|
distroVersion = distroVersionFreeBSD
|
||||||
}
|
}
|
||||||
|
|
||||||
func osVersionFreebsd() string {
|
var (
|
||||||
un := unix.Utsname{}
|
lazyVersionMeta = &lazyAtomicValue[versionMeta]{f: ptrTo(freebsdVersionMeta)}
|
||||||
|
lazyOSVersion = &lazyAtomicValue[string]{f: ptrTo(osVersionFreeBSD)}
|
||||||
|
)
|
||||||
|
|
||||||
|
func distroNameFreeBSD() string {
|
||||||
|
return lazyVersionMeta.Get().DistroName
|
||||||
|
}
|
||||||
|
|
||||||
|
func distroVersionFreeBSD() string {
|
||||||
|
return lazyVersionMeta.Get().DistroVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
type versionMeta struct {
|
||||||
|
DistroName string
|
||||||
|
DistroVersion string
|
||||||
|
DistroCodeName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func osVersionFreeBSD() string {
|
||||||
|
var un unix.Utsname
|
||||||
unix.Uname(&un)
|
unix.Uname(&un)
|
||||||
|
return unix.ByteSliceToString(un.Release[:])
|
||||||
|
}
|
||||||
|
|
||||||
var attrBuf strings.Builder
|
func freebsdVersionMeta() (meta versionMeta) {
|
||||||
attrBuf.WriteString("; version=")
|
d := distro.Get()
|
||||||
attrBuf.WriteString(unix.ByteSliceToString(un.Release[:]))
|
meta.DistroName = string(d)
|
||||||
attr := attrBuf.String()
|
switch d {
|
||||||
|
|
||||||
version := "FreeBSD"
|
|
||||||
switch distro.Get() {
|
|
||||||
case distro.Pfsense:
|
case distro.Pfsense:
|
||||||
b, _ := os.ReadFile("/etc/version")
|
b, _ := os.ReadFile("/etc/version")
|
||||||
version = fmt.Sprintf("pfSense %s", b)
|
meta.DistroVersion = string(bytes.TrimSpace(b))
|
||||||
case distro.OPNsense:
|
case distro.OPNsense:
|
||||||
b, err := exec.Command("opnsense-version").Output()
|
b, _ := exec.Command("opnsense-version").Output()
|
||||||
if err == nil {
|
meta.DistroVersion = string(bytes.TrimSpace(b))
|
||||||
version = string(b)
|
|
||||||
} else {
|
|
||||||
version = "OPNsense"
|
|
||||||
}
|
|
||||||
case distro.TrueNAS:
|
case distro.TrueNAS:
|
||||||
b, err := os.ReadFile("/etc/version")
|
b, _ := os.ReadFile("/etc/version")
|
||||||
if err == nil {
|
meta.DistroVersion = string(bytes.TrimSpace(b))
|
||||||
version = string(b)
|
|
||||||
} else {
|
|
||||||
version = "TrueNAS"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// the /etc/version files end in a newline
|
return
|
||||||
return fmt.Sprintf("%s%s", strings.TrimSuffix(version, "\n"), attr)
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -21,14 +20,39 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
osVersion = osVersionLinux
|
osVersion = lazyOSVersion.Get
|
||||||
packageType = packageTypeLinux
|
packageType = packageTypeLinux
|
||||||
|
distroName = distroNameLinux
|
||||||
|
distroVersion = distroVersionLinux
|
||||||
|
distroCodeName = distroCodeNameLinux
|
||||||
if v := linuxDeviceModel(); v != "" {
|
if v := linuxDeviceModel(); v != "" {
|
||||||
SetDeviceModel(v)
|
SetDeviceModel(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
lazyVersionMeta = &lazyAtomicValue[versionMeta]{f: ptrTo(linuxVersionMeta)}
|
||||||
|
lazyOSVersion = &lazyAtomicValue[string]{f: ptrTo(osVersionLinux)}
|
||||||
|
)
|
||||||
|
|
||||||
|
type versionMeta struct {
|
||||||
|
DistroName string
|
||||||
|
DistroVersion string
|
||||||
|
DistroCodeName string // "jammy", etc (VERSION_CODENAME from /etc/os-release)
|
||||||
|
}
|
||||||
|
|
||||||
|
func distroNameLinux() string {
|
||||||
|
return lazyVersionMeta.Get().DistroName
|
||||||
|
}
|
||||||
|
|
||||||
|
func distroVersionLinux() string {
|
||||||
|
return lazyVersionMeta.Get().DistroVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func distroCodeNameLinux() string {
|
||||||
|
return lazyVersionMeta.Get().DistroCodeName
|
||||||
|
}
|
||||||
|
|
||||||
func linuxDeviceModel() string {
|
func linuxDeviceModel() string {
|
||||||
for _, path := range []string{
|
for _, path := range []string{
|
||||||
// First try the Synology-specific location.
|
// First try the Synology-specific location.
|
||||||
@ -52,15 +76,22 @@ func linuxDeviceModel() string {
|
|||||||
func getQnapQtsVersion(versionInfo string) string {
|
func getQnapQtsVersion(versionInfo string) string {
|
||||||
for _, field := range strings.Fields(versionInfo) {
|
for _, field := range strings.Fields(versionInfo) {
|
||||||
if suffix, ok := strs.CutPrefix(field, "QTSFW_"); ok {
|
if suffix, ok := strs.CutPrefix(field, "QTSFW_"); ok {
|
||||||
return "QTS " + suffix
|
return suffix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func osVersionLinux() string {
|
func osVersionLinux() string {
|
||||||
// TODO(bradfitz,dgentry): cache this, or make caller(s) cache it.
|
var un unix.Utsname
|
||||||
|
unix.Uname(&un)
|
||||||
|
return unix.ByteSliceToString(un.Release[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func linuxVersionMeta() (meta versionMeta) {
|
||||||
dist := distro.Get()
|
dist := distro.Get()
|
||||||
|
meta.DistroName = string(dist)
|
||||||
|
|
||||||
propFile := "/etc/os-release"
|
propFile := "/etc/os-release"
|
||||||
switch dist {
|
switch dist {
|
||||||
case distro.Synology:
|
case distro.Synology:
|
||||||
@ -69,10 +100,12 @@ func osVersionLinux() string {
|
|||||||
propFile = "/etc/openwrt_release"
|
propFile = "/etc/openwrt_release"
|
||||||
case distro.WDMyCloud:
|
case distro.WDMyCloud:
|
||||||
slurp, _ := ioutil.ReadFile("/etc/version")
|
slurp, _ := ioutil.ReadFile("/etc/version")
|
||||||
return fmt.Sprintf("%s", string(bytes.TrimSpace(slurp)))
|
meta.DistroVersion = string(bytes.TrimSpace(slurp))
|
||||||
|
return
|
||||||
case distro.QNAP:
|
case distro.QNAP:
|
||||||
slurp, _ := ioutil.ReadFile("/etc/version_info")
|
slurp, _ := ioutil.ReadFile("/etc/version_info")
|
||||||
return getQnapQtsVersion(string(slurp))
|
meta.DistroVersion = getQnapQtsVersion(string(slurp))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
m := map[string]string{}
|
m := map[string]string{}
|
||||||
@ -86,50 +119,45 @@ func osVersionLinux() string {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
var un unix.Utsname
|
if v := m["VERSION_CODENAME"]; v != "" {
|
||||||
unix.Uname(&un)
|
meta.DistroCodeName = v
|
||||||
|
|
||||||
var attrBuf strings.Builder
|
|
||||||
attrBuf.WriteString("; kernel=")
|
|
||||||
attrBuf.WriteString(unix.ByteSliceToString(un.Release[:]))
|
|
||||||
if inContainer() {
|
|
||||||
attrBuf.WriteString("; container")
|
|
||||||
}
|
}
|
||||||
if env := GetEnvType(); env != "" {
|
if v := m["VERSION_ID"]; v != "" {
|
||||||
fmt.Fprintf(&attrBuf, "; env=%s", env)
|
meta.DistroVersion = v
|
||||||
}
|
}
|
||||||
attr := attrBuf.String()
|
|
||||||
|
|
||||||
id := m["ID"]
|
id := m["ID"]
|
||||||
|
if id != "" {
|
||||||
|
meta.DistroName = id
|
||||||
|
}
|
||||||
switch id {
|
switch id {
|
||||||
case "debian":
|
case "debian":
|
||||||
|
// Debian's VERSION_ID is just like "11". But /etc/debian_version has "11.5" normally.
|
||||||
|
// Or "bookworm/sid" on sid/testing.
|
||||||
slurp, _ := ioutil.ReadFile("/etc/debian_version")
|
slurp, _ := ioutil.ReadFile("/etc/debian_version")
|
||||||
return fmt.Sprintf("Debian %s (%s)%s", bytes.TrimSpace(slurp), m["VERSION_CODENAME"], attr)
|
if v := string(bytes.TrimSpace(slurp)); v != "" {
|
||||||
case "ubuntu":
|
if '0' <= v[0] && v[0] <= '9' {
|
||||||
return fmt.Sprintf("Ubuntu %s%s", m["VERSION"], attr)
|
meta.DistroVersion = v
|
||||||
|
} else if meta.DistroCodeName == "" {
|
||||||
|
meta.DistroCodeName = v
|
||||||
|
}
|
||||||
|
}
|
||||||
case "", "centos": // CentOS 6 has no /etc/os-release, so its id is ""
|
case "", "centos": // CentOS 6 has no /etc/os-release, so its id is ""
|
||||||
if cr, _ := ioutil.ReadFile("/etc/centos-release"); len(cr) > 0 { // "CentOS release 6.10 (Final)
|
if meta.DistroVersion == "" {
|
||||||
return fmt.Sprintf("%s%s", bytes.TrimSpace(cr), attr)
|
if cr, _ := ioutil.ReadFile("/etc/centos-release"); len(cr) > 0 { // "CentOS release 6.10 (Final)
|
||||||
}
|
meta.DistroVersion = string(bytes.TrimSpace(cr))
|
||||||
fallthrough
|
}
|
||||||
case "fedora", "rhel", "alpine", "nixos":
|
|
||||||
// Their PRETTY_NAME is fine as-is for all versions I tested.
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
if v := m["PRETTY_NAME"]; v != "" {
|
|
||||||
return fmt.Sprintf("%s%s", v, attr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if v := m["PRETTY_NAME"]; v != "" && meta.DistroVersion == "" && !strings.HasSuffix(v, "/sid") {
|
||||||
|
meta.DistroVersion = v
|
||||||
|
}
|
||||||
switch dist {
|
switch dist {
|
||||||
case distro.Synology:
|
case distro.Synology:
|
||||||
return fmt.Sprintf("Synology %s%s", m["productversion"], attr)
|
meta.DistroVersion = m["productversion"]
|
||||||
case distro.OpenWrt:
|
case distro.OpenWrt:
|
||||||
return fmt.Sprintf("OpenWrt %s%s", m["DISTRIB_RELEASE"], attr)
|
meta.DistroVersion = m["DISTRIB_RELEASE"]
|
||||||
case distro.Gokrazy:
|
|
||||||
return fmt.Sprintf("Gokrazy%s", attr)
|
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Other%s", attr)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func packageTypeLinux() string {
|
func packageTypeLinux() string {
|
||||||
|
@ -19,7 +19,7 @@ func TestQnap(t *testing.T) {
|
|||||||
remotes/origin/QTSFW_5.0.0`
|
remotes/origin/QTSFW_5.0.0`
|
||||||
|
|
||||||
got := getQnapQtsVersion(version_info)
|
got := getQnapQtsVersion(version_info)
|
||||||
want := "QTS 5.0.0"
|
want := "5.0.0"
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("got %q; want %q", got, want)
|
t.Errorf("got %q; want %q", got, want)
|
||||||
}
|
}
|
||||||
|
@ -11,21 +11,20 @@
|
|||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
"tailscale.com/syncs"
|
|
||||||
"tailscale.com/util/winutil"
|
"tailscale.com/util/winutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
osVersion = osVersionWindows
|
osVersion = lazyOSVersion.Get
|
||||||
packageType = packageTypeWindows
|
packageType = lazyPackageType.Get
|
||||||
}
|
}
|
||||||
|
|
||||||
var winVerCache syncs.AtomicValue[string]
|
var (
|
||||||
|
lazyOSVersion = &lazyAtomicValue[string]{f: ptrTo(osVersionWindows)}
|
||||||
|
lazyPackageType = &lazyAtomicValue[string]{f: ptrTo(packageTypeWindows)}
|
||||||
|
)
|
||||||
|
|
||||||
func osVersionWindows() string {
|
func osVersionWindows() string {
|
||||||
if s, ok := winVerCache.LoadOk(); ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
major, minor, build := windows.RtlGetNtVersionNumbers()
|
major, minor, build := windows.RtlGetNtVersionNumbers()
|
||||||
s := fmt.Sprintf("%d.%d.%d", major, minor, build)
|
s := fmt.Sprintf("%d.%d.%d", major, minor, build)
|
||||||
// Windows 11 still uses 10 as its major number internally
|
// Windows 11 still uses 10 as its major number internally
|
||||||
@ -34,9 +33,6 @@ func osVersionWindows() string {
|
|||||||
s += fmt.Sprintf(".%d", ubr)
|
s += fmt.Sprintf(".%d", ubr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s != "" {
|
|
||||||
winVerCache.Store(s)
|
|
||||||
}
|
|
||||||
return s // "10.0.19041.388", ideally
|
return s // "10.0.19041.388", ideally
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,11 +466,29 @@ type Service struct {
|
|||||||
// Because it contains pointers (slices), this type should not be used
|
// Because it contains pointers (slices), this type should not be used
|
||||||
// as a value type.
|
// as a value type.
|
||||||
type Hostinfo struct {
|
type Hostinfo struct {
|
||||||
IPNVersion string `json:",omitempty"` // version of this code
|
IPNVersion string `json:",omitempty"` // version of this code (in version.Long format)
|
||||||
FrontendLogID string `json:",omitempty"` // logtail ID of frontend instance
|
FrontendLogID string `json:",omitempty"` // logtail ID of frontend instance
|
||||||
BackendLogID string `json:",omitempty"` // logtail ID of backend instance
|
BackendLogID string `json:",omitempty"` // logtail ID of backend instance
|
||||||
OS string `json:",omitempty"` // operating system the client runs on (a version.OS value)
|
OS string `json:",omitempty"` // operating system the client runs on (a version.OS value)
|
||||||
OSVersion string `json:",omitempty"` // operating system version, with optional distro prefix ("Debian 10.4", "Windows 10 Pro 10.0.19041")
|
|
||||||
|
// OSVersion is the version of the OS, if available.
|
||||||
|
//
|
||||||
|
// For Android, it's like "10", "11", "12", etc. For iOS and macOS it's like
|
||||||
|
// "15.6.1" or "12.4.0". For Windows it's like "10.0.19044.1889". For
|
||||||
|
// FreeBSD it's like "12.3-STABLE".
|
||||||
|
//
|
||||||
|
// For Linux, prior to Tailscale 1.32, we jammed a bunch of fields into this
|
||||||
|
// string on Linux, like "Debian 10.4; kernel=xxx; container; env=kn" and so
|
||||||
|
// on. As of Tailscale 1.32, this is simply the kernel version on Linux, like
|
||||||
|
// "5.10.0-17-amd64".
|
||||||
|
OSVersion string `json:",omitempty"`
|
||||||
|
|
||||||
|
Container opt.Bool `json:",omitempty"` // whether the client is running in a container
|
||||||
|
Env string `json:",omitempty"` // a hostinfo.EnvType in string form
|
||||||
|
Distro string `json:",omitempty"` // "debian", "ubuntu", "nixos", ...
|
||||||
|
DistroVersion string `json:",omitempty"` // "20.04", ...
|
||||||
|
DistroCodeName string `json:",omitempty"` // "jammy", "bullseye", ...
|
||||||
|
|
||||||
Desktop opt.Bool `json:",omitempty"` // if a desktop was detected on Linux
|
Desktop opt.Bool `json:",omitempty"` // if a desktop was detected on Linux
|
||||||
Package string `json:",omitempty"` // Tailscale package to disambiguate ("choco", "appstore", etc; "" for unknown)
|
Package string `json:",omitempty"` // Tailscale package to disambiguate ("choco", "appstore", etc; "" for unknown)
|
||||||
DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone12,3")
|
DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone12,3")
|
||||||
|
@ -120,6 +120,11 @@ func (src *Hostinfo) Clone() *Hostinfo {
|
|||||||
BackendLogID string
|
BackendLogID string
|
||||||
OS string
|
OS string
|
||||||
OSVersion string
|
OSVersion string
|
||||||
|
Container opt.Bool
|
||||||
|
Env string
|
||||||
|
Distro string
|
||||||
|
DistroVersion string
|
||||||
|
DistroCodeName string
|
||||||
Desktop opt.Bool
|
Desktop opt.Bool
|
||||||
Package string
|
Package string
|
||||||
DeviceModel string
|
DeviceModel string
|
||||||
|
@ -31,13 +31,32 @@ func fieldsOf(t reflect.Type) (fields []string) {
|
|||||||
|
|
||||||
func TestHostinfoEqual(t *testing.T) {
|
func TestHostinfoEqual(t *testing.T) {
|
||||||
hiHandles := []string{
|
hiHandles := []string{
|
||||||
"IPNVersion", "FrontendLogID", "BackendLogID",
|
"IPNVersion",
|
||||||
"OS", "OSVersion", "Desktop", "Package", "DeviceModel", "Hostname",
|
"FrontendLogID",
|
||||||
"ShieldsUp", "ShareeNode",
|
"BackendLogID",
|
||||||
"GoArch", "GoVersion",
|
"OS",
|
||||||
"RoutableIPs", "RequestTags",
|
"OSVersion",
|
||||||
"Services", "NetInfo", "SSH_HostKeys", "Cloud",
|
"Container",
|
||||||
"Userspace", "UserspaceRouter",
|
"Env",
|
||||||
|
"Distro",
|
||||||
|
"DistroVersion",
|
||||||
|
"DistroCodeName",
|
||||||
|
"Desktop",
|
||||||
|
"Package",
|
||||||
|
"DeviceModel",
|
||||||
|
"Hostname",
|
||||||
|
"ShieldsUp",
|
||||||
|
"ShareeNode",
|
||||||
|
"GoArch",
|
||||||
|
"GoVersion",
|
||||||
|
"RoutableIPs",
|
||||||
|
"RequestTags",
|
||||||
|
"Services",
|
||||||
|
"NetInfo",
|
||||||
|
"SSH_HostKeys",
|
||||||
|
"Cloud",
|
||||||
|
"Userspace",
|
||||||
|
"UserspaceRouter",
|
||||||
}
|
}
|
||||||
if have := fieldsOf(reflect.TypeOf(Hostinfo{})); !reflect.DeepEqual(have, hiHandles) {
|
if have := fieldsOf(reflect.TypeOf(Hostinfo{})); !reflect.DeepEqual(have, hiHandles) {
|
||||||
t.Errorf("Hostinfo.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
|
t.Errorf("Hostinfo.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
|
||||||
|
@ -250,19 +250,24 @@ func (v *HostinfoView) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v HostinfoView) IPNVersion() string { return v.ж.IPNVersion }
|
func (v HostinfoView) IPNVersion() string { return v.ж.IPNVersion }
|
||||||
func (v HostinfoView) FrontendLogID() string { return v.ж.FrontendLogID }
|
func (v HostinfoView) FrontendLogID() string { return v.ж.FrontendLogID }
|
||||||
func (v HostinfoView) BackendLogID() string { return v.ж.BackendLogID }
|
func (v HostinfoView) BackendLogID() string { return v.ж.BackendLogID }
|
||||||
func (v HostinfoView) OS() string { return v.ж.OS }
|
func (v HostinfoView) OS() string { return v.ж.OS }
|
||||||
func (v HostinfoView) OSVersion() string { return v.ж.OSVersion }
|
func (v HostinfoView) OSVersion() string { return v.ж.OSVersion }
|
||||||
func (v HostinfoView) Desktop() opt.Bool { return v.ж.Desktop }
|
func (v HostinfoView) Container() opt.Bool { return v.ж.Container }
|
||||||
func (v HostinfoView) Package() string { return v.ж.Package }
|
func (v HostinfoView) Env() string { return v.ж.Env }
|
||||||
func (v HostinfoView) DeviceModel() string { return v.ж.DeviceModel }
|
func (v HostinfoView) Distro() string { return v.ж.Distro }
|
||||||
func (v HostinfoView) Hostname() string { return v.ж.Hostname }
|
func (v HostinfoView) DistroVersion() string { return v.ж.DistroVersion }
|
||||||
func (v HostinfoView) ShieldsUp() bool { return v.ж.ShieldsUp }
|
func (v HostinfoView) DistroCodeName() string { return v.ж.DistroCodeName }
|
||||||
func (v HostinfoView) ShareeNode() bool { return v.ж.ShareeNode }
|
func (v HostinfoView) Desktop() opt.Bool { return v.ж.Desktop }
|
||||||
func (v HostinfoView) GoArch() string { return v.ж.GoArch }
|
func (v HostinfoView) Package() string { return v.ж.Package }
|
||||||
func (v HostinfoView) GoVersion() string { return v.ж.GoVersion }
|
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) GoVersion() string { return v.ж.GoVersion }
|
||||||
func (v HostinfoView) RoutableIPs() views.IPPrefixSlice {
|
func (v HostinfoView) RoutableIPs() views.IPPrefixSlice {
|
||||||
return views.IPPrefixSliceOf(v.ж.RoutableIPs)
|
return views.IPPrefixSliceOf(v.ж.RoutableIPs)
|
||||||
}
|
}
|
||||||
@ -282,6 +287,11 @@ func (v HostinfoView) Equal(v2 HostinfoView) bool { return v.ж.Equal(v2.
|
|||||||
BackendLogID string
|
BackendLogID string
|
||||||
OS string
|
OS string
|
||||||
OSVersion string
|
OSVersion string
|
||||||
|
Container opt.Bool
|
||||||
|
Env string
|
||||||
|
Distro string
|
||||||
|
DistroVersion string
|
||||||
|
DistroCodeName string
|
||||||
Desktop opt.Bool
|
Desktop opt.Bool
|
||||||
Package string
|
Package string
|
||||||
DeviceModel string
|
DeviceModel string
|
||||||
|
Loading…
Reference in New Issue
Block a user