kizniche / Mycodo

An environmental monitoring and regulation system
http://kylegabriel.com/projects/
GNU General Public License v3.0
2.94k stars 494 forks source link

Improving the upgrade method #154

Closed kizniche closed 7 years ago

kizniche commented 7 years ago

Currently, upgrades are performed through the web UI under Admin/Upgrade, and will perform a git reset to bring the local repository to the master HEAD:

git fetch
git reset --hard origin/master

This necessitates the use of git branches to avoid committing to master and exposing users to the ability to upgrade. Additionally, there may come a time that the developers would like to push a commit to master but not want the master available to be upgraded to.

One solution I thought of is to use releases as a way to upgrade the system. When a new release that has a version number above the currently-installed version, an upgrade can be performed. I wrote a script, test_update_version_info.py, that will collect all current Mycodo releases named "vX.X.X", where the Xs are the version's major, minor, and patch numbers. The script will filter all other names out, then sort the versions to find the highest. Then, the tar archive URL is presented for the latest release version matching the "vX.X.X" format.

I was thinking the prefix to the version number can be used to denote different types of releases, in case there were a future option in Mycodo to be able to jump to an experimental upgrade track that allowed users to upgrade to pre-stable releases.

kizniche commented 7 years ago

I've updated the script to distinguish the latest major version (i.e. 4 in v4.X.X) in the list of releases. This can be useful to prevent unrestricted upgrading to the latest version. For instance, if a user has v4.0.26 installed, and versions 4.0.27, and 5.0.0 have been released since that install, then the user would only be able to upgrade the the latest v4 release. This prevents upgrading to a major version that may not have backward-compatibility with v4 dependencies, databases, etc.

After adding test releases and running, here is the output:

kiz@tango:mycodo/scripts$ ./test_new_upgrade_method.py
List of all Mycodo Releases:
v5.0.0
v4.0.27
v4.0.26

Latest Version: v5.0.0
Tar URL: https://api.github.com/repos/kizniche/Mycodo/tarball/v5.0.0

Latest v4 Version: v4.0.27
Tar URL: https://api.github.com/repos/kizniche/Mycodo/tarball/v4.0.27
kizniche commented 7 years ago

I've been experimenting with a new upgrade script. It replaces the Mycodo install with the latest release, moves camera folders and databases, then upgrades the database and restarts the web UI and daemon. Unique about it is a two-part system. It will create a script that will execute after the first half of the script has finished executing. This new script will be separate from the Mycodo install and allows all the files in the Mycodo install directory to be safely moved, without having to worry about what happens to an executing script when it tries to move itself.

I've tested it, and it works, but I'm not sure if I covered enough bases to ensure an error doesn't break the system. I added a reversion method that will restore the old version if there's an error in the part of the crucial moving process. I've also tested this to work.

Because this new method doesn't rely on cloning the git repository, commit hashes will need to be phased out of the system (particularly on the update page that exclusively relies on commit hashes at the moment).

kizniche commented 7 years ago

I forgot to reference this issue again, but the most recent commit 4f8d56a introduces the new upgrade system to the mycodo_dev branch. It allows upgrading only to releases with the tag "v4.X.X" where Y and Z denote the minor and patch version numbers, respectively. I've tested it to work, but I'll continue to test to try and find issues.

Edit: I've created a temporary release, v4.1.0 (which is actually just a copy of release v4.0.26), to test the new upgrade system before release to the public.

kizniche commented 7 years ago

I created a new repo to test the new upgrade system, and after about 20 upgrade tests, I believe it's at a point that works. I already deleted the repo, but all the fixes have been pushed to the mycodo_dev branch.

It essentially works like this:

  1. When the user visits the Upgrade page of the web UI, Flask will query github for any tags with the format "vX.Y.Z" representing version numbers.
  2. Sort tag versions, and filter based on the major version number ("X" above). For instance, if we're currently on version v4.x.x, this enables only version 4 upgrades to be displayed and able to be upgraded to. This enables any non-backwards-compatible upgrades to move to v5, preventing users from upgrading automatically from version v4 to v5., Note: there can still be a system implemented in the last version of the v4 track that adds instructions to the upgrade page that will notify users how to safely upgrade to v5.
  3. If a higher version exists in the filtered list, the user will be prompted with the ability to use the Upgrade button and begin the upgrade process.
  4. The upgrade process begins by stopping the daemon.
  5. Download the latest release tarball and extract into a temp directory.
  6. Databases, statistics ID file, and ssl certificates are copied from the current install to the temp directory.
  7. Databases are upgraded with alembic in the temp directory.
  8. Camera images (still, timelapse, videos) are moved from the current install to the temp directory.
  9. The current install is moved to /var/Mycodo-backups/ and named appropriately with the date/time of the move and the current version number (can be used in the future for identifying a backup for restoring).
  10. The temp directory is moved to where the previous install was.
  11. Permissions are set, systemctl is updated with the new files (actually symlinks, but prevents minor errors about file content changes occurring).
  12. The daemon is started and the web interface reinitialized.

Also, there is a recovery function built into the upgrade script that will rollback any changes if there are any errors encountered in the process of the upgrade during any critical parts (the moving of the install directories). Any errors that prevent the upgrade are logged and will be indicated on the upgrade page that an error occurred during the upgrade and will prevent the user from attempting the upgrade again (will remove the upgrade button) until the user physically deletes the ~/Mycodo/.upgrade file that indicates an error occurred. This page will also instruct the user where to find the upgrade log to find what error caused the issue.

I think this method is better than the current one for a couple reasons:

kizniche commented 7 years ago

I'm leaning toward merging the mycodo_dev branch with the new upgrade system. I'm fairly confident it will work. However, if not, reinstalling Mycodo wouldn't be too difficult. I'm just wondering if anyone has any objections or issues I may not have considered.