mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
version, cmd/tailscale: add version.Meta, tailscale version --json
Add `tailscale version --json` JSON output mode. This will be used later for a double-opt-in (per node consent like Tailscale SSH + control config) to let admins do remote upgrades via `tailscale update` via a c2n call, which would then need to verify the cmd/tailscale found on disk for running tailscale update corresponds to the running tailscaled, refusing if anything looks amiss. Plus JSON output modes are just nice to have, rather than parsing unstable/fragile/obscure text formats. Updates #6995 Updates #6907 Change-Id: I7821ab7fbea4612f4b9b7bdc1be1ad1095aca71b Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
5ca22a0068
commit
ba5aa2c486
@ -6,10 +6,13 @@
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/peterbourgon/ff/v3/ffcli"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/version"
|
||||
)
|
||||
|
||||
@ -20,6 +23,7 @@
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("version")
|
||||
fs.BoolVar(&versionArgs.daemon, "daemon", false, "also print local node's daemon version")
|
||||
fs.BoolVar(&versionArgs.json, "json", false, "output in JSON format")
|
||||
return fs
|
||||
})(),
|
||||
Exec: runVersion,
|
||||
@ -27,23 +31,38 @@
|
||||
|
||||
var versionArgs struct {
|
||||
daemon bool // also check local node's daemon version
|
||||
json bool
|
||||
}
|
||||
|
||||
func runVersion(ctx context.Context, args []string) error {
|
||||
if len(args) > 0 {
|
||||
return fmt.Errorf("too many non-flag arguments: %q", args)
|
||||
}
|
||||
if !versionArgs.daemon {
|
||||
var err error
|
||||
var st *ipnstate.Status
|
||||
|
||||
if versionArgs.daemon {
|
||||
st, err = localClient.StatusWithoutPeers(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if versionArgs.json {
|
||||
m := version.GetMeta()
|
||||
if st != nil {
|
||||
m.DaemonLong = st.Version
|
||||
}
|
||||
e := json.NewEncoder(os.Stdout)
|
||||
e.SetIndent("", "\t")
|
||||
return e.Encode(m)
|
||||
}
|
||||
|
||||
if st == nil {
|
||||
outln(version.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
printf("Client: %s\n", version.String())
|
||||
|
||||
st, err := localClient.StatusWithoutPeers(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printf("Daemon: %s\n", st.Version)
|
||||
return nil
|
||||
}
|
||||
|
7
tailcfg/tailcfg_export_test.go
Normal file
7
tailcfg/tailcfg_export_test.go
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2023 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 tailcfg
|
||||
|
||||
var ExportKeyMarshalText = keyMarshalText
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tailcfg
|
||||
package tailcfg_test
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
@ -16,6 +16,7 @@
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "tailscale.com/tailcfg"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/util/must"
|
||||
@ -660,7 +661,7 @@ func BenchmarkKeyMarshalText(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
var k [32]byte
|
||||
for i := 0; i < b.N; i++ {
|
||||
sinkBytes = keyMarshalText("prefix", k)
|
||||
sinkBytes = ExportKeyMarshalText("prefix", k)
|
||||
}
|
||||
}
|
||||
|
||||
@ -670,7 +671,7 @@ func TestAppendKeyAllocs(t *testing.T) {
|
||||
}
|
||||
var k [32]byte
|
||||
err := tstest.MinAllocsPerRun(t, 1, func() {
|
||||
sinkBytes = keyMarshalText("prefix", k)
|
||||
sinkBytes = ExportKeyMarshalText("prefix", k)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -12,7 +12,9 @@
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
tailscaleroot "tailscale.com"
|
||||
"tailscale.com/syncs"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
// IsMobile reports whether this is a mobile client build.
|
||||
@ -104,3 +106,76 @@ func initUnstable() {
|
||||
}
|
||||
isUnstableBuild = minor%2 == 1
|
||||
}
|
||||
|
||||
// Meta is a JSON-serializable type that contains all the version
|
||||
// information.
|
||||
type Meta struct {
|
||||
// MajorMinorPatch is the "major.minor.patch" version string, without
|
||||
// any hyphenated suffix.
|
||||
MajorMinorPatch string `json:"majorMinorPatch"`
|
||||
|
||||
// IsDev is whether Short contains a -dev suffix. This is whether the build
|
||||
// is a development build (as opposed to an official stable or unstable
|
||||
// build stamped in the usual ways). If you just run "go install" or "go
|
||||
// build" on a dev branch, this will be true.
|
||||
IsDev bool `json:"isDev,omitempty"`
|
||||
|
||||
// Short is MajorMinorPatch but optionally adding "-dev" or "-devYYYYMMDD"
|
||||
// for dev builds, depending on how it was build.
|
||||
Short string `json:"short"`
|
||||
|
||||
// Long is the full version string, including git commit hash(es) as the
|
||||
// suffix.
|
||||
Long string `json:"long"`
|
||||
|
||||
// UnstableBranch is whether the build is from an unstable (development)
|
||||
// branch. That is, it reports whether the minor version is odd.
|
||||
UnstableBranch bool `json:"unstableBranch,omitempty"`
|
||||
|
||||
// GitCommit, if non-empty, is the git commit of the
|
||||
// github.com/tailscale/tailscale repository at which Tailscale was
|
||||
// built. Its format is the one returned by `git describe --always
|
||||
// --exclude "*" --dirty --abbrev=200`.
|
||||
GitCommit string `json:"gitCommit,omitempty"`
|
||||
|
||||
// GitDirty is whether Go stamped the binary as having dirty version
|
||||
// control changes in the working directory (debug.ReadBuildInfo
|
||||
// setting "vcs.modified" was true).
|
||||
GitDirty bool `json:"gitDirty,omitempty"`
|
||||
|
||||
// ExtraGitCommit, if non-empty, is the git commit of a "supplemental"
|
||||
// repository at which Tailscale was built. Its format is the same as
|
||||
// gitCommit.
|
||||
//
|
||||
// ExtraGitCommit is used to track the source revision when the main
|
||||
// Tailscale repository is integrated into and built from another
|
||||
// repository (for example, Tailscale's proprietary code, or the
|
||||
// Android OSS repository). Together, GitCommit and ExtraGitCommit
|
||||
// exactly describe what repositories and commits were used in a
|
||||
// build.
|
||||
ExtraGitCommit string `json:"extraGitCommit,omitempty"`
|
||||
|
||||
// DaemonLong is the version number from the tailscaled
|
||||
// daemon, if requested.
|
||||
DaemonLong string `json:"daemonLong,omitempty"`
|
||||
|
||||
// Cap is the current Tailscale capability version. It's a monotonically
|
||||
// incrementing integer that's incremented whenever a new capability is
|
||||
// added.
|
||||
Cap int `json:"cap"`
|
||||
}
|
||||
|
||||
// GetMeta returns version metadata about the current build.
|
||||
func GetMeta() Meta {
|
||||
return Meta{
|
||||
MajorMinorPatch: strings.TrimSpace(tailscaleroot.Version),
|
||||
Short: Short,
|
||||
Long: Long,
|
||||
GitCommit: GitCommit,
|
||||
GitDirty: GitDirty,
|
||||
ExtraGitCommit: ExtraGitCommit,
|
||||
IsDev: strings.Contains(Short, "-dev"), // TODO(bradfitz): could make a bool for this in init
|
||||
UnstableBranch: IsUnstableBuild(),
|
||||
Cap: int(tailcfg.CurrentCapabilityVersion),
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ func init() {
|
||||
// --exclude "*" --dirty --abbrev=200`.
|
||||
var GitCommit = ""
|
||||
|
||||
// GitDirty is whether Go stamped the binary has having dirty version
|
||||
// GitDirty is whether Go stamped the binary as having dirty version
|
||||
// control changes in the working directory (debug.ReadBuildInfo
|
||||
// setting "vcs.modified" was true).
|
||||
var GitDirty bool
|
||||
|
Loading…
Reference in New Issue
Block a user