clientupdate: implement update for Unraid (#10344)

Use the [`plugin`
CLI](https://forums.unraid.net/topic/72240-solved-is-there-a-way-to-installuninstall-plugins-from-script/#comment-676870)
to fetch and apply the update.

Updates https://github.com/tailscale/tailscale/issues/10184

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
Andrew Lytvynov 2023-11-28 14:28:30 -07:00 committed by GitHub
parent 5e861c3871
commit 5a9e935597
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 538 additions and 6 deletions

View File

@ -176,10 +176,7 @@ func (up *Updater) getUpdateFunction() (fn updateFunction, canAutoUpdate bool) {
case distro.Alpine:
return up.updateAlpineLike, true
case distro.Unraid:
// Unraid runs from memory, updates must be installed via the Unraid
// plugin manager to be persistent.
// TODO(awly): implement Unraid updates using the 'plugin' CLI.
return nil, false
return up.updateUnraid, true
case distro.QNAP:
return up.updateQNAP, true
}
@ -1148,6 +1145,64 @@ func (up *Updater) updateQNAP() (err error) {
return nil
}
func (up *Updater) updateUnraid() (err error) {
if up.Version != "" {
return errors.New("installing a specific version on Unraid is not supported")
}
if err := requireRoot(); err != nil {
return err
}
defer func() {
if err != nil {
err = fmt.Errorf(`%w; you can try updating using "plugin check tailscale.plg && plugin update tailscale.plg"`, err)
}
}()
// We need to run `plugin check` for the latest tailscale.plg to get
// downloaded. Unfortunately, the output of this command does not contain
// the latest tailscale version available. So we'll parse the downloaded
// tailscale.plg file manually below.
out, err := exec.Command("plugin", "check", "tailscale.plg").CombinedOutput()
if err != nil {
return fmt.Errorf("failed to check if Tailscale plugin is upgradable: %w, output: %q", err, out)
}
// Note: 'plugin check' downloads plugins to /tmp/plugins.
// The installed .plg files are in /boot/config/plugins/, but the pending
// ones are in /tmp/plugins. We should parse the pending file downloaded by
// 'plugin check'.
latest, err := parseUnraidPluginVersion("/tmp/plugins/tailscale.plg")
if err != nil {
return fmt.Errorf("failed to find latest Tailscale version in /boot/config/plugins/tailscale.plg: %w", err)
}
if !up.confirm(latest) {
return nil
}
up.Logf("c2n: running 'plugin update tailscale.plg'")
cmd := exec.Command("plugin", "update", "tailscale.plg")
cmd.Stdout = up.Stdout
cmd.Stderr = up.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed tailscale plugin update: %w", err)
}
return nil
}
func parseUnraidPluginVersion(plgPath string) (string, error) {
plg, err := os.ReadFile(plgPath)
if err != nil {
return "", err
}
re := regexp.MustCompile(`<FILE Name="/boot/config/plugins/tailscale/tailscale_(\d+\.\d+\.\d+)_[a-z0-9]+.tgz">`)
match := re.FindStringSubmatch(string(plg))
if len(match) < 2 {
return "", errors.New("version not found in plg file")
}
return match[1], nil
}
func writeFile(r io.Reader, path string, perm os.FileMode) error {
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to remove existing file at %q: %w", path, err)

View File

@ -795,3 +795,31 @@ func TestCleanupOldDownloads(t *testing.T) {
})
}
}
func TestParseUnraidPluginVersion(t *testing.T) {
tests := []struct {
plgPath string
wantVer string
wantErr string
}{
{plgPath: "testdata/tailscale-1.52.0.plg", wantVer: "1.52.0"},
{plgPath: "testdata/tailscale-1.54.0.plg", wantVer: "1.54.0"},
{plgPath: "testdata/tailscale-nover.plg", wantErr: "version not found in plg file"},
{plgPath: "testdata/tailscale-nover-path-mentioned.plg", wantErr: "version not found in plg file"},
}
for _, tt := range tests {
t.Run(tt.plgPath, func(t *testing.T) {
got, err := parseUnraidPluginVersion(tt.plgPath)
if got != tt.wantVer {
t.Errorf("got version: %q, want %q", got, tt.wantVer)
}
var gotErr string
if err != nil {
gotErr = err.Error()
}
if gotErr != tt.wantErr {
t.Errorf("got error: %q, want %q", gotErr, tt.wantErr)
}
})
}
}

