clientupdate: detect when tailscale is installed without package manager (#9137)

On linux users can install Tailscale via package managers or direct
tarball downloads. Detect when Tailscale is not installed via a package
manager so we can pick the correct update mechanism. Leave the tarball
update function unimplemented for now (coming in next PR!).

Updates #6995
Updates #8760

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
Andrew Lytvynov 2023-08-29 18:36:05 -06:00 committed by GitHub
parent 8b492b4121
commit abfe5d3879
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -158,6 +158,10 @@ func Update(args UpdateArgs) error {
case haveExecutable("apk"):
up.update = up.updateAlpineLike
}
// If nothing matched, fall back to tarball updates.
if up.update == nil {
up.update = up.updateLinuxBinary
}
case "darwin":
switch {
case !args.AppStore && !version.IsSandboxedMacOS():
@ -300,6 +304,14 @@ func parseSynoinfo(path string) (string, error) {
}
func (up *updater) updateDebLike() error {
if err := requireRoot(); err != nil {
return err
}
if err := exec.Command("dpkg", "--status", "tailscale").Run(); err != nil && isExitError(err) {
// Tailscale was not installed via apt, update via tarball download
// instead.
return up.updateLinuxBinary()
}
ver, err := requestedTailscaleVersion(up.Version, up.track)
if err != nil {
return err
@ -308,10 +320,6 @@ func (up *updater) updateDebLike() error {
return nil
}
if err := requireRoot(); err != nil {
return err
}
if updated, err := updateDebianAptSourcesList(up.track); err != nil {
return err
} else if updated {
@ -402,6 +410,11 @@ func updateDebianAptSourcesListBytes(was []byte, dstTrack string) (newContent []
}
func (up *updater) updateArchLike() error {
if err := exec.Command("pacman", "--query", "tailscale").Run(); err != nil && isExitError(err) {
// Tailscale was not installed via pacman, update via tarball download
// instead.
return up.updateLinuxBinary()
}
// Arch maintainer asked us not to implement "tailscale update" or
// auto-updates on Arch-based distros:
// https://github.com/tailscale/tailscale/issues/6995#issuecomment-1687080106
@ -419,6 +432,11 @@ func (up *updater) updateFedoraLike(packageManager string) func() error {
if err := requireRoot(); err != nil {
return err
}
if err := exec.Command(packageManager, "info", "--installed", "tailscale").Run(); err != nil && isExitError(err) {
// Tailscale was not installed via yum/dnf, update via tarball
// download instead.
return up.updateLinuxBinary()
}
defer func() {
if err != nil {
err = fmt.Errorf(`%w; you can try updating using "%s upgrade tailscale"`, err, packageManager)
@ -497,6 +515,11 @@ func (up *updater) updateAlpineLike() (err error) {
if err := requireRoot(); err != nil {
return err
}
if err := exec.Command("apk", "info", "--installed", "tailscale").Run(); err != nil && isExitError(err) {
// Tailscale was not installed via apk, update via tarball download
// instead.
return up.updateLinuxBinary()
}
defer func() {
if err != nil {
@ -764,6 +787,11 @@ func (up *updater) updateFreeBSD() (err error) {
if err := requireRoot(); err != nil {
return err
}
if err := exec.Command("pkg", "query", "%n", "tailscale").Run(); err != nil && isExitError(err) {
// Tailscale was not installed via pkg and we don't pre-compile
// binaries for it.
return errors.New("Tailscale was not installed via pkg, binary updates on FreeBSD are not supported; please reinstall Tailscale using pkg or update manually")
}
defer func() {
if err != nil {
@ -793,6 +821,10 @@ func (up *updater) updateFreeBSD() (err error) {
return nil
}
func (up *updater) updateLinuxBinary() error {
return errors.New("Linux binary updates without a package manager are not supported yet")
}
func haveExecutable(name string) bool {
path, err := exec.LookPath(name)
return err == nil && path != ""
@ -867,3 +899,8 @@ func requireRoot() error {
return errors.New("must be root")
}
}
func isExitError(err error) bool {
var exitErr *exec.ExitError
return errors.As(err, &exitErr)
}