diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..cce767d4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +# See https://pre-commit.com for more information +repos: +- repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.1.10 + hooks: + - id: remove-tabs + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: fix-byte-order-marker + - id: mixed-line-ending + - id: check-merge-conflict + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-docstring-first + - id: check-symlinks + - id: destroyed-symlinks + - id: fix-byte-order-marker diff --git a/truetool/.github/renovate-config.js b/truetool/.github/renovate-config.js new file mode 100644 index 00000000..da91bb42 --- /dev/null +++ b/truetool/.github/renovate-config.js @@ -0,0 +1,25 @@ +module.exports = { + dryRun: false, + username: 'truecharts-admin', + gitAuthor: 'truecharts-admin ', + onboarding: false, + platform: 'github', + repositories: [ + 'truecharts/truetool', + ], + packageRules: [ + { + description: 'lockFileMaintenance', + matchUpdateTypes: [ + 'pin', + 'digest', + 'patch', + 'minor', + 'major', + 'lockFileMaintenance', + ], + dependencyDashboardApproval: false, + stabilityDays: 0, + }, + ], +}; diff --git a/truetool/.github/renovate.json5 b/truetool/.github/renovate.json5 new file mode 100644 index 00000000..de107f43 --- /dev/null +++ b/truetool/.github/renovate.json5 @@ -0,0 +1,23 @@ +{ + "semanticCommits": "enabled", + "extends": ["helpers:pinGitHubActionDigests"], + "dependencyDashboard": true, + "dependencyDashboardTitle": "Renovate Dashboard 🤖", + "suppressNotifications": ["prIgnoreNotification"], + "commitMessageTopic": "{{depName}}", + "commitMessageExtra": "to {{newVersion}}", + "commitMessageSuffix": "", + "rebaseWhen": "conflicted", + "prConcurrentLimit": 100, + "pinDigests": true, + "automerge": true, + "gitAuthor": "TrueCharts-Admin ", + "packageRules": [ + // Setup datasources for github actions + { + "matchManagers": ["github-actions"], + "commitMessageTopic": "github-action {{depName}} [skip ci]", + "automerge": true, + } + ] +} diff --git a/truetool/.github/workflows/renovate.yml b/truetool/.github/workflows/renovate.yml new file mode 100644 index 00000000..1ef06756 --- /dev/null +++ b/truetool/.github/workflows/renovate.yml @@ -0,0 +1,18 @@ +name: Renovate +on: + workflow_dispatch: + schedule: + - cron: "0 */6 * * *" +jobs: + renovate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + with: + token: ${{ secrets.BOT_TOKEN }} + - name: Self-hosted Renovate + uses: renovatebot/github-action@927d318430b8127887559d9721fdfcaaa4646fb9 # v35.2.0 + with: + configurationFile: .github/renovate-config.js + token: ${{ secrets.BOT_TOKEN }} diff --git a/truetool/.github/workflows/shellcheck.yml b/truetool/.github/workflows/shellcheck.yml new file mode 100644 index 00000000..b0c3d633 --- /dev/null +++ b/truetool/.github/workflows/shellcheck.yml @@ -0,0 +1,26 @@ +on: + push: + pull_request: + workflow_dispatch: + +name: 'Lint and Test' + +jobs: + shellcheck: + name: Shellcheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3 + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + check_together: 'yes' + env: + SHELLCHECK_OPTS: -e SC2154 + + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3 + - uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435 # v4 + - uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507 # tag=v3.0.0 diff --git a/truetool/.pre-commit-config.yaml b/truetool/.pre-commit-config.yaml new file mode 100644 index 00000000..cce767d4 --- /dev/null +++ b/truetool/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +# See https://pre-commit.com for more information +repos: +- repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.1.10 + hooks: + - id: remove-tabs + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: fix-byte-order-marker + - id: mixed-line-ending + - id: check-merge-conflict + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-docstring-first + - id: check-symlinks + - id: destroyed-symlinks + - id: fix-byte-order-marker diff --git a/truetool/LICENSE b/truetool/LICENSE new file mode 100644 index 00000000..0a7a3987 --- /dev/null +++ b/truetool/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2022, TrueCharts +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/truetool/README.md b/truetool/README.md new file mode 100644 index 00000000..42cfacd3 --- /dev/null +++ b/truetool/README.md @@ -0,0 +1,144 @@ +# truetool + +An easy tool for frequently used TrueNAS SCALE CLI utilities. + +Please before using this tool, [read this note](https://truecharts.org/manual/guides/Important-MUST-READ) + +## Table of contents: + +* [Synopsis](#synopsis) +* [Arguments](#arguments) +* [How to Install](#how-to-install) +* [How to Update](#how-to-update) +* [Creating a Cron Job](#creating-a-cron-job) +* [Additional Information](#additional-information) + +
+ +## Synopsis + +TrueTool is a command line tool, designed to enable some features of TrueNAS SCALE that are either not-enabled by default or not-available in the Web-GUI. +It also offers a few handy shortcuts for commonly required chores, like: Enabling Apt or Helm + +## Arguments + +| Flag | Example | Parameter | Description | +| --------------- | ---------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| --delete-backup | --delete-backup | None | Opens a menu to delete backups
_Useful if you need to delete old system backups or backups from other scripts_ | +| --restore | --restore | None | Restore TrueTool specific `ix-applications dataset` snapshot | +| --mount | --mount | None | Initiates mounting feature
Choose between unmounting and mounting PVC data | +| --dns | --dns | None | list all of your applications DNS names and their web ports | +| --list-backups | --list-backups | None | Prints a list of backups available | +| --helm-enable | --helm-enable | None | Enables Helm command access on SCALE | +| --kubeapi-enable | --kubeapi-enable | None | Enables external access to Kuberntes API port | +| --apt-enable | --apt-enable | None | Enables Apt command access on SCALE | +| --no-color | --no-color | None | (deprecated) Disables showing colors in terminal output, usefull for SCALE Email output | +| -U | -U | None | Update applications, ignoring major version changes | +| -u | -u | None | Update applications, do NOT update if there was a major version change | +| -b | -b 14 | Integer | Backup `ix-applications` dataset
_Creates backups up to the number you've chosen_ | +| -i | -i nextcloud -i sonarr | String | Applications listed will be ignored during updating
_List one application after another as shown in the example_ | +| -v | -v | None | Verbose Output
| +| -t | -t 150 | Integer | Set a custom timeout to be used with either:
`-m`
_Time the script will wait for application to be "STOPPED"_
or
`-(u\|U)`
_Time the script will wait for application to be either "STOPPED" or "ACTIVE"_ | +| -s | -s | None | Sync Catalogs prior to updating | +| -p | -p | None | Prune old/unused docker images | + + +
+
+ +## How to Install + +### oneliner + +``` +curl -s https://raw.githubusercontent.com/truecharts/truetool/main/bootstrap | bash +``` + +You can now use truetool anywhere `truetool -ARGUMENTS` + +## Manual Install + +### Choose a folder + +It's important to save the script in a folder that is persistent across TrueNAS System Updates. +This saves you from reinstalling or experiencing an accidental lack-of-backups after an update. + +##### New dataset + +In this example we created a `scripts` dataset on the TrueNAS SCALE system, feel free to use another folder. + +##### Root folder + +The `/root` folder houses files for the root user. +It's also persistent across updates and hence can be safely used for storing the script. + +### Open a Terminal + +**Change Directory to your scripts folder** + +``` +cd /mnt/pool/scripts +``` + +**Git Clone truetool** + +``` +git clone https://github.com/truecharts/truetool.git +``` + +**Change Directory to truetool folder** + +``` +cd truetool +``` + +From here, you can just run truetool with `bash truetool.sh -ARGUMENTS` + +
+ +## How to Update + +TrueTool updates itself automatically. + +
+ + +## Creating a Cron Job + +1. TrueNAS SCALE GUI +2. System Settings +3. Advanced +4. Cron Jobs + 1. Click Add + +| Name | Value | Reason | +| ---------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| `Description` | TrueTool Update apps | This is up to you, put whatever you think is a good description in here | +| `Command` | `bash /PATH/TO/truetool_DIRECTORY/truetool.sh --no-color -b 14 -sup` | This is the command you will be running on your schedule, example: `bash /mnt/speed/scripts/truetool/truetool.sh -b 14 -sup` | +| `Run As User` | `root` | Running the script as `root` is REQUIRED. You cannot access all of the kubernetes functions without this user. | +| `Schedule` | Up to you, example: `0400` | Again up to you | +| `Hide Standard Output` | `False` or Un-ticked | It's best to keep an eye on updates and enable this to receive email reports | +| `Hide Standard Error` | `False` or Un-ticked | We definitely want to see what errors occurred during updating | +| `Enabled` | `True` or Ticked | This will Enable the script to run on your schedule | + +
+
+ +### Additional Information + +#### TrueTool vs HeavyScript + +TrueTool and HeavyScript are based, in essence, based on the original (python based) TrueUpdate and TrueTool. +Then Support-Manager for TrueCharts, HeavyBullets8, ported this to Bash and started adding some additional logic and options for tasks we frequently needed our users to do, such as mounting PVC's. + +After a month or so, the TrueCharts Team officially started refactoring this expanded bash-port. Due to personal reasons, HeavyBullets by then decided to separate from TrueCharts after merging the TrueCharts refactor into his own work. The beauty of OpenSource. + +From this point onwards the HeavyScript and TrueTool diverged a bit. Development of TrueTool slowed down a bit during Q3 of 2022 and HeavyScript significantly improved on the reliability of primary features while also adding some of it's own. + +While previously HeavyScript and TrueTool shared a lot of code back-and-forth without much care to attribution, we've decided to more officially attribute and start using functions with all the HeavyScript improvements in-place for some of the primary features like: Backup, Restore and App-Updates. Cleanly seperating those from TrueCharts features that have neglitable involvement of HeavyScript. + +Users from HeavyScript should be able to safely start using TrueTool, as we've made precautions to ensure the backups take over smoothly. +We, however, do _not_ advise using HeavyScript with TrueCharts Apps. Not because it's a bad App, but because we offer an alternative that is validated by our Staff. + +We internally review changes within our staff team, to verify we somewhat stick to best-practices. This means, in some cases, we decided not to port certain features from HeavyScript and did decide to add features we think are useful and safe. +But this also means we can give guarantees TrueTool works optimally with our Catalog of TrueNAS SCALE Apps, as well as official Apps. diff --git a/truetool/bin/truetool b/truetool/bin/truetool new file mode 100644 index 00000000..4376aa1f --- /dev/null +++ b/truetool/bin/truetool @@ -0,0 +1,6 @@ +#!/bin/bash +orig_cwd=$(pwd) +cd "$HOME/truetool" || exit +# pass all arguments '$@' to truetool.sh +bash ./truetool.sh "$@" +cd "$orig_cwd" || exit diff --git a/truetool/bootstrap b/truetool/bootstrap new file mode 100644 index 00000000..75d0bc48 --- /dev/null +++ b/truetool/bootstrap @@ -0,0 +1,62 @@ +#!/bin/bash +# exit on errors +set -e +# Check that we are root +if [[ ! $(whoami) == 'root' ]]; then + echo 'This is intended to be ran as root' + exit 1 +fi + +# Check if the truetool repo already exists +if [[ -d $HOME/truetool ]]; then + cd "$HOME/truetool" || exit + git log -n1|cat +else + cd "$HOME" || exit + git clone https://github.com/truecharts/truetool.git +fi + +# Check if $HOME/bin exists, or make it +if [[ ! -d $HOME/bin ]]; then + mkdir -p "$HOME/bin" +fi + +# Check if the truetool wrapper exists, or make it +if [[ ! -x "$HOME/bin/truetool" ]]; then + install -m555 -v "$HOME/truetool/bin/truetool" "$HOME/bin/" +fi + +# these vars are used by the following functions +LINE_TO_ADD='' +TARGET_FILE_FOR_ADD="$HOME/.profile" + +check_if_line_exists() +{ + if [[ "$VERBOSITY" -gt '7' ]]; then + echo "Checking for '$LINE_TO_ADD' in $TARGET_FILE_FOR_ADD" + fi + grep -qsFx "$LINE_TO_ADD" "$TARGET_FILE_FOR_ADD" +} + +add_line_to() +{ + if [[ "$VERBOSITY" -gt '5' ]]; then + echo "Adding '$LINE_TO_ADD' to $TARGET_FILE_FOR_ADD" + fi + TARGET_FILE=$TARGET_FILE_FOR_ADD + [ -w "$TARGET_FILE" ] || TARGET_FILE=$TARGET_FILE_FOR_ADD + printf "%s\n" "$LINE_TO_ADD" >> "$TARGET_FILE" +} + +dotfiles_install () { + # Adjusting dotfiles by adding $HOME/bin to our path + touch "$HOME/.zshrc" + touch "$HOME/.bashrc" + LINE_TO_ADD="$(printf "export PATH=\"%s:\$PATH\"" '/root/bin')" + TARGET_FILE_FOR_ADD="$HOME/.bashrc" + check_if_line_exists || add_line_to + TARGET_FILE_FOR_ADD="$HOME/.zshrc" + check_if_line_exists || add_line_to +} + +dotfiles_install diff --git a/truetool/functions/LICENSE b/truetool/functions/LICENSE new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/truetool/functions/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/truetool/functions/backup.sh b/truetool/functions/backup.sh new file mode 100644 index 00000000..46ffa330 --- /dev/null +++ b/truetool/functions/backup.sh @@ -0,0 +1,311 @@ +#!/bin/bash + + +backup(){ +echo_backup+=("🄱 🄰 🄲 🄺 🅄 🄿 🅂") +echo_backup+=("Number of backups was set to $number_of_backups") +date=$(date '+%Y_%m_%d_%H_%M_%S') +[[ "$verbose" == "true" ]] && cli -c 'app kubernetes backup_chart_releases backup_name=''"'TrueTool_"$date"'"' &> /dev/null && echo_backup+=(TrueTool_"$date") +[[ -z "$verbose" ]] && echo_backup+=("\nNew Backup Name:") && cli -c 'app kubernetes backup_chart_releases backup_name=''"'TrueTool_"$date"'"' | tail -n 1 &> /dev/null && echo_backup+=(TrueTool_"$date") +mapfile -t list_backups < <(cli -c 'app kubernetes list_backups' | grep -E "HeavyScript_|TrueTool_" | sort -t '_' -Vr -k2,7 | awk -F '|' '{print $2}'| tr -d " \t\r") +if [[ ${#list_backups[@]} -gt "$number_of_backups" ]]; then + echo_backup+=("\nDeleted the oldest backup(s) for exceeding limit:") + overflow=$(( ${#list_backups[@]} - "$number_of_backups" )) + mapfile -t list_overflow < <(cli -c 'app kubernetes list_backups' | grep -E "HeavyScript_|TrueTool_" | sort -t '_' -V -k2,7 | awk -F '|' '{print $2}'| tr -d " \t\r" | head -n "$overflow") + for i in "${list_overflow[@]}" + do + cli -c 'app kubernetes delete_backup backup_name=''"'"$i"'"' &> /dev/null || echo_backup+=("Failed to delete $i") + echo_backup+=("$i") + done +fi + +#Dump the echo_array, ensures all output is in a neat order. +for i in "${echo_backup[@]}" +do + echo -e "$i" +done +echo +echo +} +export -f backup + + +deleteBackup(){ +clear -x && echo "pulling all restore points.." +# shellcheck disable=SC2178 +list_backups=$(cli -c 'app kubernetes list_backups' | sort -t '_' -Vr -k2,7 | tr -d " \t\r" | awk -F '|' '{print $2}' | nl -s ") " | column -t) +# shellcheck disable=SC2128 +if [[ -z "$list_backups" ]]; then + echo "No restore points available" + exit +fi + +#Select a restore point +while true +do + clear -x + title + echo -e "Choose a Restore Point to Delete\nThese may be out of order if they are not TrueTool backups" + echo "$list_backups" + echo + echo "0) Exit" + read -rt 240 -p "Please type a number: " selection || { echo -e "\nFailed to make a selection in time" ; exit; } + restore_point=$(echo "$list_backups" | grep ^"$selection)" | awk '{print $2}') + if [[ $selection == 0 ]]; then + echo "Exiting.." + exit + elif [[ -z "$selection" ]]; then + echo "Your selection cannot be empty" + sleep 3 + continue + elif [[ -z "$restore_point" ]]; then + echo "Invalid Selection: $selection, was not an option" + sleep 3 + continue + fi + break # Break out of the loop if all of the If statement checks above are untrue +done + +#Confirm deletion +while true +do + clear -x + echo -e "WARNING:\nYou CANNOT go back after deleting your restore point" + echo -e "\n\nYou have chosen:\n$restore_point\n\n" + read -rt 120 -p "Would you like to proceed with deletion? (y/N): " yesno || { echo -e "\nFailed to make a selection in time" ; exit; } + case $yesno in + [Yy] | [Yy][Ee][Ss]) + echo -e "\nDeleting $restore_point" + cli -c 'app kubernetes delete_backup backup_name=''"'"$restore_point"'"' &>/dev/null || { echo "Failed to delete backup.."; exit; } + echo "Sucessfully deleted" + break + ;; + [Nn] | [Nn][Oo]) + echo "Exiting" + exit + ;; + *) + echo "That was not an option, try again" + sleep 3 + continue + ;; + esac +done + +#Check if there are more backups to delete +while true +do + read -rt 120 -p "Delete more backups? (y/N): " yesno || { echo -e "\nFailed to make a selection in time" ; exit; } + case $yesno in + [Yy] | [Yy][Ee][Ss]) + deleteBackup + break + ;; + [Nn] | [Nn][Oo]) + exit + ;; + *) + echo "$yesno was not an option, try again" + sleep 2 + continue + ;; + + esac + +done +} +export -f deleteBackup + + +restore(){ +clear -x && echo "pulling restore points.." +list_backups=$(cli -c 'app kubernetes list_backups' | grep -E "HeavyScript_|TrueTool_" | sort -t '_' -Vr -k2,7 | tr -d " \t\r" | awk -F '|' '{print $2}' | nl -s ") " | column -t) +if [[ -z "$list_backups" ]]; then + echo "No TrueTool restore points available" + exit +fi + +#Select a restore point +while true +do + clear -x + title + echo "Choose a Restore Point" + echo "$list_backups" + echo + echo "0) Exit" + read -rt 240 -p "Please type a number: " selection || { echo -e "\nFailed to make a selection in time" ; exit; } + restore_point=$(echo "$list_backups" | grep ^"$selection)" | awk '{print $2}') + if [[ $selection == 0 ]]; then + echo "Exiting.." + exit + elif [[ -z "$selection" ]]; then + echo "Your selection cannot be empty" + sleep 3 + continue + elif [[ -z "$restore_point" ]]; then + echo "Invalid Selection: $selection, was not an option" + sleep 3 + continue + fi + break +done + + +## Check to see if empty PVC data is present in any of the applications ## + +# Find all pv_info.json files two subfolders deep with the restore point name +pool=$(cli -c 'app kubernetes config' | grep -E "pool\s\|" | awk -F '|' '{print $3}' | tr -d " \t\n\r") +files=$(find "$(find /mnt/"$pool"/ix-applications/backups -maxdepth 0 )" -name pv_info.json | grep "$restore_point"); + +# Iterate over the list of files +for file in $files; do + # Check if the file only contains {} subfolders + contents=$(cat "$file") + if [[ "$contents" == '{}' ]]; then + # Print the file if it meets the criterion + file=$(echo "$file" | awk -F '/' '{print $7}') + borked_array+=("${file}") + fi +done + + +# Grab applications that are supposed to have PVC data +mapfile -t apps_with_pvc < <(k3s kubectl get pvc -A | sort -u | awk '{print $1 "\t" $2 "\t" $4}' | sed "s/^0/ /" | awk '{print $1}' | cut -c 4-) + + +# Iterate over the list of applications with empty PVC data +# Unset the application if it is not supposed to have PVC data +index=0 +for app in "${borked_array[@]}"; do + if ! printf '%s\0' "${apps_with_pvc[@]}" | grep -iFxqz "${app}" ; then + unset "borked_array[$index]" + else + borked=True + fi + ((index++)) +done + + + +# If there is still empty PVC data, exit +if [[ $borked == True ]]; then + echo "Warning!:" + echo "The following applications have empty PVC data:" + for app in "${borked_array[@]}"; do + echo -e "$app" + done + echo "We have no choice but to exit" + echo "If you were to restore, you would lose all of your application data" + echo "If you are on Bluefin version: 22.12.0, and have not yet ran the patch, you will need to run it" + echo "Afterwards you will be able to create backups and restore them" + echo "This is a known ix-systems bug and has nothing to do with TrueTool" + exit +fi + + + +## Check the restore point, and ensure it is the same version as the current system ## +# Boot Query +boot_query=$(cli -m csv -c 'system bootenv query created,realname') + +# Get the date of system version and when it was updated +current_version=$(cli -m csv -c 'system version' | awk -F '-' '{print $3}') +when_updated=$(echo "$boot_query" | grep "$current_version",\ +| awk -F ',' '{print $2}' | sed 's/[T|-]/_/g' | sed 's/:/_/g' | awk -F '_' '{print $1 $2 $3 $4 $5}') + +# Get the date of the chosen restore point +restore_point_date=$(echo "$restore_point" | awk -F '_' '{print $2 $3 $4 $5 $6}' | tr -d "_") + +# Grab previous version +previous_version=$(echo "$boot_query" | sort -nr | grep -A 1 "$current_version," | tail -n 1) + +# Compare the dates +while (("$restore_point_date" < "$when_updated" )) +do + clear -x + echo "The restore point you have chosen is from an older version of Truenas Scale" + echo "This is not recommended, as it may cause issues with the system" + echo "Either that, or your systems date is incorrect.." + echo + echo "Current SCALE Information:" + echo "Version: $current_version" + echo "When Updated: $(echo "$restore_point" | awk -F '_' '{print $2 "-" $3 "-" $4}')" + echo + echo "Restore Point SCALE Information:" + echo "Version: $(echo "$previous_version" | awk -F ',' '{print $1}')" + echo "When Updated: $(echo "$previous_version" | awk -F ',' '{print $2}' | awk -F 'T' '{print $1}')" + echo + read -rt 120 -p "Would you like to proceed? (y/N): " yesno || { echo -e "\nFailed to make a selection in time" ; exit; } + case $yesno in + [Yy] | [Yy][Ee][Ss]) + echo "Proceeding.." + sleep 3 + break + ;; + [Nn] | [Nn][Oo]) + echo "Exiting" + exit + ;; + *) + echo "That was not an option, try again" + sleep 3 + continue + ;; + esac +done + + +#Confirm restore +while true +do + clear -x + echo -e "WARNING:\nThis is NOT guranteed to work\nThis is ONLY supposed to be used as a LAST RESORT\nConsider rolling back your applications instead if possible" + echo -e "\n\nYou have chosen:\n$restore_point\n\n" + read -rt 120 -p "Would you like to proceed with restore? (y/N): " yesno || { echo -e "\nFailed to make a selection in time" ; exit; } + case $yesno in + [Yy] | [Yy][Ee][Ss]) + pool=$(cli -c 'app kubernetes config' | grep -E "pool\s\|" | awk -F '|' '{print $3}' | tr -d " \t\n\r") + + # Set mountpoints to legacy prior to restore, ensures correct properties for the are set + echo -e "\nSetting correct ZFS properties for application volumes.." + for pvc in $(zfs list -t filesystem -r "$pool"/ix-applications/releases -o name -H | grep "volumes/pvc") + do + if zfs set mountpoint=legacy "$pvc"; then + echo "Success for - \"$pvc\"" + else + echo "Error: Setting properties for \"$pvc\", failed.." + fi + done + + # Ensure readonly is turned off + if ! zfs set readonly=off "$pool"/ix-applications;then + echo -e "Error: Failed to set ZFS ReadOnly to \"off\"" + echo -e "After the restore, attempt to run the following command manually:" + echo "zfs set readonly=off $pool/ix-applications" + fi + + echo "Finished setting properties.." + + # Beginning snapshot restore + echo -e "\nStarting restore, this will take a LONG time." + if ! cli -c 'app kubernetes restore_backup backup_name=''"'"$restore_point"'"'; then + echo "Restore failed, exiting.." + exit 1 + fi + exit + ;; + [Nn] | [Nn][Oo]) + echo "Exiting" + exit + ;; + *) + echo "That was not an option, try again" + sleep 3 + continue + ;; + esac +done + +} +export -f restore diff --git a/truetool/functions/dns.sh b/truetool/functions/dns.sh new file mode 100644 index 00000000..e628c12a --- /dev/null +++ b/truetool/functions/dns.sh @@ -0,0 +1,27 @@ +#!/bin/bash + + +dns(){ +clear -x +echo "Generating DNS Names.." + +# Pulling pod names +k3s crictl pods --namespace ix -s Ready | sed -E 's/[[:space:]]([0-9]*|About)[a-z0-9 ]{5,12}ago[[:space:]]//' | grep -v 'svclb-' | sed '1d' >> dns_file +mapfile -t ix_name_array < <(< dns_file awk '{print $4}' | sort -u ) + +# Pulling all ports +all_ports=$(k3s kubectl get service -A) + +clear -x +count=0 +for i in "${ix_name_array[@]}" +do + [[ count -le 0 ]] && echo -e "\n" && ((count++)) + full_app_name=$(grep -E "\s$i\s" "dns_file" | awk '{print $3}' | sed 's/-[^-]*-[^-]*$//' | sed 's/-0//' | head -n 1) + app_name=$(echo "$i" | cut -c 4-) + port=$(echo "$all_ports" | grep -E "\s$full_app_name\s" | awk '{print $6}' | grep -Eo "^[[:digit:]]+{1}") + echo -e "$app_name $full_app_name.$i.svc.cluster.local $port" +done | nl -s ") " -b t | sed '0,/\s\s\s/{s/\s\s\s/- ---- -------- ----/}'| column -t -N "#,Name,DNS_Name,Port" +rm dns_file +} +export -f dns diff --git a/truetool/functions/mount.sh b/truetool/functions/mount.sh new file mode 100644 index 00000000..a062bf27 --- /dev/null +++ b/truetool/functions/mount.sh @@ -0,0 +1,114 @@ +#!/bin/bash + + +mount(){ +pool=$(cli -c 'app kubernetes config' | grep -E "pool\s\|" | awk -F '|' '{print $3}' | tr -d " \t\n\r") +while true +do + clear -x + title + echo "PVC Mount Menu" + echo "--------------" + echo "1) Mount" + echo "2) Unmount All" + echo + echo "0) Exit" + read -rt 120 -p "Please type a number: " selection || { echo -e "\nFailed to make a selection in time" ; exit; } + case $selection in + 0) + echo "Exiting.." + exit + ;; + 1) + call=$(k3s kubectl get pvc -A | sort -u | awk '{print $1 "\t" $2 "\t" $4}' | sed "s/^0/ /") + mount_list=$(echo "$call" | sed 1d | nl -s ") ") + mount_title=$(echo "$call" | head -n 1) + list=$(echo -e "# $mount_title\n$mount_list" | column -t) + while true + do + clear -x + title + echo "$list" + echo + echo "0) Exit" + read -rt 120 -p "Please type a number: " selection || { echo -e "\nFailed to make a selection in time" ; exit; } + + #Check for valid selection. If no issues, continue + [[ $selection == 0 ]] && echo "Exiting.." && exit + app=$(echo -e "$list" | grep ^"$selection)" | awk '{print $2}' | cut -c 4- ) + [[ -z "${app}" ]] && echo "Invalid Selection: $selection, was not an option" && sleep 3 && continue + pvc=$(echo -e "$list" | grep ^"$selection)") + + #Stop applicaiton if not stopped + status=$(cli -m csv -c 'app chart_release query name,status' | grep "^${app}," | awk -F ',' '{print $2}'| tr -d " \t\n\r") + if [[ "$status" != "STOPPED" ]]; then + echo -e "\nStopping ${app} prior to mount" + if ! cli -c 'app chart_release scale release_name='\""${app}"\"\ 'scale_options={"replica_count": 0}' &> /dev/null; then + echo "Failed to stop ${app}" + exit 1 + else + echo "Stopped" + fi + else + echo -e "\n${app} is already stopped" + fi + + #Grab data then output and mount + data_name=$(echo "${pvc}" | awk '{print $3}') + volume_name=$(echo "${pvc}" | awk '{print $4}') + full_path=$(zfs list -t filesystem -r "${pool}"/ix-applications/releases/"${app}"/volumes -o name -H | grep "$volume_name") + if ! zfs set mountpoint=/truetool/"$data_name" "${full_path}" ; then + echo "Error: Failed to mount ${app}" + exit 1 + else + echo -e "\nMounted\n$data_name" + fi + echo -e "\nUnmount with:\nzfs set mountpoint=legacy ${full_path} && rmdir /mnt/truetool/$data_name\n\nOr use the Unmount All option\n" + + #Ask if user would like to mount something else + while true + do + echo + read -rt 120 -p "Would you like to mount anything else? (y/N): " yesno || { echo -e "\nFailed to make a selection in time" ; exit; } + case $yesno in + [Yy] | [Yy][Ee][Ss]) + clear -x + title + break + ;; + [Nn] | [Nn][Oo]) + exit + ;; + *) + echo "Invalid selection \"$yesno\" was not an option" + sleep 3 + continue + ;; + esac + done + done + ;; + 2) + mapfile -t unmount_array < <(basename -a /mnt/truetool/* | sed "s/*//") + [[ -z ${unmount_array[*]} ]] && echo "Theres nothing to unmount" && sleep 3 && continue + for i in "${unmount_array[@]}" + do + main=$(k3s kubectl get pvc -A | grep -E "\s$i\s" | awk '{print $1, $2, $4}') + app=$(echo "$main" | awk '{print $1}' | cut -c 4-) + pvc=$(echo "$main" | awk '{print $3}') + full_path=$(find /mnt/"${pool}"/ix-applications/releases/"${app}"/volumes/ -maxdepth 0 | cut -c 6-) + zfs set mountpoint=legacy "${full_path}""${pvc}" + echo "$i unmounted" && rmdir /mnt/truetool/"$i" || echo "failed to unmount $i" + done + rmdir /mnt/truetool + sleep 3 + ;; + *) + echo "Invalid selection, \"$selection\" was not an option" + sleep 3 + continue + ;; + esac +done +} +export -f mount diff --git a/truetool/functions/readme.md b/truetool/functions/readme.md new file mode 100644 index 00000000..360abd32 --- /dev/null +++ b/truetool/functions/readme.md @@ -0,0 +1,3 @@ +# HeavyScript Functions + +This folder contains modified variants of functions ported from HeavyScript. diff --git a/truetool/functions/update_apps.sh b/truetool/functions/update_apps.sh new file mode 100644 index 00000000..6ad0d59f --- /dev/null +++ b/truetool/functions/update_apps.sh @@ -0,0 +1,363 @@ +#!/bin/bash + + +commander(){ +mapfile -t array < <(cli -m csv -c 'app chart_release query name,update_available,human_version,human_latest_version,container_images_update_available,status' | tr -d " \t\r" | grep -E ",true($|,)" | sort) +echo -e "🅄 🄿 🄳 🄰 🅃 🄴 🅂" +[[ -z ${array[*]} ]] && echo "There are no updates available" && echo -e "\n" && return 0 || echo "Update(s) Available: ${#array[@]}" +echo "Asynchronous Updates: $update_limit" +[[ -z $timeout ]] && echo "Default Timeout: 500" && timeout=500 || echo "Custom Timeout: $timeout" +[[ "$timeout" -le 120 ]] && echo "Warning: Your timeout is set low and may lead to premature rollbacks or skips" +[[ $ignore_image_update == "true" ]] && echo "Image Updates: Disabled" || echo "Image Updates: Enabled" +pool=$(cli -c 'app kubernetes config' | grep -E "dataset\s\|" | awk -F '|' '{print $3}' | awk -F '/' '{print $1}' | tr -d " \t\n\r") + +index=0 +for app in "${array[@]}" +do + app_name=$(echo "$app" | awk -F ',' '{print $1}') #print out first catagory, name. + old_app_ver=$(echo "$app" | awk -F ',' '{print $4}' | awk -F '_' '{print $1}' | awk -F '.' '{print $1}') #previous/current Application MAJOR Version + new_app_ver=$(echo "$app" | awk -F ',' '{print $5}' | awk -F '_' '{print $1}' | awk -F '.' '{print $1}') #new Application MAJOR Version + old_chart_ver=$(echo "$app" | awk -F ',' '{print $4}' | awk -F '_' '{print $2}' | awk -F '.' '{print $1}') # Old Chart MAJOR version + new_chart_ver=$(echo "$app" | awk -F ',' '{print $5}' | awk -F '_' '{print $2}' | awk -F '.' '{print $1}') # New Chart MAJOR version + diff_app=$(diff <(echo "$old_app_ver") <(echo "$new_app_ver")) #caluclating difference in major app versions + diff_chart=$(diff <(echo "$old_chart_ver") <(echo "$new_chart_ver")) #caluclating difference in Chart versions + old_full_ver=$(echo "$app" | awk -F ',' '{print $4}') #Upgraded From + new_full_ver=$(echo "$app" | awk -F ',' '{print $5}') #Upraded To + + #Skip application if its on ignore list + if printf '%s\0' "${ignore[@]}" | grep -iFxqz "${app_name}" ; then + echo -e "\n$app_name\nIgnored, skipping" + unset "array[$index]" + #Skip appliaction if major update and not ignoreing major versions + elif [[ "$diff_app" != "$diff_chart" && $update_apps == "true" ]] ; then + echo -e "\n$app_name\nMajor Release, update manually" + unset "array[$index]" + # Skip update if application previously failed on this exact update version + elif grep -qs "^$app_name," failed 2>/dev/null; then + failed_ver=$(grep "^$app_name," failed | awk -F ',' '{print $2}') + if [[ "$failed_ver" == "$new_full_ver" ]] ; then + echo -e "\n$app_name\nSkipping previously failed version:\n$new_full_ver" + unset "array[$index]" + else + sed -i /"$app_name",/d failed + fi + #Skip Image updates if ignore image updates is set to true + elif [[ $old_full_ver == "$new_full_ver" && $ignore_image_update == "true" ]]; then + echo -e "\n$app_name\nImage update, skipping.." + unset "array[$index]" + fi + ((index++)) +done +array=("${array[@]}") +[[ ${#array[@]} == 0 ]] && echo && echo && return + + +index=0 +while_count=0 +rm deploying 2>/dev/null +rm finished 2>/dev/null +while [[ ${#processes[@]} != 0 || $(wc -l finished 2>/dev/null | awk '{ print $1 }') -lt "${#array[@]}" ]] +do + if while_status=$(cli -m csv -c 'app chart_release query name,update_available,human_version,human_latest_version,container_images_update_available,status' 2>/dev/null) ; then + ((while_count++)) + [[ -z $while_status ]] && continue || echo -e "$while_count\n$while_status" > all_app_status + mapfile -t deploying_check < <(grep ",DEPLOYING," all_app_status) + for i in "${deploying_check[@]}" + do + app_name=$(echo "$i" | awk -F ',' '{print $1}') + [[ ! -e deploying ]] && touch deploying + if ! grep -qs "$app_name,DEPLOYING" deploying; then + echo "$app_name,DEPLOYING" >> deploying + fi + done + else + echo "Middlewared timed out. Consider setting a lower number for async applications" + continue + fi + count=0 + for proc in "${processes[@]}" + do + kill -0 "$proc" &> /dev/null || unset "processes[$count]" + ((count++)) + done + processes=("${processes[@]}") + if [[ $index -lt ${#array[@]} && "${#processes[@]}" -lt "$update_limit" ]]; then + pre_process "${array[$index]}" & + processes+=($!) + ((index++)) + else + sleep 3 + fi +done +rm deploying 2>/dev/null +rm finished 2>/dev/null +echo +echo +} +export -f commander + + +pre_process(){ +app_name=$(echo "${array[$index]}" | awk -F ',' '{print $1}') #print out first catagory, name. +startstatus=$(echo "${array[$index]}" | awk -F ',' '{print $2}') #status of the app: STOPPED / DEPLOYING / ACTIVE +old_full_ver=$(echo "${array[$index]}" | awk -F ',' '{print $4}') #Upgraded From +new_full_ver=$(echo "${array[$index]}" | awk -F ',' '{print $5}') #Upraded To +rollback_version=$(echo "${array[$index]}" | awk -F ',' '{print $4}' | awk -F '_' '{print $2}') + + +# Check if app is external services, append outcome to external_services file +[[ ! -e external_services ]] && touch external_services +if ! grep -qs "^$app_name," external_services ; then + if ! grep -qs "/external-service" /mnt/"$pool"/ix-applications/releases/"$app_name"/charts/"$(find /mnt/"$pool"/ix-applications/releases/"$app_name"/charts/ -maxdepth 1 -type d -printf '%P\n' | sort -r | head -n 1)"/Chart.yaml; then + echo "$app_name,false" >> external_services + else + echo "$app_name,true" >> external_services + fi +fi + +# If application is deploying prior to updating, attempt to wait for it to finish +if [[ "$startstatus" == "DEPLOYING" ]]; then + SECONDS=0 + while [[ "$status" == "DEPLOYING" ]] + do + status=$(grep "^$app_name," all_app_status | awk -F ',' '{print $2}') + if [[ "$SECONDS" -ge "$timeout" ]]; then + echo_array+=("Application is stuck Deploying, Skipping to avoid damage") + echo_array + return + fi + sleep 5 + done +fi + +# If user is using -S, stop app prior to updating +echo_array+=("\n$app_name") +if [[ $stop_before_update == "true" && "$startstatus" != "STOPPED" ]]; then # Check to see if user is using -S or not + [[ "$verbose" == "true" ]] && echo_array+=("Stopping prior to update..") + if stop_app ; then + echo_array+=("Stopped") + else + echo_array+=("Error: Failed to stop $app_name") + echo_array + return 1 + fi +fi + +# Send app through update function +[[ "$verbose" == "true" ]] && echo_array+=("Updating..") +if update_app ;then + if [[ $old_full_ver == "$new_full_ver" ]]; then + echo_array+=("Updated Container Image") + else + echo_array+=("Updated\n$old_full_ver\n$new_full_ver") + fi +else + echo_array+=("Failed to update\nManual intervention may be required") + echo_array + return +fi + +# If app is external services, do not send for post processing +if grep -qs "^$app_name,true" external_services ; then + echo_array + return +# If app is container image update, dont send for post processing +elif [[ $old_full_ver == "$new_full_ver" ]]; then + echo_array + return +else + post_process +fi +} +export -f pre_process + + +post_process(){ +SECONDS=0 +count=0 +if [[ $rollback == "true" || "$startstatus" == "STOPPED" ]]; then + while true + do + + # If app reports ACTIVE right away, assume its a false positive and wait for it to change, or trust it after 5 updates to all_app_status + status=$(grep "^$app_name," all_app_status | awk -F ',' '{print $2}') + if [[ $count -lt 1 && $status == "ACTIVE" && "$(grep "^$app_name," deploying 2>/dev/null | awk -F ',' '{print $2}')" != "DEPLOYING" ]]; then # If status shows up as Active or Stopped on the first check, verify that. Otherwise it may be a false report.. + [[ "$verbose" == "true" ]] && echo_array+=("Verifying $status..") + before_loop=$(head -n 1 all_app_status) + current_loop=0 + until [[ "$status" != "ACTIVE" || $current_loop -gt 4 ]] # Wait for a specific change to app status, or 3 refreshes of the file to go by. + do + status=$(grep "^$app_name," all_app_status | awk -F ',' '{print $2}') + sleep 1 + if ! echo -e "$(head -n 1 all_app_status)" | grep -qs ^"$before_loop" ; then + before_loop=$(head -n 1 all_app_status) + ((current_loop++)) + fi + done + fi + (( count++ )) + + if [[ "$status" == "ACTIVE" ]]; then + if [[ "$startstatus" == "STOPPED" ]]; then + [[ "$verbose" == "true" ]] && echo_array+=("Returing to STOPPED state..") + if stop_app ; then + echo_array+=("Stopped") + else + echo_array+=("Error: Failed to stop $app_name") + echo_array + return 1 + fi + break + else + echo_array+=("Active") + break + fi + elif [[ "$SECONDS" -ge "$timeout" ]]; then + if [[ $rollback == "true" ]]; then + if [[ "$failed" != "true" ]]; then + echo "$app_name,$new_full_ver" >> failed + echo_array+=("Error: Run Time($SECONDS) for $app_name has exceeded Timeout($timeout)") + echo_array+=("If this is a slow starting application, set a higher timeout with -t") + echo_array+=("If this applicaion is always DEPLOYING, you can disable all probes under the Healthcheck Probes Liveness section in the edit configuration") + echo_array+=("Reverting update..") + if rollback_app ; then + echo_array+=("Rolled Back") + else + echo_array+=("Error: Failed to rollback $app_name\nAbandoning") + echo_array + return 1 + fi + failed="true" + SECONDS=0 + count=0 + continue #run back post_process function if the app was stopped prior to update + else + echo_array+=("Error: Run Time($SECONDS) for $app_name has exceeded Timeout($timeout)") + echo_array+=("The application failed to be ACTIVE even after a rollback") + echo_array+=("Manual intervention is required\nStopping, then Abandoning") + if stop_app ; then + echo_array+=("Stopped") + else + echo_array+=("Error: Failed to stop $app_name") + echo_array + return 1 + fi + break + fi + else + echo "$app_name,$new_full_ver" >> failed + echo_array+=("Error: Run Time($SECONDS) for $app_name has exceeded Timeout($timeout)") + echo_array+=("If this is a slow starting application, set a higher timeout with -t") + echo_array+=("If this applicaion is always DEPLOYING, you can disable all probes under the Healthcheck Probes Liveness section in the edit configuration") + echo_array+=("Manual intervention is required\nStopping, then Abandoning") + if stop_app ; then + echo_array+=("Stopped") + else + echo_array+=("Error: Failed to stop $app_name") + echo_array + return 1 + fi + break + fi + else + [[ "$verbose" == "true" ]] && echo_array+=("Waiting $((timeout-SECONDS)) more seconds for $app_name to be ACTIVE") + sleep 5 + continue + fi + done +fi +echo_array +} +export -f post_process + + +rollback_app(){ +count=0 +app_update_avail=$(grep "^$app_name," all_app_status | awk -F ',' '{print $3}') +while [[ $app_update_avail == "false" ]] +do + app_update_avail=$(grep "^$app_name," all_app_status | awk -F ',' '{print $3}') + if [[ $count -gt 2 ]]; then # If failed to rollback app 3 times, return failure to parent shell + return 1 + elif ! cli -c "app chart_release rollback release_name=\"$app_name\" rollback_options={\"item_version\": \"$rollback_version\"}" &> /dev/null ; then + before_loop=$(head -n 1 all_app_status) + ((count++)) + until [[ $(head -n 1 all_app_status) != "$before_loop" ]] # Upon failure, wait for status update before continuing + do + sleep 1 + done + else + break + fi +done +} + + +update_app(){ +current_loop=0 +while true +do + update_avail=$(grep "^$app_name," all_app_status | awk -F ',' '{print $3","$6}') + if [[ $update_avail =~ "true" ]]; then + if ! cli -c 'app chart_release upgrade release_name=''"'"$app_name"'"' &> /dev/null ; then + before_loop=$(head -n 1 all_app_status) + current_loop=0 + until [[ "$(grep "^$app_name," all_app_status | awk -F ',' '{print $3","$6}')" != "$update_avail" ]] # Wait for a specific change to app status, or 3 refreshes of the file to go by. + do + if [[ $current_loop -gt 2 ]]; then + cli -c 'app chart_release upgrade release_name=''"'"$app_name"'"' &> /dev/null || return 1 # After waiting, attempt an update once more, if fails, return error code + elif ! echo -e "$(head -n 1 all_app_status)" | grep -qs ^"$before_loop" ; then # The file has been updated, but nothing changed specifically for the app. + before_loop=$(head -n 1 all_app_status) + ((current_loop++)) + fi + sleep 1 + done + fi + break + elif [[ ! $update_avail =~ "true" ]]; then + break + else + sleep 3 + fi +done +} +export -f update_app + + +stop_app(){ +count=0 +while [[ "$status" != "STOPPED" ]] +do + status=$( grep "^$app_name," all_app_status | awk -F ',' '{print $2}') + if [[ $count -gt 2 ]]; then # If failed to stop app 3 times, return failure to parent shell + return 1 + elif ! cli -c 'app chart_release scale release_name='\""$app_name"\"\ 'scale_options={"replica_count": 0}' &> /dev/null ; then + before_loop=$(head -n 1 all_app_status) + ((count++)) + until [[ $(head -n 1 all_app_status) != "$before_loop" ]] # Upon failure, wait for status update before continuing + do + sleep 1 + done + else + break + fi +done +} +export -f stop_app + + +echo_array(){ +#Dump the echo_array, ensures all output is in a neat order. +for i in "${echo_array[@]}" +do + echo -e "$i" +done +final_check +} +export -f echo_array + + +final_check(){ + [[ ! -e finished ]] && touch finished + echo "$app_name,finished" >> finished +} diff --git a/truetool/includes/backup.sh b/truetool/includes/backup.sh new file mode 100644 index 00000000..9308664d --- /dev/null +++ b/truetool/includes/backup.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +## Simple shortcut to just list the backups without promts and such +listBackups(){ +echo -e "${BWhite}Backup Listing Tool${Color_Off}" +clear -x && echo "pulling all restore points.." +list_backups=$(cli -c 'app kubernetes list_backups' | grep -v system-update | sort -t '_' -Vr -k2,7 | tr -d " \t\r" | awk -F '|' '{print $2}' | nl | column -t) +[[ -z "$list_backups" ]] && echo -e "${IRed}No restore points available${Color_Off}" && exit || echo "Detected Backups:" && echo "$list_backups" +} +export -f listBackups diff --git a/truetool/includes/chores.sh b/truetool/includes/chores.sh new file mode 100644 index 00000000..c7b02f5a --- /dev/null +++ b/truetool/includes/chores.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +helmEnable(){ +echo -e "${BWhite}Enabling Helm${Color_Off}" +export KUBECONFIG=/etc/rancher/k3s/k3s.yaml && echo -e "${IGreen}Helm Enabled${Color_Off}"|| echo -e "${IRed}Helm Enable FAILED${Color_Off}" +} +export -f helmEnable + +aptEnable(){ +echo -e "${BWhite}Enabling Apt-Commands${Color_Off}" +chmod +x /usr/bin/apt* && echo -e "${IGreen}APT enabled${Color_Off}"|| echo -e "${IRed}APT Enable FAILED${Color_Off}" +} +export -f aptEnable + +kubeapiEnable(){ +local -r comment='iX Custom Rule to drop connection requests to k8s cluster from external sources' +echo -e "${BWhite}Enabling Kubernetes API${Color_Off}" +if iptables -t filter -L INPUT 2> /dev/null | grep -q "${comment}" ; then + iptables -D INPUT -p tcp -m tcp --dport 6443 -m comment --comment "${comment}" -j DROP && echo -e "${IGreen}Kubernetes API enabled${Color_Off}"|| echo -e "${IRed}Kubernetes API Enable FAILED${Color_Off}" +else + echo -e "${IGreen}Kubernetes API already enabled${Color_Off}" +fi +} +export -f kubeapiEnable + +# Prune unused docker images to prevent dataset/snapshot bloat related slowdowns on SCALE +prune(){ +echo -e "🄿 🅁 🅄 🄽 🄴" +if (( "$scaleVersion" >= 22120 )); then + cli -c 'app container config prune prune_options={"remove_unused_images": true, "remove_stopped_containers": true}' | head -n -4 || echo "Failed to Prune Docker Images" +else + docker image prune -af | grep "^Total" || echo "Failed to Prune Docker Images" +fi +} +export -f prune + +middlewareRestart() { + echo "We need to restart middlewared." + echo "This will cause a short downtime for the webui approximately 10-30 seconds" + echo "Restarting middlewared" + service middlewared restart & + wait $! + echo "Restarted middlewared" +} +export -f middlewareRestart + + +sync(){ +echo_sync+=("🅂 🅈 🄽 🄲") +cli -c 'app catalog sync_all' &> /dev/null && echo_sync+=("Catalog sync complete") + +#Dump the echo_array, ensures all output is in a neat order. +for i in "${echo_sync[@]}" +do + echo -e "$i" +done +echo +echo +} +export -f sync diff --git a/truetool/includes/help.sh b/truetool/includes/help.sh new file mode 100644 index 00000000..a31cd4fc --- /dev/null +++ b/truetool/includes/help.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +help(){ +[[ $help == "true" ]] && clear -x +echo "" +echo -e "${BWhite}Basic Utilities${Color_Off}" +echo "--mount | Initiates mounting feature, choose between unmounting and mounting PVC data" +echo "--restore | Opens a menu to restore a \"truetool\" backup that was taken on your \"ix-applications\" dataset" +echo "--delete-backup | Opens a menu to delete backups on your system" +echo "--list-backups | Prints a list of backups available" +echo "--helm-enable | Enables Helm command access on SCALE" +echo "--apt-enable | Enables Apt command access on SCALE" +echo "--kubeapi-enable | Enables external access to Kuberntes API port" +echo "--dns | List all of your applications DNS names and their web ports" +echo +echo -e "${BWhite}Update Options${Color_Off}" +echo "-U | Update all applications, ignores versions" +echo "-u | Update all applications, does not update Major releases" +echo "-b | Back-up your ix-applications dataset, specify a number after -b" +echo "-i | Add application to ignore list, one by one, see example below." +echo "-v | verbose output" +echo "-t | Set a custom timeout in seconds when checking if either an App or Mountpoint correctly Started, Stopped or (un)Mounted. Defaults to 500 seconds" +echo "-s | sync catalog" +echo "-p | Prune unused/old docker images" +echo +echo -e "${BWhite}Examples${Color_Off}" +echo "bash truetool.sh -b 14 -i portainer -i arch -i sonarr -i radarr -t 600 -vsUp" +echo "bash /mnt/tank/scripts/truetool.sh -t 150 --mount" +echo "bash /mnt/tank/scripts/truetool.sh --dns" +echo "bash /mnt/tank/scripts/truetool.sh --restore" +echo "bash /mnt/tank/scripts/truetool.sh --delete-backup" +echo +exit +} +export -f help diff --git a/truetool/includes/mount.sh b/truetool/includes/mount.sh new file mode 100644 index 00000000..7d5a6019 --- /dev/null +++ b/truetool/includes/mount.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +mountPVC(){ +echo -e "${BWhite}PVC Mounting Tool${Color_Off}" +clear -x +title +echo -e "1 Mount\n2 Unmount All" && read -rt 600 -p "Please type a number: " selection +[[ -z "$selection" ]] && echo "Your selection cannot be empty" && exit #Check for valid selection. If none, kill script +if [[ $selection == "1" ]]; then + list=$(k3s kubectl get pvc -A | sort -u | awk '{print NR-1, "\t" $1 "\t" $2 "\t" $4}' | column -t | sed "s/^0/ /") + echo "$list" && read -rt 120 -p "Please type a number: " selection + [[ -z "$selection" ]] && echo "Your selection cannot be empty" && exit #Check for valid selection. If none, kill script + app=$(echo -e "$list" | grep ^"$selection " | awk '{print $2}' | cut -c 4- ) + [[ -z "$app" ]] && echo "Invalid Selection: $selection, was not an option" && exit #Check for valid selection. If none, kill script + pvc=$(echo -e "$list" | grep ^"$selection ") + status=$(cli -m csv -c 'app chart_release query name,status' | grep -E "^$app\b" | awk -F ',' '{print $2}'| tr -d " \t\n\r") + if [[ "$status" != "STOPPED" ]]; then + [[ -z $timeout ]] && echo -e "\nDefault Timeout: 500" && timeout=500 || echo -e "\nCustom Timeout: $timeout" + SECONDS=0 && echo -e "\nScaling down $app" && midclt call chart.release.scale "$app" '{"replica_count": 0}' &> /dev/null + else + echo -e "\n$app is already stopped" + fi + while [[ "$SECONDS" -le "$timeout" && "$status" != "STOPPED" ]] + do + status=$(cli -m csv -c 'app chart_release query name,status' | grep -E "^$app\b" | awk -F ',' '{print $2}'| tr -d " \t\n\r") + echo -e "Waiting $((timeout-SECONDS)) more seconds for $app to be STOPPED" && sleep 5 + done + data_name=$(echo "$pvc" | awk '{print $3}') + volume_name=$(echo "$pvc" | awk '{print $4}') + full_path=$(zfs list | grep "$volume_name" | awk '{print $1}') + echo -e "\nMounting\n$full_path\nTo\n/mnt/truetool/$data_name" && zfs set mountpoint="/truetool/$data_name" "$full_path" && echo -e "Mounted, Use the Unmount All option to unmount\n" + exit +elif [[ $selection == "2" ]]; then + mapfile -t unmount_array < <(basename -a /mnt/truetool/* | sed "s/*//") + [[ -z ${unmount_array[*]} ]] && echo "Theres nothing to unmount" && exit + for i in "${unmount_array[@]}" + do + main=$(k3s kubectl get pvc -A | grep -E "\s$i\s" | awk '{print $1, $2, $4}') + app=$(echo "$main" | awk '{print $1}' | cut -c 4-) + pvc=$(echo "$main" | awk '{print $3}') + mapfile -t path < <(find /mnt/*/ix-applications/releases/"$app"/volumes/ -maxdepth 0 | cut -c 6-) + if [[ "${#path[@]}" -gt 1 ]]; then #if there is another app with the same name on another pool, use the current pools application, since the other instance is probably old, or unused. + echo "$i is a name used on more than one pool.. attempting to use your current kubernetes apps pool" + pool=$(cli -c 'app kubernetes config' | grep -E "dataset\s\|" | awk -F '|' '{print $3}' | awk -F '/' '{print $1}' | tr -d " \t\n\r") + full_path=$(find /mnt/"$pool"/ix-applications/releases/"$app"/volumes/ -maxdepth 0 | cut -c 6-) + zfs set mountpoint=legacy "$full_path""$pvc" && echo "$i unmounted" && rmdir /mnt/truetool/"$i" || echo "${IRed}FAILED${Color_Off} to unmount $i" + else + # shellcheck disable=SC2128 + zfs set mountpoint=legacy "$path""$pvc" && echo "$i unmounted" && rmdir /mnt/truetool/"$i" || echo "${IRed}FAILED${Color_Off} to unmount $i" + fi + done + rmdir /mnt/truetool +else + echo -e "${IRed}Invalid selection, \"$selection\" was not an option${Color_Off}" +fi +} +export -f mountPVC diff --git a/truetool/includes/no_args.sh b/truetool/includes/no_args.sh new file mode 100644 index 00000000..9ff0f3c0 --- /dev/null +++ b/truetool/includes/no_args.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# shellcheck disable=SC2034 + +no_args(){ + echo "0 Show Help" + echo "1 List Internal Service DNS Names" + echo "2 Mount and Unmount PVC storage for easy access" + echo "3 List Backups" + echo "4 Create a Backup" + echo "5 Restore a Backup" + echo "6 Delete a Backup" + echo "7 Enable Helm Commands" + echo "8 Enable Apt and Apt-Get Commands" + echo "9 Update All Apps" + echo "10 Enable external access to Kuberntes API port" + read -rt 600 -p "Please select an option by number: " selection + + case $selection in + 0) + help="true" + ;; + 1) + dns="true" + ;; + 2) + mountPVC="true" + ;; + 3) + listBackups="true" + ;; + 4) + read -rt 600 -p "Please type the max number of backups to keep: " backups + re='^[0-9]+$' + number_of_backups=$backups + ! [[ $backups =~ $re ]] && echo -e "Error: -b needs to be assigned an interger\n\"""$number_of_backups""\" is not an interger" >&2 && exit + [[ "$number_of_backups" -le 0 ]] && echo "Error: Number of backups is required to be at least 1" && exit + ;; + 5) + restore="true" + ;; + 6) + deleteBackup="true" + ;; + 7) + helmEnable="true" + ;; + 8) + aptEnable="true" + ;; + 9) + echo "" + echo "1 Update Apps Excluding likely breaking major changes" + echo "2 Update Apps Including likely breaking major changes" + read -rt 600 -p "Please select an option by number: " updateType + if [[ "$updateType" == "1" ]]; then + update_apps="true" + elif [[ "$updateType" == "2" ]]; then + update_all_apps="true" + else + echo "INVALID ENTRY" && exit 1 + fi + ;; + 10) + kubeapiEnable="true" + ;; + *) + echo "Unknown option" && exit 1 + ;; + esac + echo "" +} +export -f no_args diff --git a/truetool/includes/patch.sh b/truetool/includes/patch.sh new file mode 100644 index 00000000..600a2aa3 --- /dev/null +++ b/truetool/includes/patch.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +patchv22120(){ +echo "Applying 22.12 HotPatch 1" + +( wget -q -P /tmp https://github.com/truecharts/truetool/raw/main/patch/2212/HP1.patch && echo "download completed" || echo "download failed" ) && ( patch -N -s -p0 -d /usr/lib/python3/dist-packages/middlewared/ &>/dev/null < /tmp/HP1.patch && echo "patch completed" || echo "Patch Already Applied" ) && rm -rf /tmp/HP1.patch + +echo "Applying 22.12 HotPatch 2" +( wget -q -P /tmp https://github.com/truecharts/truetool/raw/main/patch/2212/HP2.patch && echo "download completed" || echo "download failed" ) && ( patch -N -s -p0 -d /usr/lib/python3/dist-packages/middlewared/ &>/dev/null < /tmp/HP2.patch && echo "patch completed" && restartmiddleware=yes || echo "Patch Already Applied" ) && rm -rf /tmp/HP2.patch +} +export -f patchv22120 + + + + +hotpatch(){ +echo "Starting hotpatcher..." +restartmiddleware=no +if (( "$scaleVersion" == 22120 )); then + patchv22120 +else + echo "No hotpatch available for your version, congratulations!" +fi + +if [[ "$restartmiddleware" == "yes" ]]; then + middlewareRestart +fi +restartmiddleware=no +} +export -f hotpatch diff --git a/truetool/includes/title.sh b/truetool/includes/title.sh new file mode 100644 index 00000000..b6c9e740 --- /dev/null +++ b/truetool/includes/title.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Fancy ascii title. +title(){ +if [[ -z $titleShown ]]; then + echo -e "${IRed} _______ _____ _ _ "; + echo " |__ __| / ____| | | | "; + echo " | |_ __ _ _ ___| | | |__ __ _ _ __| |_ ___ "; + echo -e "${IYellow} | | '__| | | |/ _ \ | | '_ \ / _\` | '__| __/ __|"; + echo " | | | | |_| | __/ |____| | | | (_| | | | |_\__ \\"; + echo -e "${IGreen} __|_|_| \__,_|\___|\_____|_| |_|\__,_|_| \__|___/"; + echo " |__ __| |__ __| | | "; + echo -e "${IBlue} | |_ __ _ _ ___| | ___ ___ | | "; + echo " | | '__| | | |/ _ \ |/ _ \ / _ \| | "; + echo -e "${IPurple} | | | | |_| | __/ | (_) | (_) | | "; + echo " |_|_| \__,_|\___|_|\___/ \___/|_| "; + echo " "; + echo -e "${Color_Off} "; +fi +titleShown='true' +} +export -f title diff --git a/truetool/includes/update_self.sh b/truetool/includes/update_self.sh new file mode 100644 index 00000000..7ade24e2 --- /dev/null +++ b/truetool/includes/update_self.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +## AutoUpdate TrueTool using Git +updater(){ +echo -e "${BWhite}Checking for updates...${Color_Off}" +git remote set-url origin "${targetRepo}" +BRANCH=$(git rev-parse --abbrev-ref HEAD) +git fetch -q +git update-index -q --refresh +if [[ $(git status --branch --porcelain) == *"behind"* ]]; then + echo -e "${IPurple}TrueTool requires update${Color_Off}" + git reset --hard -q + git checkout -q "${BRANCH}" + git pull -q + echo "script updated" + if [[ "$CHANGED" == "true" ]]; then + echo "LOOP DETECTED, exiting" + exit 1 + else + echo "restarting script after update..." + export CHANGED="true" + . "${SCRIPT_DIR}/truetool.sh" "$@" + exit + fi +else + echo -e "${IGreen}script up-to-date${Color_Off}" + export CHANGED="false" +fi +echo "" +} +export -f updater diff --git a/truetool/patch/2212/HP1.patch b/truetool/patch/2212/HP1.patch new file mode 100644 index 00000000..72e293a9 --- /dev/null +++ b/truetool/patch/2212/HP1.patch @@ -0,0 +1,125 @@ +diff --git plugins/chart_releases_linux/chart_release.py plugins/chart_releases_linux/chart_release.py +index 76e3825bc0f..f65cc0eac24 100644 +--- plugins/chart_releases_linux/chart_release.py ++++ plugins/chart_releases_linux/chart_release.py +@@ -606,7 +606,7 @@ async def do_delete(self, job, release_name, options): + # If we had pre-install jobs, it's possible we have leftover pods which the job did not remove + # based on dev specified settings of cleaning it up - let's remove those + for pod in await self.middleware.call('k8s.pod.query', [['metadata.namespace', '=', namespace]]): +- owner_references = pod['metadata'].get('owner_references') ++ owner_references = pod['metadata'].get('ownerReferences') + if not isinstance(owner_references, list) or all( + owner_reference.get('name') not in pre_install_jobs for owner_reference in owner_references + ): +@@ -658,7 +658,7 @@ async def remove_storage_class_and_dataset(self, release_name, job=None): + pvc_volume_ds = os.path.join(release_ds, 'volumes') + for pv in await self.middleware.call( + 'k8s.pv.query', [ +- ['spec.csi.volume_attributes.openebs\\.io/poolname', '=', pvc_volume_ds] ++ ['spec.csi.volumeAttributes.openebs\\.io/poolname', '=', pvc_volume_ds] + ] + ): + await self.middleware.call('k8s.pv.delete', pv['metadata']['name']) +diff --git plugins/chart_releases_linux/resources.py plugins/chart_releases_linux/resources.py +index c7180147a5f..941de79da45 100644 +--- plugins/chart_releases_linux/resources.py ++++ plugins/chart_releases_linux/resources.py +@@ -158,13 +158,13 @@ async def retrieve_pv_pvc_mapping_internal(self, chart_release): + } + + for pv in chart_release['resources']['persistent_volumes']: +- claim_name = pv['spec'].get('claim_ref', {}).get('name') ++ claim_name = pv['spec'].get('claimRef', {}).get('name') + if claim_name: + csi_spec = pv['spec']['csi'] +- volumes_ds = csi_spec['volume_attributes']['openebs.io/poolname'] ++ volumes_ds = csi_spec['volumeAttributes']['openebs.io/poolname'] + if ( + os.path.join(chart_release['dataset'], 'volumes') != volumes_ds or +- csi_spec['volume_handle'] not in zfs_volumes ++ csi_spec['volumeHandle'] not in zfs_volumes + ): + # We are only going to backup/restore pvc's which were consuming + # their respective storage class and we have related zfs volume present +@@ -174,8 +174,8 @@ async def retrieve_pv_pvc_mapping_internal(self, chart_release): + mapping[claim_name] = { + 'name': pv_name, + 'pv_details': pv, +- 'dataset': os.path.join(volumes_ds, csi_spec['volume_handle']), +- 'zv_details': zfs_volumes[csi_spec['volume_handle']], ++ 'dataset': os.path.join(volumes_ds, csi_spec['volumeHandle']), ++ 'zv_details': zfs_volumes[csi_spec['volumeHandle']], + } + return mapping + +@@ -247,11 +247,11 @@ async def get_workload_storage_details(self): + # because of chart release reclaim policy being retain + for pv in await self.middleware.call( + 'k8s.pv.query', [[ +- 'spec.csi.volume_attributes.openebs\\.io/poolname', '^', ++ 'spec.csi.volumeAttributes.openebs\\.io/poolname', '^', + f'{os.path.join(k8s_config["dataset"], "releases")}/' + ]] + ): +- dataset = pv['spec']['csi']['volume_attributes']['openebs.io/poolname'] ++ dataset = pv['spec']['csi']['volumeAttributes']['openebs.io/poolname'] + rl = dataset.split('/', 4) + if len(rl) > 4: + mapping['persistent_volumes'][rl[3]].append(pv) +diff --git plugins/chart_releases_linux/scale_workload.py plugins/chart_releases_linux/scale_workload.py +index 117dab3a79c..e9525150278 100644 +--- plugins/chart_releases_linux/scale_workload.py ++++ plugins/chart_releases_linux/scale_workload.py +@@ -246,10 +246,10 @@ async def get_workload_to_pod_mapping(self, namespace): + for r in await self.middleware.call( + f'k8s.{key}.query', [ + ['metadata.namespace', '=', namespace], +- ['metadata', 'rin', 'owner_references'], ++ ['metadata', 'rin', 'ownerReferences'], + ], {'select': ['metadata']} + ): +- for owner_reference in filter(lambda o: o.get('uid'), r['metadata']['owner_references'] or []): ++ for owner_reference in filter(lambda o: o.get('uid'), r['metadata']['ownerReferences'] or []): + mapping[key][owner_reference['uid']][r['metadata']['uid']] = r + + pod_mapping = defaultdict(list) +diff --git plugins/kubernetes_linux/restore.py plugins/kubernetes_linux/restore.py +index 4897e3f8b7a..ec13a332b6e 100644 +--- plugins/kubernetes_linux/restore.py ++++ plugins/kubernetes_linux/restore.py +@@ -218,7 +218,11 @@ def restore_backup(self, job, backup_name, options): + failed_pv_restores.append(f'Unable to create ZFS Volume for {pvc!r} PVC: {e}') + continue + ++ # We need to safely access claim_ref vollume attribute keys as with k8s client api re-write ++ # camel casing which was done by kubernetes asyncio package is not happening anymore + pv_spec = pv['pv_details']['spec'] ++ claim_ref = pv_spec.get('claim_ref') or pv_spec['claimRef'] ++ pv_volume_attrs = pv_spec['csi'].get('volume_attributes') or pv_spec['csi']['volumeAttributes'] + try: + self.middleware.call_sync('k8s.pv.create', { + 'metadata': { +@@ -229,18 +233,18 @@ def restore_backup(self, job, backup_name, options): + 'storage': pv_spec['capacity']['storage'], + }, + 'claimRef': { +- 'name': pv_spec['claim_ref']['name'], +- 'namespace': pv_spec['claim_ref']['namespace'], ++ 'name': claim_ref['name'], ++ 'namespace': claim_ref['namespace'], + }, + 'csi': { + 'volumeAttributes': { + 'openebs.io/poolname': RE_POOL.sub( +- f'{k8s_pool}\\1', pv_spec['csi']['volume_attributes']['openebs.io/poolname'] ++ f'{k8s_pool}\\1', pv_volume_attrs['openebs.io/poolname'] + ) + }, +- 'volumeHandle': pv_spec['csi']['volume_handle'], ++ 'volumeHandle': pv_spec['csi'].get('volume_handle') or pv_spec['csi']['volumeHandle'], + }, +- 'storageClassName': pv_spec['storage_class_name'], ++ 'storageClassName': pv_spec.get('storage_class_name') or pv_spec['storageClassName'], + }, + }) + except Exception as e: diff --git a/truetool/patch/2212/HP2.patch b/truetool/patch/2212/HP2.patch new file mode 100644 index 00000000..30357c93 --- /dev/null +++ b/truetool/patch/2212/HP2.patch @@ -0,0 +1,14 @@ +diff --git plugins/kubernetes_linux/backup.py plugins/kubernetes_linux/backup.py +index 365cd1718b4..1046a64c2a5 100644 +index d8a48d45f89..365cd1718b4 100644 +--- plugins/kubernetes_linux/backup.py ++++ plugins/kubernetes_linux/backup.py +@@ -61,7 +61,8 @@ def backup_chart_releases(self, job, backup_name): + ['metadata.namespace', '=', chart_release['namespace']] + ] + ) +- for secret in sorted(secrets, key=lambda d: d['metadata']['name']): ++ # We ignore this keeping in line with helm behaviour where the secret malformed is ignored by helm ++ for secret in sorted(filter(lambda d: d.get('data'), secrets), key=lambda d: d['metadata']['name']): + with open(os.path.join(secrets_dir, secret['metadata']['name']), 'w') as f: + f.write(self.middleware.call_sync('k8s.secret.export_to_yaml_internal', secret)) diff --git a/truetool/truetool.sh b/truetool/truetool.sh new file mode 100644 index 00000000..977f3d48 --- /dev/null +++ b/truetool/truetool.sh @@ -0,0 +1,200 @@ +#!/bin/bash + +# Constants +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )"; +dir=$(basename "$SCRIPT_DIR") + +# Change this if you want to fork the project +enableUpdate="true" +targetRepo="https://github.com/truecharts/truetool.git" + +# CD to the folder containing the script to ensure consistent runs +cd "${SCRIPT_DIR}" || echo -e "ERROR: Something went wrong accessing the script directory" + +# Includes +# shellcheck source=includes/chores.sh +source includes/chores.sh +# shellcheck source=includes/help.sh +source includes/help.sh +# shellcheck source=includes/help.sh +source includes/patch.sh +# shellcheck source=includes/no_args.sh +source includes/no_args.sh +# shellcheck source=includes/title.sh +source includes/title.sh +# shellcheck source=includes/update_self.sh +source includes/update_self.sh +# shellcheck source=includes/backup.sh +source includes/backup.sh + +# Libraries loaded from Heavyscript +# shellcheck source=functions/dns.sh +source functions/dns.sh +# shellcheck source=functions/mount.sh +source functions/mount.sh +# shellcheck source=functions/backup.sh +source functions/backup.sh +# shellcheck source=functions/update_apps.sh +source functions/update_apps.sh + + +#If no argument is passed, set flag to show menu +if [[ -z "$*" || "-" == "$*" || "--" == "$*" ]]; then + no_args="true" +else + + # Parse script options + while getopts ":si:b:t:uUpSv-:" opt + do + case $opt in + -) + case "${OPTARG}" in + help) + help="true" + ;; + dns) + dns="true" + ;; + mount) + mountPVC="true" + ;; + restore) + restore="true" + ;; + delete-backup) + deleteBackup="true" + ;; + list-backups) + listBackups="true" + ;; + helm-enable) + helmEnable="true" + ;; + apt-enable) + aptEnable="true" + ;; + kubeapi-enable) + kubeapiEnable="true" + ;; + no-color) + echo "Colors are removed, so the no-color option is deprecated. Please stop using this" + ;; + *) + echo -e "Invalid Option \"--$OPTARG\"\n" && help + exit + ;; + esac + ;; + \?) + echo -e "Invalid Option \"-$OPTARG\"\n" && help + exit + ;; + :) + echo -e "Option: \"-$OPTARG\" requires an argument\n" && help + exit + ;; + b) + re='^[0-9]+$' + number_of_backups=$OPTARG + ! [[ $OPTARG =~ $re ]] && echo -e "Error: -b needs to be assigned an interger\n\"""$number_of_backups""\" is not an interger" >&2 && exit + [[ "$number_of_backups" -le 0 ]] && echo "Error: Number of backups is required to be at least 1" && exit + ;; + i) + ignore+=("$OPTARG") + ;; + t) + re='^[0-9]+$' + timeout=$OPTARG + ! [[ $timeout =~ $re ]] && echo -e "Error: -t needs to be assigned an interger\n\"""$timeout""\" is not an interger" >&2 && exit + ;; + s) + sync="true" + ;; + U) + update_all_apps="true" + ;; + u) + update_apps="true" + ;; + p) + prune="true" + ;; + v) + verbose="true" + ;; + *) + echo -e "Invalid Option \"--$OPTARG\"\n" && help + exit + ;; + esac + done +fi + +title + +[[ "$enableUpdate" == "true" ]] && updater "$@" + +scaleVersion=$(cli -c 'system version' | awk -F '-' '{print $3}' | awk -F '.' '{print $1 $2 $3}' | tr -d " \t\r\.") +update_limit=$(nproc --all) +rollback="true" + +## Always check if a hotpatch needs to be applied +hotpatch + +# Show menu if menu flag is set +if [[ "$no_args" == "true" ]]; then + no_args +fi + +## Exit if incompatable functions are called +[[ "$update_all_apps" == "true" && "$update_apps" == "true" ]] && echo -e "-U and -u cannot BOTH be called" && exit + +## Exit if unsafe combinations are used +# Restore and update right after eachother, might cause super weird issues tha are hard to bugtrace +[[ ( "$update_all_apps" == "true" || "$update_apps" == "true" ) && ( "$restore" == "true" ) ]] && echo -e "Update and Restore cannot both be done in the same run..." && exit + +# Backup Deletion is generally considered to be a "once in a while" thing and not great to sync with automated updates for that reason +[[ ( "$update_all_apps" == "true" || "$update_apps" == "true" ) && ( "$deleteBackup" == "true" ) ]] && echo -e "Update Backup-Deletion cannot both be done in the same run..." && exit + +# Backup Deletion is generally considered to be a "once in a while" thing and not great to sync with automated updates for that reason +[[ ( "$update_all_apps" == "true" || "$update_apps" == "true" ) && ( "$deleteBackup" == "true" ) ]] && echo -e "Update and Backup-Deletion cannot both be done in the same run..." && exit + +# Backup listing is a printout, which would either clutter the output or be already outdated when combined with backup +[[ ( "$update_all_apps" == "true" || "$update_apps" == "true" ) && ( "$listBackups" == "true" ) ]] && echo -e "Update and Listing Backups cannot both be done in the same run..." && exit + +# Backup backup would be done after a backup is restored, which would lead to a backup that is... the same as the one restored... +[[ ( "$restore" == "true" && "$number_of_backups" -ge 1 )]] && echo -e "Restoring a backup and making a backup cannot both be done in the same run..." && exit + +# While technically possible, this is asking for user error... where a user by habit mistakes one prompt, for the other. +[[ ( "$restore" == "true" && "$deleteBackup" == "true" )]] && echo -e "restoring a backup and deleting a backup cannot both be done in the same run..." && exit + + +# Continue to call functions in specific order +[[ "$help" == "true" ]] && help +[[ "$helmEnable" == "true" ]] && helmEnable +[[ "$aptEnable" == "true" ]] && aptEnable +[[ "$kubeapiEnable" == "true" ]] && kubeapiEnable +[[ "$aptEnable" == "true" || "$helmEnable" == "true" || "$kubeapiEnable" == "true" ]] && exit +[[ "$listBackups" == "true" ]] && listBackups && exit +[[ "$deleteBackup" == "true" ]] && deleteBackup && exit +[[ "$dns" == "true" ]] && dns && exit +[[ "$restore" == "true" ]] && restore && exit +[[ "$mountPVC" == "true" ]] && mount && exit +if [[ "$number_of_backups" -gt 1 && "$sync" == "true" ]]; then # Run backup and sync at the same time + echo "Running Apps Backup & Syncing Catalog" + if [[ "$prune" == "true" ]]; then + prune & + fi + backup & + sync & + wait +elif [[ "$number_of_backups" -gt 1 && -z "$sync" ]]; then # If only backup is true, run it + echo "Running Apps Backup" + backup +elif [[ "$sync" == "true" && -z "$number_of_backups" ]]; then # If only sync is true, run it + echo "Syncing Catalog" + echo -e "Syncing Catalog(s)\n\n" + sync +fi +[[ "$update_all_apps" == "true" || "$update_apps" == "true" ]] && commander +[[ "$prune" == "true" ]] && prune