@ -63,16 +63,19 @@ func versionToTrack(v string) (string, error) {
// Arguments contains arguments needed to run an update.
// Arguments contains arguments needed to run an update.
type Arguments struct {
type Arguments struct {
// Version can be a specific version number or one of the predefined track
// Version is the specific version to install.
// constants:
// Mutually exclusive with Track.
Version string
// Track is the release track to use:
//
//
// - CurrentTrack will use the latest version from the same track as the
// - CurrentTrack will use the latest version from the same track as the
// running binary
// running binary
// - StableTrack and UnstableTrack will use the latest versions of the
// - StableTrack and UnstableTrack will use the latest versions of the
// corresponding tracks
// corresponding tracks
//
//
// Leaving this empty is the same as using CurrentTrack.
// Leaving this empty will use Version or fall back to CurrentTrack if both
Version string
// Track and Version are empty.
Track string
// Logf is a logger for update progress messages.
// Logf is a logger for update progress messages.
Logf logger . Logf
Logf logger . Logf
// Stdout and Stderr should be used for output instead of os.Stdout and
// Stdout and Stderr should be used for output instead of os.Stdout and
@ -99,12 +102,20 @@ func (args Arguments) validate() error {
if args . Logf == nil {
if args . Logf == nil {
return errors . New ( "missing Logf callback in Arguments" )
return errors . New ( "missing Logf callback in Arguments" )
}
}
if 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 {
case StableTrack , UnstableTrack , CurrentTrack :
// All valid values.
default :
return fmt . Errorf ( "unsupported track %q" , args . Track )
}
return nil
return nil
}
}
type Updater struct {
type Updater struct {
Arguments
Arguments
track string
// 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
@ -128,21 +139,19 @@ func NewUpdater(args Arguments) (*Updater, error) {
if args . ForAutoUpdate && ! canAutoUpdate {
if args . ForAutoUpdate && ! canAutoUpdate {
return nil , errors . ErrUnsupported
return nil , errors . ErrUnsupported
}
}
switch up . Version {
if up . Track == CurrentTrack {
case StableTrack , UnstableTrack :
switch {
up . track = up . Version
case up . Version != "" :
case CurrentTrack :
if version . IsUnstableBuild ( ) {
up . track = UnstableTrack
} else {
up . track = StableTrack
}
default :
var err error
var err error
up . t rack, 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 ( ) :
up . Track = UnstableTrack
default :
up . Track = StableTrack
}
}
}
if up . Arguments . PkgsAddr == "" {
if up . Arguments . PkgsAddr == "" {
up . Arguments . PkgsAddr = "https://pkgs.tailscale.com"
up . Arguments . PkgsAddr = "https://pkgs.tailscale.com"
@ -248,10 +257,10 @@ 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 ) {
switch cmpver . Compare ( version . Short ( ) , ver ) {
case 0 :
case 0 :
up . Logf ( "already running %v version %v; no update needed" , up . t rack, ver )
up . Logf ( "already running %v version %v; no update needed" , up . T rack, ver )
return false
return false
case 1 :
case 1 :
up . Logf ( "installed %v version %v is newer than the latest available version %v; no update needed" , up . t rack, version . Short ( ) , ver )
up . Logf ( "installed %v version %v is newer than the latest available version %v; no update needed" , up . T rack, version . Short ( ) , ver )
return false
return false
}
}
if up . Confirm != nil {
if up . Confirm != nil {
@ -277,7 +286,7 @@ func (up *Updater) updateSynology() error {
if err != nil {
if err != nil {
return err
return err
}
}
latest , err := latestPackages ( up . t rack)
latest , err := latestPackages ( up . T rack)
if err != nil {
if err != nil {
return err
return err
}
}
@ -296,7 +305,7 @@ func (up *Updater) updateSynology() error {
if err != nil {
if err != nil {
return err
return err
}
}
pkgsPath := fmt . Sprintf ( "%s/%s" , up . t rack, spkName )
pkgsPath := fmt . Sprintf ( "%s/%s" , up . T rack, spkName )
spkPath := filepath . Join ( spkDir , path . Base ( pkgsPath ) )
spkPath := filepath . Join ( spkDir , path . Base ( pkgsPath ) )
if err := up . downloadURLToFile ( pkgsPath , spkPath ) ; err != nil {
if err := up . downloadURLToFile ( pkgsPath , spkPath ) ; err != nil {
return err
return err
@ -395,7 +404,7 @@ func (up *Updater) updateDebLike() error {
// instead.
// instead.
return up . updateLinuxBinary ( )
return up . updateLinuxBinary ( )
}
}
ver , err := requestedTailscaleVersion ( up . Version , up . t rack)
ver , err := requestedTailscaleVersion ( up . Version , up . T rack)
if err != nil {
if err != nil {
return err
return err
}
}
@ -403,10 +412,10 @@ func (up *Updater) updateDebLike() error {
return nil
return nil
}
}
if updated , err := updateDebianAptSourcesList ( up . t rack) ; err != nil {
if updated , err := updateDebianAptSourcesList ( up . T rack) ; err != nil {
return err
return err
} else if updated {
} else if updated {
up . Logf ( "Updated %s to use the %s track" , aptSourcesFile , up . t rack)
up . Logf ( "Updated %s to use the %s track" , aptSourcesFile , up . T rack)
}
}
cmd := exec . Command ( "apt-get" , "update" ,
cmd := exec . Command ( "apt-get" , "update" ,
@ -534,7 +543,7 @@ func (up *Updater) updateFedoraLike(packageManager string) func() error {
}
}
} ( )
} ( )
ver , err := requestedTailscaleVersion ( up . Version , up . t rack)
ver , err := requestedTailscaleVersion ( up . Version , up . T rack)
if err != nil {
if err != nil {
return err
return err
}
}
@ -542,10 +551,10 @@ func (up *Updater) updateFedoraLike(packageManager string) func() error {
return nil
return nil
}
}
if updated , err := updateYUMRepoTrack ( yumRepoConfigFile , up . t rack) ; err != nil {
if updated , err := updateYUMRepoTrack ( yumRepoConfigFile , up . T rack) ; err != nil {
return err
return err
} else if updated {
} else if updated {
up . Logf ( "Updated %s to use the %s track" , yumRepoConfigFile , up . t rack)
up . Logf ( "Updated %s to use the %s track" , yumRepoConfigFile , up . T rack)
}
}
cmd := exec . Command ( packageManager , "install" , "--assumeyes" , fmt . Sprintf ( "tailscale-%s-1" , ver ) )
cmd := exec . Command ( packageManager , "install" , "--assumeyes" , fmt . Sprintf ( "tailscale-%s-1" , ver ) )
@ -691,8 +700,8 @@ const (
)
)
var (
var (
verifyAuthenticode func ( string ) error // or nil on non- Windows
verifyAuthenticode func ( string ) error // set non-nil only on Windows
markTempFileFunc func ( string ) error // or nil on non- Windows
markTempFileFunc func ( string ) error // set non-nil only on Windows
)
)
func ( up * Updater ) updateWindows ( ) error {
func ( up * Updater ) updateWindows ( ) error {
@ -725,7 +734,7 @@ you can run the command prompt as Administrator one of these ways:
* press Windows + x , then press a
* press Windows + x , then press a
* press Windows + r , type in "cmd" , then press Ctrl + Shift + Enter ` )
* press Windows + r , type in "cmd" , then press Ctrl + Shift + Enter ` )
}
}
ver , err := requestedTailscaleVersion ( up . Version , up . t rack)
ver , err := requestedTailscaleVersion ( up . Version , up . T rack)
if err != nil {
if err != nil {
return err
return err
}
}
@ -748,7 +757,7 @@ you can run the command prompt as Administrator one of these ways:
return err
return err
}
}
up . cleanupOldDownloads ( filepath . Join ( msiDir , "*.msi" ) )
up . cleanupOldDownloads ( filepath . Join ( msiDir , "*.msi" ) )
pkgsPath := fmt . Sprintf ( "%s/tailscale-setup-%s-%s.msi" , up . t rack, ver , arch )
pkgsPath := fmt . Sprintf ( "%s/tailscale-setup-%s-%s.msi" , up . T rack, ver , arch )
msiTarget := filepath . Join ( msiDir , path . Base ( pkgsPath ) )
msiTarget := filepath . Join ( msiDir , path . Base ( pkgsPath ) )
if err := up . downloadURLToFile ( pkgsPath , msiTarget ) ; err != nil {
if err := up . downloadURLToFile ( pkgsPath , msiTarget ) ; err != nil {
return err
return err
@ -958,7 +967,7 @@ func (up *Updater) updateLinuxBinary() error {
if err := requireRoot ( ) ; err != nil {
if err := requireRoot ( ) ; err != nil {
return err
return err
}
}
ver , err := requestedTailscaleVersion ( up . Version , up . t rack)
ver , err := requestedTailscaleVersion ( up . Version , up . T rack)
if err != nil {
if err != nil {
return err
return err
}
}
@ -999,7 +1008,7 @@ func (up *Updater) downloadLinuxTarball(ver string) (string, error) {
if err := os . MkdirAll ( dlDir , 0700 ) ; err != nil {
if err := os . MkdirAll ( dlDir , 0700 ) ; err != nil {
return "" , err
return "" , err
}
}
pkgsPath := fmt . Sprintf ( "%s/tailscale_%s_%s.tgz" , up . t rack, ver , runtime . GOARCH )
pkgsPath := fmt . Sprintf ( "%s/tailscale_%s_%s.tgz" , up . T rack, ver , runtime . GOARCH )
dlPath := filepath . Join ( dlDir , path . Base ( pkgsPath ) )
dlPath := filepath . Join ( dlDir , path . Base ( pkgsPath ) )
if err := up . downloadURLToFile ( pkgsPath , dlPath ) ; err != nil {
if err := up . downloadURLToFile ( pkgsPath , dlPath ) ; err != nil {
return "" , err
return "" , err