mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	We use `git describe --tags` to get information about the number of commit since the last major version, and the sha of the current HEAD. This is added to the ZULIP_VERSION when a deploy is done from `git`. Modified heavily by punchagan to: * to use git describe instead of `git log` and `wc` * use a separate script to run the git describe command * write the file with version info to var/ and remove it from the repo Fixes #4685.
		
			
				
	
	
		
			201 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python3
 | 
						|
#
 | 
						|
# This script contains the actual logic for upgrading from an old
 | 
						|
# version of Zulip to the new version.  upgrade-zulip-stage-2 is
 | 
						|
# always run from the new version of Zulip, so any bug fixes take
 | 
						|
# effect on the very next upgrade.
 | 
						|
import argparse
 | 
						|
import configparser
 | 
						|
import hashlib
 | 
						|
import subprocess
 | 
						|
import os
 | 
						|
import sys
 | 
						|
import logging
 | 
						|
import time
 | 
						|
 | 
						|
os.environ["PYTHONUNBUFFERED"] = "y"
 | 
						|
 | 
						|
# Force a known locale.  Some packages on PyPI fail to install in some locales.
 | 
						|
os.environ["LC_ALL"] = "en_US.UTF-8"
 | 
						|
os.environ["LANG"] = "en_US.UTF-8"
 | 
						|
os.environ["LANGUAGE"] = "en_US.UTF-8"
 | 
						|
 | 
						|
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
 | 
						|
from scripts.lib.zulip_tools import DEPLOYMENTS_DIR, su_to_zulip, assert_running_as_root
 | 
						|
 | 
						|
assert_running_as_root()
 | 
						|
 | 
						|
logging.Formatter.converter = time.gmtime
 | 
						|
logging.basicConfig(format="%(asctime)s upgrade-zulip-stage-2: %(message)s",
 | 
						|
                    level=logging.INFO)
 | 
						|
 | 
						|
# make sure we have appropriate file permissions
 | 
						|
os.umask(0o22)
 | 
						|
 | 
						|
parser = argparse.ArgumentParser()
 | 
						|
parser.add_argument("deploy_path", metavar="deploy_path",
 | 
						|
                    help="Path to deployment directory")
 | 
						|
parser.add_argument("--skip-puppet", dest="skip_puppet", action='store_true',
 | 
						|
                    help="Skip doing puppet/apt upgrades.")
 | 
						|
parser.add_argument("--skip-migrations", dest="skip_migrations", action='store_true',
 | 
						|
                    help="Skip doing migrations.")
 | 
						|
parser.add_argument("--from-git", dest="from_git", action='store_true',
 | 
						|
                    help="Upgrading from git, so run update-prod-static.")
 | 
						|
parser.add_argument("--ignore-static-assets", dest="ignore_static_assets", action='store_true',
 | 
						|
                    help="Do not attempt to copy/manage static assets.")
 | 
						|
parser.add_argument("--skip-purge-old-deployments", dest="skip_purge_old_deployments",
 | 
						|
                    action="store_true", help="Skip purging old deployments.")
 | 
						|
args = parser.parse_args()
 | 
						|
 | 
						|
deploy_path = args.deploy_path
 | 
						|
os.chdir(deploy_path)
 | 
						|
 | 
						|
config_file = configparser.RawConfigParser()
 | 
						|
config_file.read("/etc/zulip/zulip.conf")
 | 
						|
try:
 | 
						|
    tornado_processes = int(config_file.get('application_server', 'tornado_processes'))
 | 
						|
except (configparser.NoSectionError, configparser.NoOptionError):
 | 
						|
    tornado_processes = 1
 | 
						|
 | 
						|
# Handle issues around upstart on Ubuntu Xenial
 | 
						|
subprocess.check_call(["./scripts/lib/check-upstart"])
 | 
						|
 | 
						|
if not args.skip_puppet:
 | 
						|
    logging.info("Upgrading system packages...")
 | 
						|
    subprocess.check_call(["apt-get", "update"])
 | 
						|
    subprocess.check_call(["apt-get", "-y", "upgrade"])
 | 
						|
 | 
						|