View File

@ -0,0 +1,115 @@
<?xml version='1.0' standalone='yes'?>
<!DOCTYPE PLUGIN>
<PLUGIN
name="tailscale"
author="Derek Kaser"
version="2023.11.01"
pluginURL="https://raw.githubusercontent.com/dkaser/unraid-tailscale/main/plugin/tailscale.plg"
launch="Settings/Tailscale"
support="https://forums.unraid.net/topic/136889-plugin-tailscale/"
>
<CHANGES>
<![CDATA[
###2023.11.01###
- Update Tailscale to 1.52.0 (new checksum from upstream package server)
###2023.10.31###
- Update Tailscale to 1.52.0
###2023.10.29###
- Update Tailscale to 1.50.1
- Fix nginx hang when Tailscale restarts
###2023.09.26###
- Update Tailscale to 1.50.0
- New Tailscale web interface
###2023.09.14a###
- Update Tailscale to 1.48.2
###2023.08.22###
- Update Tailscale to 1.48.1
For older releases, see https://github.com/dkaser/unraid-tailscale/releases
]]>
</CHANGES>
<FILE Name="/boot/config/plugins/tailscale/tailscale_1.52.0_amd64.tgz">
<URL>https://pkgs.tailscale.com/stable/tailscale_1.52.0_amd64.tgz</URL>
<MD5>b4d15d9908737e08e3f95ed5104603ce</MD5>
</FILE>
<FILE Name="/boot/config/plugins/tailscale/unraid-tailscale-utils-1.4.1-noarch-1.txz">
<URL>https://github.com/dkaser/unraid-tailscale-utils/releases/download/1.4.1/unraid-tailscale-utils-1.4.1-noarch-1.txz</URL>
<MD5>7095ab4b88b34d8f5da6483865883267</MD5>
</FILE>
<FILE Name="/boot/config/plugins/tailscale/unraid-plugin-diagnostics-1.2.2-noarch-1.txz">
<URL>https://github.com/dkaser/unraid-plugin-diagnostics/releases/download/1.2.2/unraid-plugin-diagnostics-1.2.2-noarch-1.txz</URL>
<MD5>9d358575499305889962d83ebd90c20c</MD5>
</FILE>
<!--
The 'install' script.
-->
<FILE Run="/bin/bash">
<INLINE>
<![CDATA[
if [ -d "/usr/local/emhttp/plugins/tailscale" ]; then
rm -rf /usr/local/emhttp/plugins/tailscale
fi
upgradepkg --install-new /boot/config/plugins/tailscale/unraid-plugin-diagnostics-1.2.2-noarch-1.txz
upgradepkg --install-new --reinstall /boot/config/plugins/tailscale/unraid-tailscale-utils-1.4.1-noarch-1.txz
mkdir -p /usr/local/emhttp/plugins/tailscale/bin
tar xzf /boot/config/plugins/tailscale/tailscale_1.52.0_amd64.tgz --strip-components 1 -C /usr/local/emhttp/plugins/tailscale/bin
ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscale /usr/local/sbin/tailscale
ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscaled /usr/local/sbin/tailscaled
mkdir -p /var/local/emhttp/plugins/tailscale
echo "VERSION=2023.11.01" >> /var/local/emhttp/plugins/tailscale/tailscale.ini
# start tailscaled
/usr/local/emhttp/plugins/tailscale/restart.sh
# cleanup old versions
rm -f /boot/config/plugins/tailscale/tailscale-utils-*.txz
rm -f $(ls /boot/config/plugins/tailscale/unraid-tailscale-utils-*.txz 2>/dev/null | grep -v '1.4.1')
rm -f $(ls /boot/config/plugins/tailscale/unraid-plugin-diagnostics-*.txz 2>/dev/null | grep -v '1.2.2')
rm -f $(ls /boot/config/plugins/tailscale/*.tgz 2>/dev/null | grep -v 'tailscale_1.52.0_amd64')
echo ""
echo "----------------------------------------------------"
echo " tailscale has been installed."
echo " Version: 2023.11.01"
echo "----------------------------------------------------"
echo ""
]]>
</INLINE>
</FILE>
<!--
The 'remove' script.
-->
<FILE Run="/bin/bash" Method="remove">
<INLINE>
<![CDATA[
# Stop service
/etc/rc.d/rc.tailscale stop 2>/dev/null
rm /usr/local/sbin/tailscale
rm /usr/local/sbin/tailscaled
removepkg unraid-tailscale-utils-1.4.1
rm -rf /usr/local/emhttp/plugins/tailscale
rm -rf /boot/config/plugins/tailscale
]]>
</INLINE>
</FILE>
</PLUGIN>

View File

@ -0,0 +1,112 @@
<?xml version='1.0' standalone='yes'?>
<!DOCTYPE PLUGIN>
<PLUGIN
name="tailscale"
author="Derek Kaser"
version="2023.11.18"
pluginURL="https://raw.githubusercontent.com/dkaser/unraid-tailscale/main/plugin/tailscale.plg"
launch="Settings/Tailscale"
support="https://forums.unraid.net/topic/136889-plugin-tailscale/"
>
<CHANGES>
<![CDATA[
###2023.11.18###
- Update Tailscale to 1.54.0
###2023.11.01###
- Update Tailscale to 1.52.0 (new checksum from upstream package server)
###2023.10.31###
- Update Tailscale to 1.52.0
###2023.10.29###
- Update Tailscale to 1.50.1
- Fix nginx hang when Tailscale restarts
###2023.09.26###
- Update Tailscale to 1.50.0
- New Tailscale web interface
For older releases, see https://github.com/dkaser/unraid-tailscale/releases
]]>
</CHANGES>
<FILE Name="/boot/config/plugins/tailscale/tailscale_1.54.0_amd64.tgz">
<URL>https://pkgs.tailscale.com/stable/tailscale_1.54.0_amd64.tgz</URL>
<MD5>20187743e0c1c1a0d9fea47a10b6a9ba</MD5>
</FILE>
<FILE Name="/boot/config/plugins/tailscale/unraid-tailscale-utils-1.4.1-noarch-1.txz">
<URL>https://github.com/dkaser/unraid-tailscale-utils/releases/download/1.4.1/unraid-tailscale-utils-1.4.1-noarch-1.txz</URL>
<MD5>7095ab4b88b34d8f5da6483865883267</MD5>
</FILE>
<FILE Name="/boot/config/plugins/tailscale/unraid-plugin-diagnostics-1.2.2-noarch-1.txz">
<URL>https://github.com/dkaser/unraid-plugin-diagnostics/releases/download/1.2.2/unraid-plugin-diagnostics-1.2.2-noarch-1.txz</URL>
<MD5>9d358575499305889962d83ebd90c20c</MD5>
</FILE>
<!--
The 'install' script.
-->
<FILE Run="/bin/bash">
<INLINE>
<![CDATA[
if [ -d "/usr/local/emhttp/plugins/tailscale" ]; then
rm -rf /usr/local/emhttp/plugins/tailscale
fi
upgradepkg --install-new /boot/config/plugins/tailscale/unraid-plugin-diagnostics-1.2.2-noarch-1.txz
upgradepkg --install-new --reinstall /boot/config/plugins/tailscale/unraid-tailscale-utils-1.4.1-noarch-1.txz
mkdir -p /usr/local/emhttp/plugins/tailscale/bin
tar xzf /boot/config/plugins/tailscale/tailscale_1.54.0_amd64.tgz --strip-components 1 -C /usr/local/emhttp/plugins/tailscale/bin
ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscale /usr/local/sbin/tailscale
ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscaled /usr/local/sbin/tailscaled
mkdir -p /var/local/emhttp/plugins/tailscale
echo "VERSION=2023.11.18" >> /var/local/emhttp/plugins/tailscale/tailscale.ini
# start tailscaled
/usr/local/emhttp/plugins/tailscale/restart.sh
# cleanup old versions
rm -f /boot/config/plugins/tailscale/tailscale-utils-*.txz
rm -f $(ls /boot/config/plugins/tailscale/unraid-tailscale-utils-*.txz 2>/dev/null | grep -v '1.4.1')
rm -f $(ls /boot/config/plugins/tailscale/unraid-plugin-diagnostics-*.txz 2>/dev/null | grep -v '1.2.2')
rm -f $(ls /boot/config/plugins/tailscale/*.tgz 2>/dev/null | grep -v 'tailscale_1.54.0_amd64')
echo ""
echo "----------------------------------------------------"
echo " tailscale has been installed."
echo " Version: 2023.11.18"
echo "----------------------------------------------------"
echo ""
]]>
</INLINE>
</FILE>
<!--
The 'remove' script.
-->
<FILE Run="/bin/bash" Method="remove">
<INLINE>
<![CDATA[
# Stop service
/etc/rc.d/rc.tailscale stop 2>/dev/null
rm /usr/local/sbin/tailscale
rm /usr/local/sbin/tailscaled
removepkg unraid-tailscale-utils-1.4.1
rm -rf /usr/local/emhttp/plugins/tailscale
rm -rf /boot/config/plugins/tailscale
]]>
</INLINE>
</FILE>
</PLUGIN>

View File

@ -0,0 +1,110 @@
<?xml version='1.0' standalone='yes'?>
<!DOCTYPE PLUGIN>
<PLUGIN
name="tailscale"
author="Derek Kaser"
version="2023.11.01"
pluginURL="https://raw.githubusercontent.com/dkaser/unraid-tailscale/main/plugin/tailscale.plg"
launch="Settings/Tailscale"
support="https://forums.unraid.net/topic/136889-plugin-tailscale/"
>
<CHANGES>
<![CDATA[
###2023.11.01###
- Update Tailscale to 1.52.0 (new checksum from upstream package server)
###2023.10.31###
- Update Tailscale to 1.52.0
###2023.10.29###
- Update Tailscale to 1.50.1
- Fix nginx hang when Tailscale restarts
###2023.09.26###
- Update Tailscale to 1.50.0
- New Tailscale web interface
###2023.09.14a###
- Update Tailscale to 1.48.2
###2023.08.22###
- Update Tailscale to 1.48.1
For older releases, see https://github.com/dkaser/unraid-tailscale/releases
]]>
</CHANGES>
<FILE Name="/boot/config/plugins/tailscale/unraid-tailscale-utils-1.4.1-noarch-1.txz">
<URL>https://github.com/dkaser/unraid-tailscale-utils/releases/download/1.4.1/unraid-tailscale-utils-1.4.1-noarch-1.txz</URL>
<MD5>7095ab4b88b34d8f5da6483865883267</MD5>
</FILE>
<FILE Name="/boot/config/plugins/tailscale/unraid-plugin-diagnostics-1.2.2-noarch-1.txz">
<URL>https://github.com/dkaser/unraid-plugin-diagnostics/releases/download/1.2.2/unraid-plugin-diagnostics-1.2.2-noarch-1.txz</URL>
<MD5>9d358575499305889962d83ebd90c20c</MD5>
</FILE>
<!--
The 'install' script.
-->
<FILE Run="/bin/bash">
<INLINE>
<![CDATA[
if [ -d "/usr/local/emhttp/plugins/tailscale" ]; then
rm -rf /usr/local/emhttp/plugins/tailscale
fi
upgradepkg --install-new /boot/config/plugins/tailscale/unraid-plugin-diagnostics-1.2.2-noarch-1.txz
upgradepkg --install-new --reinstall /boot/config/plugins/tailscale/unraid-tailscale-utils-1.4.1-noarch-1.txz
mkdir -p /usr/local/emhttp/plugins/tailscale/bin
tar xzf /boot/config/plugins/tailscale/tailscale_1.52.0_amd64.tgz --strip-components 1 -C /usr/local/emhttp/plugins/tailscale/bin
ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscale /usr/local/sbin/tailscale
ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscaled /usr/local/sbin/tailscaled
mkdir -p /var/local/emhttp/plugins/tailscale
echo "VERSION=2023.11.01" >> /var/local/emhttp/plugins/tailscale/tailscale.ini
# start tailscaled
/usr/local/emhttp/plugins/tailscale/restart.sh
# cleanup old versions
rm -f /boot/config/plugins/tailscale/tailscale-utils-*.txz
rm -f $(ls /boot/config/plugins/tailscale/unraid-tailscale-utils-*.txz 2>/dev/null | grep -v '1.4.1')
rm -f $(ls /boot/config/plugins/tailscale/unraid-plugin-diagnostics-*.txz 2>/dev/null | grep -v '1.2.2')
rm -f $(ls /boot/config/plugins/tailscale/*.tgz 2>/dev/null | grep -v 'tailscale_1.52.0_amd64')
echo ""
echo "----------------------------------------------------"
echo " tailscale has been installed."
echo " Version: 2023.11.01"
echo "----------------------------------------------------"
echo ""
]]>
</INLINE>
</FILE>
<!--
The 'remove' script.
-->
<FILE Run="/bin/bash" Method="remove">
<INLINE>
<![CDATA[
# Stop service
/etc/rc.d/rc.tailscale stop 2>/dev/null
rm /usr/local/sbin/tailscale
rm /usr/local/sbin/tailscaled
removepkg unraid-tailscale-utils-1.4.1
rm -rf /usr/local/emhttp/plugins/tailscale
rm -rf /boot/config/plugins/tailscale
]]>
</INLINE>
</FILE>
</PLUGIN>

View File

@ -0,0 +1,102 @@
<?xml version='1.0' standalone='yes'?>
<!DOCTYPE PLUGIN>
<PLUGIN
name="tailscale"
author="Derek Kaser"
version="2023.11.01"
pluginURL="https://raw.githubusercontent.com/dkaser/unraid-tailscale/main/plugin/tailscale.plg"
launch="Settings/Tailscale"
support="https://forums.unraid.net/topic/136889-plugin-tailscale/"
>
<CHANGES>
<![CDATA[
###2023.10.29###
- Update Tailscale to 1.50.1
- Fix nginx hang when Tailscale restarts
###2023.09.26###
- Update Tailscale to 1.50.0
- New Tailscale web interface
###2023.09.14a###
- Update Tailscale to 1.48.2
###2023.08.22###
- Update Tailscale to 1.48.1
For older releases, see https://github.com/dkaser/unraid-tailscale/releases
]]>
</CHANGES>
<FILE Name="/boot/config/plugins/tailscale/unraid-tailscale-utils-1.4.1-noarch-1.txz">
<URL>https://github.com/dkaser/unraid-tailscale-utils/releases/download/1.4.1/unraid-tailscale-utils-1.4.1-noarch-1.txz</URL>
<MD5>7095ab4b88b34d8f5da6483865883267</MD5>
</FILE>
<FILE Name="/boot/config/plugins/tailscale/unraid-plugin-diagnostics-1.2.2-noarch-1.txz">
<URL>https://github.com/dkaser/unraid-plugin-diagnostics/releases/download/1.2.2/unraid-plugin-diagnostics-1.2.2-noarch-1.txz</URL>
<MD5>9d358575499305889962d83ebd90c20c</MD5>
</FILE>
<!--
The 'install' script.
-->
<FILE Run="/bin/bash">
<INLINE>
<![CDATA[
if [ -d "/usr/local/emhttp/plugins/tailscale" ]; then
rm -rf /usr/local/emhttp/plugins/tailscale
fi
upgradepkg --install-new /boot/config/plugins/tailscale/unraid-plugin-diagnostics-1.2.2-noarch-1.txz
upgradepkg --install-new --reinstall /boot/config/plugins/tailscale/unraid-tailscale-utils-1.4.1-noarch-1.txz
mkdir -p /usr/local/emhttp/plugins/tailscale/bin
ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscale /usr/local/sbin/tailscale
ln -s /usr/local/emhttp/plugins/tailscale/bin/tailscaled /usr/local/sbin/tailscaled
mkdir -p /var/local/emhttp/plugins/tailscale
echo "VERSION=2023.11.01" >> /var/local/emhttp/plugins/tailscale/tailscale.ini
# start tailscaled
/usr/local/emhttp/plugins/tailscale/restart.sh
# cleanup old versions
rm -f /boot/config/plugins/tailscale/tailscale-utils-*.txz
rm -f $(ls /boot/config/plugins/tailscale/unraid-tailscale-utils-*.txz 2>/dev/null | grep -v '1.4.1')
rm -f $(ls /boot/config/plugins/tailscale/unraid-plugin-diagnostics-*.txz 2>/dev/null | grep -v '1.2.2')
echo ""
echo "----------------------------------------------------"
echo " tailscale has been installed."
echo " Version: 2023.11.01"
echo "----------------------------------------------------"
echo ""
]]>
</INLINE>
</FILE>
<!--
The 'remove' script.
-->
<FILE Run="/bin/bash" Method="remove">
<INLINE>
<![CDATA[
# Stop service
/etc/rc.d/rc.tailscale stop 2>/dev/null
rm /usr/local/sbin/tailscale
rm /usr/local/sbin/tailscaled
removepkg unraid-tailscale-utils-1.4.1
rm -rf /usr/local/emhttp/plugins/tailscale
rm -rf /boot/config/plugins/tailscale
]]>
</INLINE>
</FILE>
</PLUGIN>

View File

@ -32,7 +32,12 @@
// - Arch (and other pacman-based distros)
// - Alpine (and other apk-based distros)
// - FreeBSD (and other pkg-based distros)
if distro.Get() != distro.Arch && distro.Get() != distro.Alpine && runtime.GOOS != "freebsd" {
// - Unraid/QNAP/Synology
if distro.Get() != distro.Arch &&
distro.Get() != distro.Alpine &&
distro.Get() != distro.QNAP &&
distro.Get() != distro.Synology &&
runtime.GOOS != "freebsd" {
fs.StringVar(&updateArgs.track, "track", "", `which track to check for updates: "stable" or "unstable" (dev); empty means same as current`)
fs.StringVar(&updateArgs.version, "version", "", `explicit version to update/downgrade to`)
}

View File

@ -395,13 +395,18 @@ func findCmdTailscale() (string, error) {
if self == "/usr/local/sbin/tailscaled" || self == "/usr/local/bin/tailscaled" {
ts = "/usr/local/bin/tailscale"
}
if distro.Get() == distro.QNAP {
switch distro.Get() {
case 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 distro.Unraid:
if self == "/usr/local/emhttp/plugins/tailscale/bin/tailscaled" {
ts = "/usr/local/emhttp/plugins/tailscale/bin/tailscale"
}
}
case "windows":
ts = filepath.Join(filepath.Dir(self), "tailscale.exe")