Running the In-Place Upgrade Script
Purpose: Upgrade an existing StorageLink 1.2.x instance to version 1.3.0 in place, without deploying a new instance.
Read first: An in-place upgrade does not give the instance a license. You start on the free 30-day trial, stay at the 10-user default, and keep paying the hourly marketplace fee. Our recommended upgrade is to deploy a new 1.3.0 instance and import your backup (see Upgrade Process for StorageLink); a new marketplace instance is licensed automatically.
Quick Steps: SSH into the VM, then run:
cd ~curl -sS -O https://thorntech-products.s3.amazonaws.com/storagelink/1.003.00/in-place-upgrade-storagelink.shsudo bash ./in-place-upgrade-storagelink.sh
Important:
- Export a backup file first via Settings → Backup & Recovery
- Supported on single-instance deployments on AWS, Azure and Google Cloud
- The upgraded instance comes up without a license. Start the free 30-day trial (10 users) under Settings → License, or non-administrator users cannot sign in and file transfers stay disabled; after the trial you need a purchased license
- If you use LetsEncrypt or your own SSL certificate, re-apply your hostname and certificate paths to the new
website.conf - Behind a load balancer or reverse proxy, add its CIDR range to
security.client-ip.trusted-proxiesin/opt/swiftgw/application.properties - The script is safe to run again if it stops partway
Recommended: Deploy a new 1.3.0 instance and import your backup. It is safer, it is licensed automatically, and it ships the current PostgreSQL and operating system image.
Product: StorageLink by Thorn Technologies — cloud storage gateway for secure file sharing
Overview
This article goes over how to run the in-place upgrade script to upgrade an existing StorageLink 1.2.x instance to version 1.3.0 (1.003.00).
StorageLink 1.3.0 introduces licensing, and that changes what an in-place upgrade means. An instance upgraded in place does not receive a license. It comes up unlicensed, you start the free 30-day trial, and you stay at the 10-user default. After the trial you have to purchase a license to keep using it, while the hourly marketplace fee continues. A new 1.3.0 instance deployed from the marketplace is licensed automatically for 10 users with nothing to activate. Deploying a new instance and importing your backup is the upgrade path we recommend; the in-place script is for deployments that cannot be replaced. See Upgrading from 1.2.1 or earlier below.
The 1.3.0 upgrade changes more than earlier ones, so read this article all the way through before you start:
- New service definition. The 1.3.0 platform starts differently, so the script replaces the
swiftgatewayservice definition and adds the application properties the new version requires. - Licensing. Licensing is new in 1.3.0. An instance upgraded in place comes up without a license. After the upgrade you activate the free 30-day trial or a purchased license in the web admin UI.
- PostgreSQL stays as it is. StorageLink 1.3.0 has no minimum PostgreSQL version, so the script leaves the database already on your instance in place. A new 1.3.0 instance deployed from the marketplace ships PostgreSQL 18; an instance upgraded in place keeps the version it has, which remains supported and continues to receive security updates from your operating system.
Upgrading from 1.2.1 or earlier: choose your path
Every StorageLink instance on 1.2.1 or earlier runs without a license, because licensing did not exist before 1.3.0. There are two ways to reach 1.3.0, and they end in different places:
| Deploy a new 1.3.0 instance and import your backup (recommended) | Upgrade in place with this script | |
|---|---|---|
| License | Licensed automatically by the marketplace for 10 StorageLink users (administrators not counted). Nothing to enter or activate. | No license. Start the free 30-day trial (10 users); after the trial you must purchase a license to keep using StorageLink. |
| User limit | 10 users | 10 users on the trial; a purchased license is sized to what you buy |
| Marketplace fee | Continues on the new instance | Continues on the existing instance, in addition to any license you purchase |
| PostgreSQL | 18, shipped with the image | The version already on the instance (15 on 1.2.1 images), still supported |
| Operating system | Freshly built image with current patches | Your existing image, patched by your normal OS updates |
| Your configuration | Restored from the backup file (users, folders, cloud connections, identity providers, settings); custom properties and SSL certificates re-applied by hand | Kept in place; custom properties and SSL certificates re-applied to the new website.conf |
| Downtime | None on the old instance until you cut over | StorageLink is unavailable while the script runs, about one to two minutes |
Our recommendation is to deploy a new instance. The procedure is in the Upgrade Process article: export a backup, deploy 1.3.0 from the marketplace listing you already use, import the backup, and cut over. The new instance is licensed from first boot and you never see the trial card.
Use the in-place script only when replacing the instance is not an option for you. If you do, plan for the trial: it lasts 30 days, and the only way past the 10-user default afterwards is a purchased license.
If you have more than 10 users, neither path keeps them all on a marketplace instance: from 1.3.0 a marketplace instance is licensed for 10 users, and a backup with more than 10 users imports the first 10 and skips the rest. Deploy from the StorageLink BYOL listing instead, where the license is available in 10-, 100- or 1,000-user tiers. The Standard vs Bring Your Own License (BYOL) article explains the two listings and the move, and Purchasing a License covers buying and activating a license.
Before You Begin
Export a backup file first
Before running the script, export a new Backup File containing your Users & Settings under Settings ---> Backup & Recovery ---> Export Backup File. If anything goes wrong, you can deploy a fresh 1.3.0 instance and import this file. The Upgrade Process article describes the export and import steps.
Downtime
StorageLink is unavailable to users from the moment the script stops the service until the health check passes at the end, so plan a short maintenance window. The first start after the upgrade applies the 1.3.0 schema changes, which can take a few minutes on a small VM.
Network access
The script downloads the 1.3.0 files from https://thorntech-products.s3.amazonaws.com. If Java 21 or the unzip tool is missing from the instance, it also installs them from your operating system's package repositories. The instance needs outbound HTTPS access for the download. If your instance has no outbound internet access, use the backup-and-restore path in the Upgrade Process article instead.
Supported platforms
The script supports single-instance StorageLink deployments on AWS (Amazon Linux 2023), Azure and Google Cloud (Ubuntu). It detects the cloud and operating system itself.
If you run a high-availability deployment, do not run this script; contact support@thorntech.com for the upgrade path.
Licensing after the upgrade
As described above, an instance upgraded in place comes up unlicensed. The script prints this warning at the start of the run:
WARNING: no license found. After this upgrade, activate a license or
trial in the admin UI (Settings -> License) or non-admin users cannot
sign in and transfers/jobs stay disabled.
Until a license or trial is active, administrators can sign in and manage configuration and users, but non-administrator users cannot sign in, and uploads, downloads and transfer jobs are disabled.
After the upgrade, sign in as an administrator and start the free 30-day trial under Settings ---> License, or activate a purchased license if you already have one. The Purchasing a License article walks through both. Transfers are unblocked as soon as the trial or license is active; no restart is needed.
The trial covers 10 StorageLink users. If your instance already has more than 10 users, they keep working, but you cannot add or replace a user until a license tier that covers your user count is active. Before the 30 days run out, purchase a license or move to a new instance as described in Upgrading from 1.2.1 or earlier.
LetsEncrypt and custom SSL certificates
The script installs a new nginx website.conf and keeps your previous one alongside it as website.conf-<stamp>, where <stamp> is the date and time of the run (a copy is also in the rollback bundle). The file is located at:
Azure & Google Cloud:
/etc/nginx/sites-available/website.conf
AWS:
/etc/nginx/conf.d/website.conf
If you used LetsEncrypt or installed your own certificate, the new file does not contain your hostname or certificate paths. After the upgrade, copy the server_name line and the ssl_certificate and ssl_certificate_key lines from the stamped copy into the new website.conf, then validate and restart nginx:
sudo nginx -t && sudo systemctl restart nginx
The Troubleshooting section of the LetsEncrypt article shows what a complete website.conf looks like with a certificate applied.
Behind a load balancer or reverse proxy
StorageLink 1.3.0 resolves each user's client IP address against a list of trusted proxies, which by default contains only the loopback addresses (127.0.0.0/8 and ::1/128), that is, the nginx proxy running on the instance itself. If StorageLink sits behind a load balancer, reverse proxy or CDN (an Azure Application Gateway or a Google Cloud load balancer, for example), add the address range your proxy sends traffic from to the security.client-ip.trusted-proxies property in /opt/swiftgw/application.properties, for example:
security.client-ip.trusted-proxies=127.0.0.0/8,::1/128,10.224.1.0/24
For an Azure Application Gateway this is the subnet the gateway is deployed in; for Google's external Application Load Balancer it is Google's proxy ranges, 130.211.0.0/22 and 35.191.0.0/16. Keep the two loopback entries: nginx on the instance forwards requests to StorageLink over loopback, so it must stay trusted. Several ranges can be separated with commas; bare IP addresses are accepted, DNS names are not. Then restart StorageLink with sudo systemctl restart swiftgateway.
Until you do, nothing fails and no error is shown, but audit records and login lockout attribute every user to the load balancer's own address. To confirm the setting, sign in as an administrator: the address your own session resolved to is shown as Seen as <address> in the top navigation bar.
Automated 1.3.0 deployments, such as the high-availability templates, set the same list through the LOAD_BALANCER_ADDRESSES variable in the launch configuration (/opt/swiftgw/launch_config.env). On an upgraded instance, use the application property above.
Deployments where users reach StorageLink directly need no change.
Behind an Azure Application Gateway the forwarded address currently includes a changing source port, so login lockout does not yet accumulate per client, and audit records show the port alongside the address. Handling this form is planned for a later release.
Memory settings
Older in-place upgrade articles asked you to edit the Java memory settings after the upgrade. The 1.3.0 script sizes them for you: it reads the instance's total RAM and sets the minimum heap to about 15% and the maximum heap to about 48% of it in /opt/swiftgw/swiftgateway-1.3.0.conf. You don't need to edit the file unless you resize the VM later. See the Memory Settings article for that case.
Log forwarding
StorageLink 1.3.0 writes license events to a new log file, /opt/swiftgw/log/license-audit-<date>.log, next to the application and audit logs. Instances launched from a 1.3.0 image on AWS and Google Cloud forward it to CloudWatch or Google Cloud Logging with the other logs. The script does not change the log agent on an upgraded instance, so if you forward StorageLink logs today, add the new file to the same agent configuration that already picks up application-*.log and audit-*.log. On Azure, add it to the Azure Monitor Agent data collection rule you use for the other logs. See the Logging article for the log file locations.
What the Script Does
The script prints every command as it runs (each appears on a line beginning with +) and stops at the first error. Every step can be repeated, so if it stops partway you simply run it again. It works through these steps in order.
Step 0 - Preflight checks. The script checks whether a license file is already present on the instance and prints the licensing warning shown above if not. It detects your cloud provider and operating system, and installs the unzip tool if the image does not have it. Finally, it validates your current nginx configuration with nginx -t. If that check fails, the script exits before changing anything (see Troubleshooting).
Step 1 - Rollback bundle. The script writes a rollback bundle to /var/backups/slink-upgrade-<stamp>/, where <stamp> is the date and time of the run, so each run gets its own bundle. It contains:
pg_dumpall.sql- a full SQL export of the PostgreSQL databases, taken before 1.3.0 applies its schema changesopt-swiftgw.tgz- an archive of/opt/swiftgw(excluding thetmpandlogfolders)swiftgateway.service- the previous service definitionwebsite.conf- the previous nginx site configurationapplication.properties- a copy taken in step 5, before any properties are changed
Step 2 - Stop StorageLink. The script stops the swiftgateway service. StorageLink is unavailable to users from this point until step 7.
Step 3 - Java 21. The script installs Java 21 if it is not already present. Instances on 1.2.1 already have it, so this step usually does nothing.
Step 4 - Install StorageLink 1.3.0. The script downloads the 1.3.0 assets and installs:
- the application jar and its conf file into
/opt/swiftgw/, with the Java heap sized for this instance's RAM - the new web admin UI into
/usr/share/nginx/admin-ui(the previous UI is kept as/usr/share/nginx/admin-ui-<stamp>) - the new nginx
website.conf(the previous one is kept aswebsite.conf-<stamp>). The new file is validated immediately. If it failsnginx -t, the script restores the previous file, keeps the failing one aswebsite.conf-1.3.0-failed, and continues - a new
webconfig.jsfor the admin UI that keeps this instance's existing OAuth client settings (the values are not written to the script output)
Step 5 - Application properties. The script adds the properties 1.3.0 requires to /opt/swiftgw/application.properties if they are not already present, and sets one existing property to its new value:
features.systemd.notify=trueserver.forward-headers-strategy=none(replaces theframeworkvalue used by 1.2.x; StorageLink 1.3.0 resolves client addresses throughsecurity.client-ip.trusted-proxiesinstead, and reports any other value as an error at startup)security.redirect.base-path=backend/server.max-http-request-header-size=128KBfeatures.api.file-upload-accept-rules=(empty)
Your other properties are not changed.
Step 6 - Service definition. The script replaces /etc/systemd/system/swiftgateway.service with the 1.3.0 unit, which launches Java directly and waits for StorageLink to signal that it is ready. The previous unit is kept as swiftgateway.service-<stamp>. The script also updates the version shown in the SSH login banner.
Step 7 - Restart and health check. The script restarts nginx (only if its configuration validates), reloads systemd, and starts swiftgateway. It then polls http://127.0.0.1:8080/actuator/health every 5 seconds for up to 10 minutes; the first start applies the 1.3.0 schema changes, which takes longest on small VMs. When the health check reports UP, the script prints the completion message. If StorageLink does not become healthy within 10 minutes, the script prints the last 40 lines of the service journal and exits with an error (see Troubleshooting).
Running the Script
SSH into the VM (see SSH into the VM). Then download and run the script from your home directory:
cd ~
curl -sS -O https://thorntech-products.s3.amazonaws.com/storagelink/1.003.00/in-place-upgrade-storagelink.sh
sudo bash ./in-place-upgrade-storagelink.sh
Run the script with sudo bash from your home directory, exactly as shown. /tmp is mounted noexec on the StorageLink image, so running sudo ./in-place-upgrade-storagelink.sh from /tmp fails with Permission denied. You don't need sudo su, chmod +x, or a separate apt-get update; the script updates the package lists itself when it needs to install something.
The script runs with set -x, so it prints each command on a line beginning with + before running it. Expect a lot of output; the messages quoted in this article appear among those trace lines. The final health check is the longest step. Keep your SSH session open until the script finishes.
To keep a copy of the output for support, you can run the last command as sudo bash ./in-place-upgrade-storagelink.sh 2>&1 | tee ~/upgrade-1.3.0.log instead.
Expected output
Near the end of a successful run, in between the + ... command-trace lines, you will see:
StorageLink 1.3.0 is UP after upgrade.
Upgrade to 1.3.0 complete.
Rollback bundle: /var/backups/slink-upgrade-<stamp>
PostgreSQL was left at its current version; 1.3.0 needs no database upgrade.
Reminder: if this instance has no license, activate one in Settings -> License.
Re-running the script
If the script stops partway (a dropped SSH session, for example), run it again with the same three commands. Every step can be repeated: the download overwrites the previous copy, properties that are already present are left alone, and each run writes its own rollback bundle and keeps its own copies of the files it replaces, so nothing from the first attempt is lost.
Verify the upgrade
Refresh the web admin UI in your browser. You should see the updated UI, and the version shown at the bottom of the page should read
1.3.0.Check that the service is running:
sudo systemctl status swiftgatewayCheck the health endpoint. The response includes
"status":"UP":curl -s http://127.0.0.1:8080/actuator/health
After the Upgrade
Work through this checklist once the script reports Upgrade to 1.3.0 complete.
Start the trial or activate a license. Go to Settings ---> License and start the free 30-day trial (10 users) or activate your purchased license. Until you do, non-administrator users cannot sign in and transfers and jobs stay disabled. Put a reminder in your calendar for the trial's end date. See Purchasing a License.
Behind a load balancer or reverse proxy? Add its CIDR range to
security.client-ip.trusted-proxiesin/opt/swiftgw/application.propertiesas described in Behind a load balancer or reverse proxy, runsudo systemctl restart swiftgateway, and confirm theSeen asaddress in the top navigation bar is your own.Using LetsEncrypt or your own certificate? Re-apply your hostname and certificate paths to the new
website.conf, then runsudo nginx -t && sudo systemctl restart nginx.Verify users and folders. Sign in and confirm your users, folders and cloud connections are all present. Note that in 1.3.0 creating folders is a separate permission from uploading. On upgrade, every user who can upload is also allowed to create folders, matching the previous behavior, so remove the Create Folder permission from any user who should not have it.
Forwarding logs? Add
/opt/swiftgw/log/license-audit-<date>.logto the agent configuration that already forwards your application and audit logs.Tuned S3 upload concurrency? The property
features.file-system.s3-max-concurrencywas renamed tofeatures.file-system.aws-s3.http-max-concurrency. The old name is ignored, so move your value to the new name in/opt/swiftgw/application.propertiesand restart StorageLink.Clean up when you are satisfied. The rollback bundle in
/var/backups/and the stamped copies of the previous admin UI,website.confand service definition can be removed once you are confident in the upgrade.
Troubleshooting
The script refuses to start because the nginx configuration fails validation
If your current nginx configuration does not pass nginx -t, the script exits immediately with:
ERROR: 'nginx -t' fails on this instance's CURRENT nginx configuration.
The upgrade has NOT started and nothing has been changed.
Fix the nginx configuration (run 'sudo nginx -t' to see the error), then re-run this script.
Nothing on the instance has changed. Run sudo nginx -t, read the error, and fix the file it names. If the file is website.conf, the Troubleshooting section of the LetsEncrypt article has a known-good example. Then run the upgrade script again.
The new website.conf failed validation
If the output contains ERROR: new website.conf fails 'nginx -t' — restoring the previous config., the script has put your previous website.conf back and kept the new one as website.conf-1.3.0-failed in the same directory. The upgrade continues with your previous nginx configuration. Send both files to support@thorntech.com so we can help you apply the 1.3.0 configuration.
"backend did not become healthy in 10 minutes"
If StorageLink does not report healthy within 10 minutes, the script prints the last 40 lines of the service journal and exits with ERROR: backend did not become healthy in 10 minutes. The service is left running, so it may still finish starting on its own. Check the service status first:
sudo systemctl status swiftgateway
Then look at the service journal and the most recent application log (the date is part of the file name):
sudo journalctl -u swiftgateway --no-pager | tail -100
/opt/swiftgw/log/application-<date>.log
One known cause: StorageLink 1.3.0 refuses to start if security.max-login-failed-attempts or security.failed-login-timeout-seconds is set below 1 in /opt/swiftgw/application.properties, and the startup error names the property. Set a value of 1 or more (the default timeout is 3600), then run sudo systemctl restart swiftgateway. Once the cause is fixed, it is safe to run the upgrade script again.
Stuck at the "Please wait while StorageLink finishes setting up" screen
If the web admin UI stays on the Please wait while StorageLink finishes setting up loading screen after the upgrade, check the service journal and the application log as described above.
Rolling back
The rollback bundle for each run is in /var/backups/slink-upgrade-<stamp>/; run ls /var/backups/ to find it. It holds a full SQL export of the database taken before the upgrade, an archive of the previous /opt/swiftgw, and the previous service definition and nginx configuration. If you need to roll back, contact support@thorntech.com before making changes and we will walk through it with you.
Contacting support
If you get stuck, email support@thorntech.com and include the full output of the script (or the ~/upgrade-1.3.0.log file if you kept one), the most recent /opt/swiftgw/log/application-<date>.log, and the output of sudo journalctl -u swiftgateway --no-pager | tail -200.
Script Contents
Here are the contents of the script for reference:
#!/bin/bash
#
# StorageLink in-place upgrade script — upgrades an existing 1.2.x instance to v1.3.0.
#
# What this upgrade changes on the instance:
# 1. Application files: the 1.3.0 jar and conf, the web admin UI and the nginx
# site config. Spring Boot 4 no longer produces an executable boot jar, so
# the systemd unit is replaced with one that launches `java -jar` with an
# EnvironmentFile (JAVA_OPTS/RUN_ARGS), Type=notify, and
# features.systemd.notify=true in application.properties.
# 2. Application properties: adds the properties 1.3.0 requires and sets
# server.forward-headers-strategy=none (1.3.0 resolves client addresses
# through security.client-ip.trusted-proxies instead).
# 3. Java 21 is installed if it is missing.
#
# PostgreSQL is left as it is. StorageLink 1.3.0 enforces no minimum PostgreSQL
# version, so the database already on the instance keeps running unchanged; a
# new 1.3.0 instance deployed from the marketplace ships PostgreSQL 18.
#
# Every step can be repeated: if the script stops partway, run it again.
#
# Supported: single-instance StorageLink 1.2.x on AWS (Amazon Linux 2023), Azure
# and Google Cloud (Ubuntu). Not for high-availability deployments.
#
# QA mode: if ASSETS_TARBALL is set (a .tgz containing swiftgateway-<ver>.jar,
# swiftgateway-<ver>.conf, admin-ui/ and website.conf), it is used instead of the
# public S3 assets.zip — lets the upgrade run against pre-release builds.
#
# How to run:
# cd ~
# curl -sS -O https://thorntech-products.s3.amazonaws.com/storagelink/1.003.00/in-place-upgrade-storagelink.sh
# sudo bash ./in-place-upgrade-storagelink.sh
#
# Run it with "sudo bash" from your home directory: /tmp is mounted noexec on
# the hardened appliance, so "sudo ./in-place-upgrade-storagelink.sh" from /tmp
# fails with "Permission denied".
#
set -xe
if [[ $(whoami) != "root" ]]; then
echo "Usage: sudo bash $0"
exit 1
fi
TARGET_VERSION="${TARGET_VERSION:-1.3.0}"
TARGET_VERBOSE_VERSION="1.003.00"
# One stamp per run: every backup copy and the rollback bundle carry it, so a
# re-run never overwrites the copies taken before the first attempt.
STAMP=$(date +"%m%d%Y-%H%M%S")
APPLICATION_PROPERTIES="/opt/swiftgw/application.properties"
BACKUP_DIR="/var/backups/slink-upgrade-${STAMP}"
# Wait for any other apt/dpkg holder (e.g. unattended-upgrades) instead of
# failing mid-upgrade, and never let a package replace a locally-modified
# config file via a conffile prompt (noninteractive runs die on those).
APT_OPTS=(-o DPkg::Lock::Timeout=600
-o Dpkg::Options::=--force-confdef
-o Dpkg::Options::=--force-confold)
function extractPropValueFromSourceFile {
local prefix="${1}"
local str=`grep "${prefix}" ${2} 2>/dev/null`
echo "${str#$prefix}" | xargs
}
#
# 0. Preflight
#
# Licensing is new in 1.3.0. A 1.2.x instance has no license, so after this
# upgrade an administrator activates a free trial or a purchased license in the
# admin UI (Settings -> License); until then non-admin users cannot sign in and
# transfers/jobs stay disabled. Warn, don't block.
if [ -d /etc/thorn ] && [ -n "$(ls -A /etc/thorn 2>/dev/null)" ]; then
echo "License file present under /etc/thorn — instance will come up licensed."
else
echo "WARNING: no license found. After this upgrade, activate a license or"
echo "trial in the admin UI (Settings -> License) or non-admin users cannot"
echo "sign in and transfers/jobs stay disabled."
fi
mkdir -p "${BACKUP_DIR}"
# Determine cloud provider (same probe order as prior scripts)
# head -c keeps a wrong-cloud error page (e.g. Google's HTML 404) from
# flooding the trace log — the real values are all short strings.
AWS_DOMAIN=$(curl -s --max-time 2 "http://169.254.169.254/latest/meta-data/services/domain" | head -c 40 || true)
AZURE_DOMAIN=$(curl --noproxy "*" --max-time 2 -s -H 'Metadata: True' "http://169.254.169.254/metadata/instance/compute/azEnvironment?api-version=2019-06-01&format=text" | head -c 40 || true)
GCP_CHECK=$(curl -s --max-time 2 "http://169.254.169.254/computeMetadata/v1/instance/zone" -H "Metadata-Flavor: Google" | head -c 80 || true)
CLOUD_PROVIDER=aws
# GCP's metadata answer contains "zones/..." — other clouds' IMDS return XML/JSON errors here
[[ $GCP_CHECK == *"zones/"* ]] && CLOUD_PROVIDER=gcp
[[ $AWS_DOMAIN == "amazonaws.com" ]] && CLOUD_PROVIDER=aws
[[ $AZURE_DOMAIN == "AzurePublicCloud" ]] && CLOUD_PROVIDER=azure
# Determine OS and nginx layout
if getent passwd www-data > /dev/null 2>&1; then
OS=ubuntu
NGINX_USER=www-data
NGINX_CONF_PATH="/etc/nginx/sites-available"
else
OS=el
NGINX_USER=nginx
NGINX_CONF_PATH="/etc/nginx/conf.d"
fi
# unzip is needed to unpack the assets bundle; not every image ships it.
if ! command -v unzip >/dev/null 2>&1; then
if [ "$OS" == "ubuntu" ]; then
apt-get "${APT_OPTS[@]}" update
DEBIAN_FRONTEND=noninteractive apt-get "${APT_OPTS[@]}" install -y -q unzip
else
yum install -y unzip
fi
fi
# Pre-flight: refuse to start if the CURRENT nginx config already fails
# validation. This upgrade stops the app — never begin that on a box whose
# web tier is already broken.
if ! nginx -t; then
echo "" >&2
echo "ERROR: 'nginx -t' fails on this instance's CURRENT nginx configuration." >&2
echo "The upgrade has NOT started and nothing has been changed." >&2
echo "Fix the nginx configuration (run 'sudo nginx -t' to see the error), then re-run this script." >&2
exit 1
fi
#
# 1. Backups (rollback bundle)
#
# Full logical DB backup — the database itself is not modified by this script,
# but 1.3.0 applies its own schema migrations on first start, so keep a copy
# of the pre-upgrade state.
sudo -u postgres pg_dumpall > "${BACKUP_DIR}/pg_dumpall.sql"
# App + config backups
tar czf "${BACKUP_DIR}/opt-swiftgw.tgz" --exclude='swiftgw/tmp/*' --exclude='swiftgw/log/*' -C /opt swiftgw
cp -a /etc/systemd/system/swiftgateway.service "${BACKUP_DIR}/swiftgateway.service"
cp -a "${NGINX_CONF_PATH}/website.conf" "${BACKUP_DIR}/website.conf" 2>/dev/null || \
cp -a /etc/nginx/sites-enabled/website.conf "${BACKUP_DIR}/website.conf" 2>/dev/null || true
echo "Rollback bundle written to ${BACKUP_DIR}"
#
# 2. Stop the application
#
systemctl stop swiftgateway
#
# 3. Java 21 runtime (1.2.x images already ship it; install if missing)
#
if ! java -version 2>&1 | grep -q 'version "21'; then
if [ "$OS" == "ubuntu" ]; then
apt-get "${APT_OPTS[@]}" update
DEBIAN_FRONTEND=noninteractive apt-get "${APT_OPTS[@]}" install -y -q openjdk-21-jre-headless
update-alternatives --set java /usr/lib/jvm/java-21-openjdk-amd64/bin/java || true
else
yum install -y java-21-amazon-corretto
/usr/sbin/update-alternatives --set java /usr/lib/jvm/java-21-amazon-corretto.x86_64/bin/java
fi
fi
#
# 4. Install StorageLink 1.3.0 files
#
cd /tmp
rm -rf /tmp/slink-upgrade-assets && mkdir /tmp/slink-upgrade-assets && cd /tmp/slink-upgrade-assets
if [ -n "${ASSETS_TARBALL}" ] && [ -f "${ASSETS_TARBALL}" ]; then
tar xzf "${ASSETS_TARBALL}"
else
curl -fsSL -o assets.zip "https://thorntech-products.s3.amazonaws.com/storagelink/${TARGET_VERBOSE_VERSION}/assets.zip"
# Normalize the zip's assets/ folder to the same top-level layout the
# ASSETS_TARBALL path produces — later steps (website.conf install) resolve
# files relative to /tmp/slink-upgrade-assets and would miss assets/.
unzip -o assets.zip && mv assets/* . && rmdir assets
fi
# jar + conf
chmod +x swiftgateway-${TARGET_VERSION}.jar
chown swiftgw:swiftgw swiftgateway-${TARGET_VERSION}.jar swiftgateway-${TARGET_VERSION}.conf
mv swiftgateway-${TARGET_VERSION}.jar swiftgateway-${TARGET_VERSION}.conf /opt/swiftgw/
# Heap sizing: the conf ships with values computed for the build VM; recompute
# for this instance like first-boot does (Xms ~15% / Xmx ~48% of MemTotal).
MEM_MB=$(awk '/MemTotal/ {printf "%d", $2/1024}' /proc/meminfo)
XMS=$((MEM_MB * 15 / 100))
XMX=$((MEM_MB * 48 / 100))
sed -i -E "s/-Xms[0-9]+m/-Xms${XMS}m/; s/-Xmx[0-9]+m/-Xmx${XMX}m/" /opt/swiftgw/swiftgateway-${TARGET_VERSION}.conf
# admin UI (previous UI kept as admin-ui-<stamp>)
if [ -d admin-ui ]; then
tar czf /usr/share/nginx/admin-ui.tar.gz admin-ui
else
mv admin-ui.tar.gz /usr/share/nginx
fi
cd /usr/share/nginx
[ -d admin-ui ] && mv admin-ui "admin-ui-${STAMP}"
tar xzvpf admin-ui.tar.gz && rm -f admin-ui.tar.gz
# Drop macOS AppleDouble metadata files (._*) if the bundle was built on a Mac.
find admin-ui -name '._*' -delete
chown -R ${NGINX_USER}:${NGINX_USER} admin-ui
# nginx site config (backup already taken; certs keep their existing paths)
cd /tmp/slink-upgrade-assets
if [ -f website.conf ]; then
cp "${NGINX_CONF_PATH}/website.conf" "${NGINX_CONF_PATH}/website.conf-${STAMP}" 2>/dev/null || true
chown ${NGINX_USER}:${NGINX_USER} website.conf
mv website.conf "${NGINX_CONF_PATH}/website.conf"
# Validate the new config immediately. Don't abort — restore the previous
# (working) config, keep the failing one for inspection, and continue loudly.
if ! nginx -t; then
echo "ERROR: new website.conf fails 'nginx -t' — restoring the previous config." >&2
mv "${NGINX_CONF_PATH}/website.conf" "${NGINX_CONF_PATH}/website.conf-${TARGET_VERSION}-failed"
cp "${BACKUP_DIR}/website.conf" "${NGINX_CONF_PATH}/website.conf"
nginx -t
fi
fi
# webconfig.js — preserve this instance's OAuth client
# The OAuth client values are served publicly in webconfig.js by design, but
# keep them out of this trace log — customers share these logs with support.
{ set +x; } 2>/dev/null
echo "Writing webconfig.js with this instance's OAuth client (values not logged)"
CLIENT_ID=$(extractPropValueFromSourceFile "security.client-id=" ${APPLICATION_PROPERTIES})
CLIENT_SECRET=$(extractPropValueFromSourceFile "security.client-secret=" ${APPLICATION_PROPERTIES})
(
cat <<EOF
window._env_ = {
"clientid": "$CLIENT_ID",
"clientsecret": "$CLIENT_SECRET",
"cloudProvider": "$CLOUD_PROVIDER",
"version": "${TARGET_VERSION%%-*}"
};
EOF
) | tee /usr/share/nginx/admin-ui/webconfig.js >/dev/null
chown ${NGINX_USER}:${NGINX_USER} /usr/share/nginx/admin-ui/webconfig.js
set -x
#
# 5. application.properties — add the 1.3.0-required properties (idempotent)
#
function ensure_prop {
local key="$1" value="$2"
grep -q "^${key}=" "${APPLICATION_PROPERTIES}" || \
echo "${key}=${value}" >> "${APPLICATION_PROPERTIES}"
}
cp -a "${APPLICATION_PROPERTIES}" "${BACKUP_DIR}/application.properties"
# Required for the Type=notify systemd unit — without it the service never
# signals readiness and systemd kills it at TimeoutSec.
ensure_prop "features.systemd.notify" "true"
# 1.3.0 resolves the client address itself (security.client-ip.trusted-proxies)
# and rebuilds proxied URLs from security.redirect.base-path, so Spring's own
# forwarded-header handling must be off. 1.2.x set this to "framework"; on
# 1.3.0 any value other than "none" is logged as an error at startup and
# bypasses the trusted-proxy resolution, so replace whatever is there.
sed -i '/^server\.forward-headers-strategy=/d' "${APPLICATION_PROPERTIES}"
ensure_prop "server.forward-headers-strategy" "none"
ensure_prop "security.redirect.base-path" "backend/"
ensure_prop "server.max-http-request-header-size" "128KB"
# Upload-path rewrite feature flags (ship-default empty ruleset)
ensure_prop "features.api.file-upload-accept-rules" ""
chown swiftgw:swiftgw "${APPLICATION_PROPERTIES}"
#
# 6. systemd unit — Spring Boot 4 launch pattern
#
cp -a /etc/systemd/system/swiftgateway.service "/etc/systemd/system/swiftgateway.service-${STAMP}"
cat > /etc/systemd/system/swiftgateway.service <<EOF
[Unit]
Description=StorageLink
After=syslog.target network.target
Before=nginx.service
[Service]
Type=notify
User=swiftgw
WorkingDirectory=/opt/swiftgw
EnvironmentFile=-/opt/swiftgw/swiftgateway-${TARGET_VERSION}.conf
ExecStart=/usr/bin/java \$JAVA_OPTS -jar /opt/swiftgw/swiftgateway-${TARGET_VERSION}.jar \$RUN_ARGS
SuccessExitStatus=143
AmbientCapabilities=CAP_NET_BIND_SERVICE
NotifyAccess=all
TimeoutSec=900
[Install]
WantedBy=multi-user.target
EOF
# Login banner version
sed -i "s/1\..*/${TARGET_VERSION}/" /etc/profile.d/login-info.sh 2>/dev/null || true
#
# 7. Restart everything and wait for health
#
if nginx -t; then
systemctl restart nginx
else
echo "WARNING: nginx config fails validation — nginx NOT restarted (see errors above)." >&2
fi
systemctl daemon-reload
systemctl start swiftgateway
# First start after the upgrade applies the 1.3.0 schema migrations, so allow
# up to 10 minutes on small VMs.
for i in $(seq 1 120); do
sleep 5
if curl -sk --max-time 5 http://127.0.0.1:8080/actuator/health | grep -q '"UP"'; then
echo "StorageLink ${TARGET_VERSION} is UP after upgrade."
break
fi
if [ "$i" == "120" ]; then
echo "ERROR: backend did not become healthy in 10 minutes"
echo "The service was left running and may still be starting. Check 'systemctl status swiftgateway'"
echo "and 'journalctl -u swiftgateway'. Once the cause is fixed it is safe to run this script again."
journalctl -u swiftgateway --no-pager | tail -40
exit 1
fi
done
echo
echo "Upgrade to ${TARGET_VERSION} complete."
echo "Rollback bundle: ${BACKUP_DIR}"
echo "PostgreSQL was left at its current version; 1.3.0 needs no database upgrade."
echo "Reminder: if this instance has no license, activate one in Settings -> License."
