mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
clientupdate: allow switching from unstable to stable tracks (#12477)
Previously, we would only compare the current version to resolved latest version for track. When running `tailscale update --track=stable` from an unstable build, it would almost always fail because the stable version is "older". But we should support explicitly switching tracks like that. Fixes #12347 Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
parent
a1ab7f7c94
commit
be54dde0eb
@ -37,11 +37,18 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CurrentTrack = ""
|
|
||||||
StableTrack = "stable"
|
StableTrack = "stable"
|
||||||
UnstableTrack = "unstable"
|
UnstableTrack = "unstable"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var CurrentTrack = func() string {
|
||||||
|
if version.IsUnstableBuild() {
|
||||||
|
return UnstableTrack
|
||||||
|
} else {
|
||||||
|
return StableTrack
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
func versionToTrack(v string) (string, error) {
|
func versionToTrack(v string) (string, error) {
|
||||||
_, rest, ok := strings.Cut(v, ".")
|
_, rest, ok := strings.Cut(v, ".")
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -106,7 +113,7 @@ func (args Arguments) validate() error {
|
|||||||
return fmt.Errorf("only one of Version(%q) or Track(%q) can be set", args.Version, args.Track)
|
return fmt.Errorf("only one of Version(%q) or Track(%q) can be set", args.Version, args.Track)
|
||||||
}
|
}
|
||||||
switch args.Track {
|
switch args.Track {
|
||||||
case StableTrack, UnstableTrack, CurrentTrack:
|
case StableTrack, UnstableTrack, "":
|
||||||
// All valid values.
|
// All valid values.
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported track %q", args.Track)
|
return fmt.Errorf("unsupported track %q", args.Track)
|
||||||
@ -119,11 +126,17 @@ type Updater struct {
|
|||||||
// Update is a platform-specific method that updates the installation. May be
|
// Update is a platform-specific method that updates the installation. May be
|
||||||
// nil (not all platforms support updates from within Tailscale).
|
// nil (not all platforms support updates from within Tailscale).
|
||||||
Update func() error
|
Update func() error
|
||||||
|
|
||||||
|
// currentVersion is the short form of the current client version as
|
||||||
|
// returned by version.Short(), typically "x.y.z". Used for tests to
|
||||||
|
// override the actual current version.
|
||||||
|
currentVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUpdater(args Arguments) (*Updater, error) {
|
func NewUpdater(args Arguments) (*Updater, error) {
|
||||||
up := Updater{
|
up := Updater{
|
||||||
Arguments: args,
|
Arguments: args,
|
||||||
|
currentVersion: version.Short(),
|
||||||
}
|
}
|
||||||
if up.Stdout == nil {
|
if up.Stdout == nil {
|
||||||
up.Stdout = os.Stdout
|
up.Stdout = os.Stdout
|
||||||
@ -139,18 +152,15 @@ func NewUpdater(args Arguments) (*Updater, error) {
|
|||||||
if args.ForAutoUpdate && !canAutoUpdate {
|
if args.ForAutoUpdate && !canAutoUpdate {
|
||||||
return nil, errors.ErrUnsupported
|
return nil, errors.ErrUnsupported
|
||||||
}
|
}
|
||||||
if up.Track == CurrentTrack {
|
if up.Track == "" {
|
||||||
switch {
|
if up.Version != "" {
|
||||||
case up.Version != "":
|
|
||||||
var err error
|
var err error
|
||||||
up.Track, err = versionToTrack(args.Version)
|
up.Track, err = versionToTrack(args.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case version.IsUnstableBuild():
|
} else {
|
||||||
up.Track = UnstableTrack
|
up.Track = CurrentTrack
|
||||||
default:
|
|
||||||
up.Track = StableTrack
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if up.Arguments.PkgsAddr == "" {
|
if up.Arguments.PkgsAddr == "" {
|
||||||
@ -259,14 +269,17 @@ func Update(args Arguments) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (up *Updater) confirm(ver string) bool {
|
func (up *Updater) confirm(ver string) bool {
|
||||||
switch cmpver.Compare(version.Short(), ver) {
|
// Only check version when we're not switching tracks.
|
||||||
case 0:
|
if up.Track == "" || up.Track == CurrentTrack {
|
||||||
|
switch c := cmpver.Compare(up.currentVersion, ver); {
|
||||||
|
case c == 0:
|
||||||
up.Logf("already running %v version %v; no update needed", up.Track, ver)
|
up.Logf("already running %v version %v; no update needed", up.Track, ver)
|
||||||
return false
|
return false
|
||||||
case 1:
|
case c > 0:
|
||||||
up.Logf("installed %v version %v is newer than the latest available version %v; no update needed", up.Track, version.Short(), ver)
|
up.Logf("installed %v version %v is newer than the latest available version %v; no update needed", up.Track, up.currentVersion, ver)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if up.Confirm != nil {
|
if up.Confirm != nil {
|
||||||
return up.Confirm(ver)
|
return up.Confirm(ver)
|
||||||
}
|
}
|
||||||
@ -681,7 +694,7 @@ func parseAlpinePackageVersion(out []byte) (string, error) {
|
|||||||
return "", fmt.Errorf("malformed info line: %q", line)
|
return "", fmt.Errorf("malformed info line: %q", line)
|
||||||
}
|
}
|
||||||
ver := parts[1]
|
ver := parts[1]
|
||||||
if cmpver.Compare(ver, maxVer) == 1 {
|
if cmpver.Compare(ver, maxVer) > 0 {
|
||||||
maxVer = ver
|
maxVer = ver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -880,7 +893,7 @@ func (up *Updater) installMSI(msi string) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
up.Logf("Install attempt failed: %v", err)
|
up.Logf("Install attempt failed: %v", err)
|
||||||
uninstallVersion := version.Short()
|
uninstallVersion := up.currentVersion
|
||||||
if v := os.Getenv("TS_DEBUG_UNINSTALL_VERSION"); v != "" {
|
if v := os.Getenv("TS_DEBUG_UNINSTALL_VERSION"); v != "" {
|
||||||
uninstallVersion = v
|
uninstallVersion = v
|
||||||
}
|
}
|
||||||
@ -1331,12 +1344,8 @@ func requestedTailscaleVersion(ver, track string) (string, error) {
|
|||||||
// LatestTailscaleVersion returns the latest released version for the given
|
// LatestTailscaleVersion returns the latest released version for the given
|
||||||
// track from pkgs.tailscale.com.
|
// track from pkgs.tailscale.com.
|
||||||
func LatestTailscaleVersion(track string) (string, error) {
|
func LatestTailscaleVersion(track string) (string, error) {
|
||||||
if track == CurrentTrack {
|
if track == "" {
|
||||||
if version.IsUnstableBuild() {
|
track = CurrentTrack
|
||||||
track = UnstableTrack
|
|
||||||
} else {
|
|
||||||
track = StableTrack
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
latest, err := latestPackages(track)
|
latest, err := latestPackages(track)
|
||||||
|
@ -846,3 +846,107 @@ func TestParseUnraidPluginVersion(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfirm(t *testing.T) {
|
||||||
|
curTrack := CurrentTrack
|
||||||
|
defer func() { CurrentTrack = curTrack }()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
fromTrack string
|
||||||
|
toTrack string
|
||||||
|
fromVer string
|
||||||
|
toVer string
|
||||||
|
confirm func(string) bool
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "on latest stable",
|
||||||
|
fromTrack: StableTrack,
|
||||||
|
toTrack: StableTrack,
|
||||||
|
fromVer: "1.66.0",
|
||||||
|
toVer: "1.66.0",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "stable upgrade",
|
||||||
|
fromTrack: StableTrack,
|
||||||
|
toTrack: StableTrack,
|
||||||
|
fromVer: "1.66.0",
|
||||||
|
toVer: "1.68.0",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "unstable upgrade",
|
||||||
|
fromTrack: UnstableTrack,
|
||||||
|
toTrack: UnstableTrack,
|
||||||
|
fromVer: "1.67.1",
|
||||||
|
toVer: "1.67.2",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "from stable to unstable",
|
||||||
|
fromTrack: StableTrack,
|
||||||
|
toTrack: UnstableTrack,
|
||||||
|
fromVer: "1.66.0",
|
||||||
|
toVer: "1.67.1",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "from unstable to stable",
|
||||||
|
fromTrack: UnstableTrack,
|
||||||
|
toTrack: StableTrack,
|
||||||
|
fromVer: "1.67.1",
|
||||||
|
toVer: "1.66.0",
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "confirm callback rejects",
|
||||||
|
fromTrack: StableTrack,
|
||||||
|
toTrack: StableTrack,
|
||||||
|
fromVer: "1.66.0",
|
||||||
|
toVer: "1.66.1",
|
||||||
|
confirm: func(string) bool {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "confirm callback allows",
|
||||||
|
fromTrack: StableTrack,
|
||||||
|
toTrack: StableTrack,
|
||||||
|
fromVer: "1.66.0",
|
||||||
|
toVer: "1.66.1",
|
||||||
|
confirm: func(string) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "downgrade",
|
||||||
|
fromTrack: StableTrack,
|
||||||
|
toTrack: StableTrack,
|
||||||
|
fromVer: "1.66.1",
|
||||||
|
toVer: "1.66.0",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
CurrentTrack = tt.fromTrack
|
||||||
|
up := Updater{
|
||||||
|
currentVersion: tt.fromVer,
|
||||||
|
Arguments: Arguments{
|
||||||
|
Track: tt.toTrack,
|
||||||
|
Confirm: tt.confirm,
|
||||||
|
Logf: t.Logf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := up.confirm(tt.toVer); got != tt.want {
|
||||||
|
t.Errorf("got %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user