add basic backup, backup-list and restore functionality
This commit is contained in:
parent
779b632835
commit
fdd76bbce5
26
README.md
26
README.md
@ -14,16 +14,30 @@ run `pip install --upgrade truetool`
|
|||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
Just run `truetool` in the shell of your TrueNAS SCALE machine, to have it process Patch and Minor version updates for all Apps
|
running `truetool` should be a good start.
|
||||||
|
|
||||||
Additional options are available:
|
Additional options are available:
|
||||||
|
|
||||||
- `truetool --catalog CATALOGNAME` where CATALOGNAME is the name of the catalog you want to process in caps
|
##### Help
|
||||||
- `truetool --versioning SCHEME` where SCHEME is the highest semver version you want to process. options: `patch`, `minor` and `major`
|
|
||||||
|
|
||||||
|
|
||||||
- `truetool -h` for the CLI help page
|
- `truetool -h` for the CLI help page
|
||||||
- `truetool -s` or ` truetool --sync` to sync the catalogs before running auto-update
|
|
||||||
- `truetool -p` or ` truetool --prune` to prune (remove) old docker images after running auto-update
|
|
||||||
|
##### Update
|
||||||
|
|
||||||
|
- `truetool -u` or ` truetool --update` update TrueNAS SCALE Apps
|
||||||
|
|
||||||
|
|
||||||
|
- `truetool --catalog CATALOGNAME` where CATALOGNAME is the name of the catalog you want to process in caps
|
||||||
|
- `truetool --versioning SCHEME` where SCHEME is the highest semver version you want to process. options: `patch`, `minor` and `major`
|
||||||
- `truetool -a` or ` truetool --all` updates both active (running) and non-active (stuck or stopped) Apps
|
- `truetool -a` or ` truetool --all` updates both active (running) and non-active (stuck or stopped) Apps
|
||||||
|
|
||||||
|
|
||||||
|
#### Backup
|
||||||
- `truetool -b` or ` truetool --backup` backup the complete Apps system prior to updates
|
- `truetool -b` or ` truetool --backup` backup the complete Apps system prior to updates
|
||||||
|
- `truetool -r` or ` truetool --restore` restores a specific backup by name
|
||||||
|
|
||||||
|
#### Other
|
||||||
|
|
||||||
|
- `truetool -s` or ` truetool --sync` to sync the catalogs before running updates
|
||||||
|
- `truetool -p` or ` truetool --prune` to prune (remove) old docker images after running auto-update
|
2
setup.py
2
setup.py
@ -7,7 +7,7 @@ README_MD = open(join(dirname(abspath(__file__)), "README.md")).read()
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="truetool",
|
name="truetool",
|
||||||
version="1.0.0",
|
version="2.0.0",
|
||||||
|
|
||||||
# The packages that constitute your project.
|
# The packages that constitute your project.
|
||||||
# For my project, I have only one - "pydash".
|
# For my project, I have only one - "pydash".
|
||||||
|
@ -2,6 +2,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
class Chart(object):
|
class Chart(object):
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
@ -51,32 +52,35 @@ def check_semver(current: str, latest: str):
|
|||||||
|
|
||||||
|
|
||||||
def execute_upgrades():
|
def execute_upgrades():
|
||||||
if ALL:
|
if UPDATE:
|
||||||
if CATALOG == "ALL":
|
if ALL:
|
||||||
filtered = filter(lambda a: a.update_available and a.status == "active", INSTALLED_CHARTS)
|
if CATALOG == "ALL":
|
||||||
else:
|
filtered = filter(lambda a: a.update_available and a.status == "active", INSTALLED_CHARTS)
|
||||||
filtered = filter(lambda a: a.update_available and a.status == "active" and a.catalog == CATALOG, INSTALLED_CHARTS)
|
|
||||||
else:
|
|
||||||
if CATALOG == "ALL":
|
|
||||||
filtered = filter(lambda a: a.update_available, INSTALLED_CHARTS)
|
|
||||||
else:
|
|
||||||
filtered = filter(lambda a: a.update_available and a.catalog == CATALOG, INSTALLED_CHARTS)
|
|
||||||
for chart in filtered:
|
|
||||||
pre_update_ver = chart.human_version
|
|
||||||
post_update_ver = chart.human_latest_version
|
|
||||||
split_current_version = chart.human_version.split("_", 1)
|
|
||||||
current_version = split_current_version[1]
|
|
||||||
split_latest = chart.human_latest_version.split("_", 1)
|
|
||||||
latest_version = split_latest[1]
|
|
||||||
if check_semver(current_version, latest_version):
|
|
||||||
print(f"Updating {chart.name}... \n")
|
|
||||||
pre_update_ver = chart.human_version
|
|
||||||
result = subprocess.run(['cli', '-c', f'app chart_release upgrade release_name="{chart.name}"'], capture_output=True)
|
|
||||||
post_update_ver = chart.human_latest_version
|
|
||||||
if "Upgrade complete" not in result.stdout.decode('utf-8'):
|
|
||||||
print(f"{chart.name} failed to upgrade. \n{result.stdout.decode('utf-8')}")
|
|
||||||
else:
|
else:
|
||||||
print(f"{chart.name} upgraded ({pre_update_ver} --> {post_update_ver})")
|
filtered = filter(lambda a: a.update_available and a.status == "active" and a.catalog == CATALOG, INSTALLED_CHARTS)
|
||||||
|
else:
|
||||||
|
if CATALOG == "ALL":
|
||||||
|
filtered = filter(lambda a: a.update_available, INSTALLED_CHARTS)
|
||||||
|
else:
|
||||||
|
filtered = filter(lambda a: a.update_available and a.catalog == CATALOG, INSTALLED_CHARTS)
|
||||||
|
for chart in filtered:
|
||||||
|
pre_update_ver = chart.human_version
|
||||||
|
post_update_ver = chart.human_latest_version
|
||||||
|
split_current_version = chart.human_version.split("_", 1)
|
||||||
|
current_version = split_current_version[1]
|
||||||
|
split_latest = chart.human_latest_version.split("_", 1)
|
||||||
|
latest_version = split_latest[1]
|
||||||
|
if check_semver(current_version, latest_version):
|
||||||
|
print(f"Updating {chart.name}... \n")
|
||||||
|
pre_update_ver = chart.human_version
|
||||||
|
result = subprocess.run(['cli', '-c', f'app chart_release upgrade release_name="{chart.name}"'], capture_output=True)
|
||||||
|
post_update_ver = chart.human_latest_version
|
||||||
|
if "Upgrade complete" not in result.stdout.decode('utf-8'):
|
||||||
|
print(f"{chart.name} failed to upgrade. \n{result.stdout.decode('utf-8')}")
|
||||||
|
else:
|
||||||
|
print(f"{chart.name} upgraded ({pre_update_ver} --> {post_update_ver})")
|
||||||
|
else:
|
||||||
|
print("Update disabled, skipping...")
|
||||||
|
|
||||||
def fetch_charts():
|
def fetch_charts():
|
||||||
rawcharts = subprocess.run(["cli", "-c", "app chart_release query"], stdout=subprocess.PIPE)
|
rawcharts = subprocess.run(["cli", "-c", "app chart_release query"], stdout=subprocess.PIPE)
|
||||||
@ -90,16 +94,27 @@ def process_args():
|
|||||||
global PRUNE
|
global PRUNE
|
||||||
global ALL
|
global ALL
|
||||||
global BACKUP
|
global BACKUP
|
||||||
|
global UPDATE
|
||||||
|
global RESTORE
|
||||||
|
global LIST
|
||||||
parser = argparse.ArgumentParser(description='Update TrueNAS SCALE Apps')
|
parser = argparse.ArgumentParser(description='Update TrueNAS SCALE Apps')
|
||||||
parser.add_argument('--catalog', nargs='?', default='ALL', help='name of the catalog you want to process in caps. Or "ALL" to render all catalogs.')
|
parser.add_argument('-c', '--catalog', nargs='?', default='ALL', help='name of the catalog you want to process in caps. Or "ALL" to render all catalogs.')
|
||||||
parser.add_argument('--versioning', nargs='?', default='minor', help='Name of the versioning scheme you want to update. Options: major, minor or patch. Defaults to minor')
|
parser.add_argument('-v', '--versioning', nargs='?', default='minor', help='Name of the versioning scheme you want to update. Options: major, minor or patch. Defaults to minor')
|
||||||
parser.add_argument('-s', '--sync', action="store_true", help='sync catalogs before trying to update')
|
parser.add_argument('-s', '--sync', action="store_true", help='sync catalogs before trying to update')
|
||||||
|
parser.add_argument('-u', '--update', action="store_true", help='update the Apps in the selected catalog')
|
||||||
parser.add_argument('-p', '--prune', action="store_true", help='prune old docker images after update')
|
parser.add_argument('-p', '--prune', action="store_true", help='prune old docker images after update')
|
||||||
parser.add_argument('-a', '--all', action="store_true", help='update "active" apps only and ignore "stopped" or "stuck" apps')
|
parser.add_argument('-a', '--all', action="store_true", help='update "active" apps only and ignore "stopped" or "stuck" apps')
|
||||||
parser.add_argument('-b', '--backup', action="store_true", help='backup the complete Apps system prior to updates')
|
parser.add_argument('-b', '--backup', action="store_true", help='backup the complete Apps system prior to updates')
|
||||||
|
parser.add_argument('-r', '--restore', nargs='?', help='restore a previous backup, disables all other features')
|
||||||
|
parser.add_argument('-l', '--list', action="store_true", help='lists existing backups')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
CATALOG = args.catalog
|
CATALOG = args.catalog
|
||||||
VERSIONING = args.versioning
|
VERSIONING = args.versioning
|
||||||
|
RESTORE = args.restore
|
||||||
|
if args.update:
|
||||||
|
UPDATE = True
|
||||||
|
else:
|
||||||
|
UPDATE = False
|
||||||
if args.sync:
|
if args.sync:
|
||||||
SYNC = True
|
SYNC = True
|
||||||
else:
|
else:
|
||||||
@ -116,6 +131,11 @@ def process_args():
|
|||||||
BACKUP = True
|
BACKUP = True
|
||||||
else:
|
else:
|
||||||
BACKUP = False
|
BACKUP = False
|
||||||
|
if args.list:
|
||||||
|
LIST = True
|
||||||
|
else:
|
||||||
|
LIST = False
|
||||||
|
|
||||||
|
|
||||||
def sync_catalog():
|
def sync_catalog():
|
||||||
if SYNC:
|
if SYNC:
|
||||||
@ -127,17 +147,36 @@ def sync_catalog():
|
|||||||
temp = process.stdout.read()
|
temp = process.stdout.read()
|
||||||
if temp:
|
if temp:
|
||||||
print (temp.decode('utf-8'))
|
print (temp.decode('utf-8'))
|
||||||
|
else:
|
||||||
|
print("Catalog Sync disabled, skipping...")
|
||||||
|
|
||||||
def docker_prune():
|
def docker_prune():
|
||||||
if PRUNE:
|
if PRUNE:
|
||||||
print("Pruning old docker images...\n")
|
print("Pruning old docker images...\n")
|
||||||
process = subprocess.Popen(["docker", "image", "prune", "-af"], stdout=subprocess.PIPE)
|
process = subprocess.Popen(["docker", "image", "prune", "-af"], stdout=subprocess.PIPE)
|
||||||
print("Images pruned.\n")
|
print("Images pruned.\n")
|
||||||
|
else:
|
||||||
|
print("Container Image Pruning disabled, skipping...")
|
||||||
|
|
||||||
def chart_backup():
|
def apps_backup():
|
||||||
if BACKUP:
|
if BACKUP:
|
||||||
print("Running App Backup...\n")
|
print("Running App Backup...\n")
|
||||||
process = subprocess.Popen(["cli", "-c", "app kubernetes backup_chart_releases"], stdout=subprocess.PIPE)
|
now = datetime.now()
|
||||||
|
command = "app kubernetes backup_chart_releases backup_name=TrueTool_"+now.strftime("%Y_%d_%m_%H_%M_%S")
|
||||||
|
process = subprocess.Popen(["cli", "-c", command], stdout=subprocess.PIPE)
|
||||||
|
while process.poll() is None:
|
||||||
|
lines = process.stdout.readline()
|
||||||
|
print (lines.decode('utf-8'))
|
||||||
|
temp = process.stdout.read()
|
||||||
|
if temp:
|
||||||
|
print (temp.decode('utf-8'))
|
||||||
|
else:
|
||||||
|
print("Backup disabled, skipping...")
|
||||||
|
|
||||||
|
def backups_list():
|
||||||
|
if LIST:
|
||||||
|
print("Running App Backup...\n")
|
||||||
|
process = subprocess.Popen(["cli", "-c", "app kubernetes list_backups"], stdout=subprocess.PIPE)
|
||||||
while process.poll() is None:
|
while process.poll() is None:
|
||||||
lines = process.stdout.readline()
|
lines = process.stdout.readline()
|
||||||
print (lines.decode('utf-8'))
|
print (lines.decode('utf-8'))
|
||||||
@ -145,18 +184,36 @@ def chart_backup():
|
|||||||
if temp:
|
if temp:
|
||||||
print (temp.decode('utf-8'))
|
print (temp.decode('utf-8'))
|
||||||
|
|
||||||
|
def apps_restore():
|
||||||
|
print("Running Backup Restore...\n")
|
||||||
|
command = "app kubernetes restore_backup backup_name="+RESTORE
|
||||||
|
print(f"{command}")
|
||||||
|
process = subprocess.Popen(["cli", "-c", command], stdout=subprocess.PIPE)
|
||||||
|
while process.poll() is None:
|
||||||
|
lines = process.stdout.readline()
|
||||||
|
print (lines.decode('utf-8'))
|
||||||
|
temp = process.stdout.read()
|
||||||
|
if temp:
|
||||||
|
print (temp.decode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
process_args()
|
process_args()
|
||||||
print("Starting TrueCharts App updater...\n")
|
print("Starting TrueCharts TrueTool...\n")
|
||||||
chart_backup()
|
if RESTORE:
|
||||||
sync_catalog()
|
apps_restore()
|
||||||
charts = fetch_charts()
|
elif LIST:
|
||||||
parse_charts(charts)
|
backups_list()
|
||||||
print("Executing Updates...\n")
|
else:
|
||||||
execute_upgrades()
|
apps_backup()
|
||||||
print("Updating Finished\n")
|
sync_catalog()
|
||||||
docker_prune()
|
charts = fetch_charts()
|
||||||
exit(0)
|
parse_charts(charts)
|
||||||
|
print("Executing Updates...\n")
|
||||||
|
execute_upgrades()
|
||||||
|
docker_prune()
|
||||||
|
print("TrueTool Finished\n")
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
Loading…
Reference in New Issue
Block a user