if not os.path.exists((os.path.join(deploy_path, "zproject/prod_settings.py"))):
 | 
						|
    # This is normally done in unpack-zulip, but for upgrading from
 | 
						|
    # zulip<1.4.0, we need to do it.  See discussion in commit 586b23637.
 | 
						|
    os.symlink("/etc/zulip/settings.py",
 | 
						|
               os.path.join(deploy_path, "zproject/prod_settings.py"))
 | 
						|
 | 
						|
# Now we should have an environment setup where we can run our tools;
 | 
						|
# first, creating the production venv.
 | 
						|
subprocess.check_call([os.path.join(deploy_path, "scripts", "lib", "create-production-venv"),
 | 
						|
                       deploy_path])
 | 
						|
 | 
						|
# Setup the thumbor venv
 | 
						|
subprocess.check_call([os.path.join(deploy_path, "scripts", "lib", "create-thumbor-venv"),
 | 
						|
                       deploy_path])
 | 
						|
 | 
						|
# Make sure the right version of node is installed
 | 
						|
subprocess.check_call([os.path.join(deploy_path, "scripts", "lib", "install-node"),
 | 
						|
                       deploy_path])
 | 
						|
 | 
						|
# Generate any new secrets that were added in the new version required.
 | 
						|
# TODO: Do caching to only run this when it has changed.
 | 
						|
subprocess.check_call([os.path.join(deploy_path, "scripts", "setup", "generate_secrets.py"),
 | 
						|
                       "--production"])
 | 
						|
 | 
						|
# Unpleasant migration: Remove any legacy deployed copies of
 | 
						|
# images-google-64 from before we renamed that emojiset to
 | 
						|
# "googleblob":
 | 
						|
emoji_path = "/home/zulip/prod-static/generated/emoji/images-google-64/1f32d.png"
 | 
						|
if os.path.exists(emoji_path):
 | 
						|
    with open(emoji_path, "rb") as f:
 | 
						|
        emoji_data = f.read()
 | 
						|
    emoji_sha = hashlib.sha1(emoji_data).hexdigest()
 | 
						|
    if emoji_sha == "47033121dc20b376e0f86f4916969872ad22a293":
 | 
						|
        import shutil
 | 
						|
        shutil.rmtree("/home/zulip/prod-static/generated/emoji/images-google-64")
 | 
						|
 | 
						|
# And then, building/installing the static assets.
 | 
						|
if args.ignore_static_assets:
 | 
						|
    # For the OS version upgrade use case, the static assets are
 | 
						|
    # already in place, and we don't need to do anything.  Further,
 | 
						|
    # neither of the options below will work for all installations,
 | 
						|
    # because if we installed from Git, `prod-static/serve` may be
 | 
						|
    # empty so we can't do the non-Git thing, whereas if we installed
 | 
						|
    # from a tarball, we won't have a `tools/` directory and thus
 | 
						|
    # cannot run `tools/update-prod-static`.
 | 
						|
    pass
 | 
						|
elif args.from_git:
 | 
						|
    # Note: The fact that this is before we apply puppet changes means
 | 
						|
    # that we don't support adding new puppet dependencies of
 | 
						|
    # update-prod-static with the git upgrade process.  But it'll fail
 | 
						|
    # safely; this seems like a worthwhile tradeoff to minimize downtime.
 | 
						|
    logging.info("Building static assets...")
 | 
						|
    subprocess.check_call(["./tools/update-prod-static", "--authors-not-required", "--prev-deploy",
 | 
						|
                           os.path.join(DEPLOYMENTS_DIR, 'current')],
 | 
						|
                          preexec_fn=su_to_zulip)
 | 
						|
    logging.info("Caching zulip git version...")
 | 
						|
    subprocess.check_call(["./tools/cache-zulip-git-version"], preexec_fn=su_to_zulip)
 | 
						|
else:
 | 
						|
    # Since this doesn't do any actual work, it's likely safe to have
 | 
						|
    # this run before we apply puppet changes (saving a bit of downtime).
 | 
						|
    logging.info("Installing static assets...")
 | 
						|
    subprocess.check_call(["cp", "-rT", os.path.join(deploy_path, 'prod-static/serve'),
 | 
						|
                           '/home/zulip/prod-static'], preexec_fn=su_to_zulip)
 | 
						|
 | 
						|
