mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
clientupdate: add support for QNAP (#10179)
Use the `qpkg_cli` to check for updates and install them. There are a couple special things about this compare to other updaters: * qpkg_cli can tell you when upgrade is available, but not what the version is * qpkg_cli --add Tailscale works for new installs, upgrades and reinstalling existing version; even reinstall of existing version takes a while Updates #10178 Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
parent
45be37cb01
commit
1f4a38ed49
@ -180,6 +180,8 @@ func (up *Updater) getUpdateFunction() (fn updateFunction, canAutoUpdate bool) {
|
|||||||
// plugin manager to be persistent.
|
// plugin manager to be persistent.
|
||||||
// TODO(awly): implement Unraid updates using the 'plugin' CLI.
|
// TODO(awly): implement Unraid updates using the 'plugin' CLI.
|
||||||
return nil, false
|
return nil, false
|
||||||
|
case distro.QNAP:
|
||||||
|
return up.updateQNAP, true
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case haveExecutable("pacman"):
|
case haveExecutable("pacman"):
|
||||||
@ -1067,6 +1069,77 @@ func (up *Updater) unpackLinuxTarball(path string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (up *Updater) updateQNAP() (err error) {
|
||||||
|
if up.Version != "" {
|
||||||
|
return errors.New("installing a specific version on QNAP is not supported")
|
||||||
|
}
|
||||||
|
if err := requireRoot(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf(`%w; you can try updating using "qpkg_cli --add Tailscale"`, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
out, err := exec.Command("qpkg_cli", "--upgradable", "Tailscale").CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to check if Tailscale is upgradable using qpkg_cli: %w, output: %q", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output should look like this:
|
||||||
|
//
|
||||||
|
// $ qpkg_cli -G Tailscale
|
||||||
|
// [Tailscale]
|
||||||
|
// upgradeStatus = 1
|
||||||
|
statusRe := regexp.MustCompile(`upgradeStatus = (\d)`)
|
||||||
|
m := statusRe.FindStringSubmatch(string(out))
|
||||||
|
if len(m) < 2 {
|
||||||
|
return fmt.Errorf("failed to check if Tailscale is upgradable using qpkg_cli, output: %q", out)
|
||||||
|
}
|
||||||
|
status, err := strconv.Atoi(m[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot parse upgradeStatus from qpkg_cli output %q: %w", out, err)
|
||||||
|
}
|
||||||
|
// Possible status values:
|
||||||
|
// 0:can upgrade
|
||||||
|
// 1:can not upgrade
|
||||||
|
// 2:error
|
||||||
|
// 3:can not get rss information
|
||||||
|
// 4:qpkg not found
|
||||||
|
// 5:qpkg not installed
|
||||||
|
//
|
||||||
|
// We want status 0.
|
||||||
|
switch status {
|
||||||
|
case 0: // proceed with upgrade
|
||||||
|
case 1:
|
||||||
|
up.Logf("no update available")
|
||||||
|
return nil
|
||||||
|
case 2, 3, 4:
|
||||||
|
return fmt.Errorf("failed to check update status with qpkg_cli (upgradeStatus = %d)", status)
|
||||||
|
case 5:
|
||||||
|
return errors.New("Tailscale was not found in the QNAP App Center")
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("failed to check update status with qpkg_cli (upgradeStatus = %d)", status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// There doesn't seem to be a way to fetch what the available upgrade
|
||||||
|
// version is. Use the generic "latest" version in confirmation prompt.
|
||||||
|
if up.Confirm != nil && !up.Confirm("latest") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
up.Logf("c2n: running qpkg_cli --add Tailscale")
|
||||||
|
cmd := exec.Command("qpkg_cli", "--add", "Tailscale")
|
||||||
|
cmd.Stdout = up.Stdout
|
||||||
|
cmd.Stderr = up.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("failed tailscale update using qpkg_cli: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func writeFile(r io.Reader, path string, perm os.FileMode) error {
|
func writeFile(r io.Reader, path string, perm os.FileMode) error {
|
||||||
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
|
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
|
||||||
return fmt.Errorf("failed to remove existing file at %q: %w", path, err)
|
return fmt.Errorf("failed to remove existing file at %q: %w", path, err)
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
"tailscale.com/util/httpm"
|
"tailscale.com/util/httpm"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
|
"tailscale.com/version/distro"
|
||||||
)
|
)
|
||||||
|
|
||||||
var c2nLogHeap func(http.ResponseWriter, *http.Request) // non-nil on most platforms (c2n_pprof.go)
|
var c2nLogHeap func(http.ResponseWriter, *http.Request) // non-nil on most platforms (c2n_pprof.go)
|
||||||
@ -341,6 +342,14 @@ func findCmdTailscale() (string, error) {
|
|||||||
if self == "/usr/local/sbin/tailscaled" || self == "/usr/local/bin/tailscaled" {
|
if self == "/usr/local/sbin/tailscaled" || self == "/usr/local/bin/tailscaled" {
|
||||||
ts = "/usr/local/bin/tailscale"
|
ts = "/usr/local/bin/tailscale"
|
||||||
}
|
}
|
||||||
|
if distro.Get() == distro.QNAP {
|
||||||
|
// The volume under /share/ where qpkg are installed is not
|
||||||
|
// predictable. But the rest of the path is.
|
||||||
|
ok, err := filepath.Match("/share/*/.qpkg/Tailscale/tailscaled", self)
|
||||||
|
if err == nil && ok {
|
||||||
|
ts = filepath.Join(filepath.Dir(self), "tailscale")
|
||||||
|
}
|
||||||
|
}
|
||||||
case "windows":
|
case "windows":
|
||||||
ts = filepath.Join(filepath.Dir(self), "tailscale.exe")
|
ts = filepath.Join(filepath.Dir(self), "tailscale.exe")
|
||||||
case "freebsd":
|
case "freebsd":
|
||||||
|
Loading…
Reference in New Issue
Block a user