usermessage_index_migrations = [
 | 
						|
    "[ ] 0082_index_starred_user_messages",
 | 
						|
    "[ ] 0083_index_mentioned_user_messages",
 | 
						|
    "[ ] 0095_index_unread_user_messages",
 | 
						|
    "[ ] 0098_index_has_alert_word_user_messages",
 | 
						|
    "[ ] 0099_index_wildcard_mentioned_user_messages",
 | 
						|
    "[ ] 0177_user_message_add_and_index_is_private_flag",
 | 
						|
    "[ ] 0180_usermessage_add_active_mobile_push_notification",
 | 
						|
]
 | 
						|
# Our next optimization is to check whether any migrations are needed
 | 
						|
# before we start the critical section of the restart.  This saves
 | 
						|
# about 1s of downtime in a no-op upgrade.
 | 
						|
migrations_needed = False
 | 
						|
if not args.skip_migrations:
 | 
						|
    logging.info("Checking for needed migrations")
 | 
						|
    migrations_output = subprocess.check_output(["./manage.py", "showmigrations"],
 | 
						|
                                                preexec_fn=su_to_zulip).decode("utf-8")
 | 
						|
    need_create_large_indexes = False
 | 
						|
    for ln in migrations_output.split("\n"):
 | 
						|
        line_str = ln.strip()
 | 
						|
        if line_str.startswith("[ ]"):
 | 
						|
            migrations_needed = True
 | 
						|
            if line_str in usermessage_index_migrations:
 | 
						|
                need_create_large_indexes = True
 | 
						|
    if need_create_large_indexes:
 | 
						|
        logging.info("Creating some expensive indexes before starting downtime.")
 | 
						|
        subprocess.check_call(["./manage.py", "create_large_indexes"],
 | 
						|
                              preexec_fn=su_to_zulip)
 | 
						|
 | 
						|
# Now we start shutting down services; we start with
 | 
						|
# process-fts-updates, which isn't on the critical serving path.
 | 
						|
if os.path.exists("/etc/supervisor/conf.d/zulip_db.conf"):
 | 
						|
    subprocess.check_call(["supervisorctl", "stop", "process-fts-updates"], preexec_fn=su_to_zulip)
 | 
						|
 | 
						|
core_server_services = ["zulip-django", "zulip-senders:*",
 | 
						|
                        "zulip-tornado" if tornado_processes == 1 else "zulip-tornado:*"]
 | 
						|
worker_services = ["zulip-workers:*"]
 | 
						|
# Stop and start thumbor service only if thumbor is installed.
 | 
						|
if os.path.exists("/etc/supervisor/conf.d/thumbor.conf"):
 | 
						|
    core_server_services.append("zulip-thumbor")
 | 
						|
 | 
						|
if not args.skip_puppet or migrations_needed:
 | 
						|
    # By default, we shut down the service to apply migrations and
 | 
						|
    # puppet changes, to minimize risk of issues due to inconsistent
 | 
						|
    # state.
 | 
						|
    logging.info("Stopping Zulip...")
 | 
						|
    subprocess.check_call(["supervisorctl", "stop"] + core_server_services + worker_services,
 | 
						|
                          preexec_fn=su_to_zulip)
 | 
						|
 | 
						|
if not args.skip_puppet:
 | 
						|
    logging.info("Applying puppet changes...")
 | 
						|
    subprocess.check_call(["./scripts/zulip-puppet-apply", "--force"])
 | 
						|
    subprocess.check_call(["apt-get", "upgrade"])
 | 
						|
 | 
						|
if migrations_needed:
 | 
						|
    logging.info("Applying database migrations...")
 | 
						|
    subprocess.check_call(["./manage.py", "migrate", "--noinput"], preexec_fn=su_to_zulip)
 | 
						|
 | 
						|
subprocess.check_call(["./manage.py", "create_realm_internal_bots"], preexec_fn=su_to_zulip)
 | 
						|
 | 
						|
logging.info("Restarting Zulip...")
 | 
						|
subprocess.check_output(["./scripts/restart-server", "--fill-cache"], preexec_fn=su_to_zulip)
 | 
						|
logging.info("Upgrade complete!")
 | 
						|
 | 
						|
if not args.skip_purge_old_deployments:
 | 
						|
    logging.info("Purging old deployments...")
 | 
						|
    subprocess.check_call(["./scripts/purge-old-deployments"])
 | 
						|
else:
 | 
						|
    logging.info("Skipping purging old deployments.")
 |