Search code examples
djangofabricdjango-deployment

Fabric - Force password prompt on Production Deploy


Is it possible to force the user to enter their password when they deploy to production?

I was deploying to staging, but accidentally hit tab on the CL to production instead and almost made a huge mistake! Needless to say I will never use autocomplete for fab ... ever ever again.

UPDATE:

Below is what our fabfile essentially looks like. Each host, like application-staging or application-production, is saved in the ssh config.

from fabric import colors
from fabric.api import *
from fabric.contrib.project import *

import git

env.app = '{{ project_name }}'
env.dest = "/var/www/%(app)s" % env
env.use_ssh_config = True

def reload_processes():
    sudo("kill -HUP `cat /tmp/%(app)s.pid`" % env)

def sync():
    repo = git.Repo(".")
    sha = repo.head.commit.hexsha
    with cd(env.dest):
        run("git fetch --all")
        run("git checkout {} -f".format(sha))

    if "production" in env.host_string:
        with cd(env.dest):
            run("compass compile")

            with prefix(". /home/ubuntu/environments/%(app)s/bin/activate" % env):
                run("%(dest)s/manage.py syncmedia" % env)

def deploy():
    sync()
    link_files()
    reload_processes()
    add_commit_sha()

def link_files():
    print(colors.yellow("Linking settings."))
    env.label = env.host_string.replace("%(app)s-", "")
    with cd(env.dest):
        sudo("rm -f local_settings.py")
        sudo("ln -s conf/settings/%(label)s.py local_settings.py" % env)

        sudo("rm -f conf/gunicorn/current.py")
        sudo("ln -s %(label)s.py conf/gunicorn/current.py" % env)

        sudo("rm -f celeryconfig.py")
        sudo("ln -s conf/settings/celery/%(label)s.py celeryconfig.py" % env)

        sudo("rm -f conf/supervisor/programs.ini" % env)
        sudo("ln -s %(label)s.ini conf/supervisor/programs.ini" % env)

def reload_processes(reload_type="soft"):
    print(colors.yellow("Reloading processes."))

    env.label = env.host_string.replace("%(app)s-", "")
    with cd(env.dest):
        sudo("kill -HUP `cat /tmp/gunicorn.%(app)s.%(label)s.pid`" % env)

def add_commit_sha():
    repo = git.Repo(".")
    sha = repo.head.commit.hexsha
    sed("{}/settings.py".format(env.dest), "^COMMIT_SHA = .*$", 'COMMIT_SHA = "{}"'.format(sha), backup="\"\"", use_sudo=True)

Solution

  • I use this pattern, where you set up the staging/prod configurations in their own tasks:

    @task
    def stage():
      env.deployment_location = 'staging'
      env.hosts = ['staging']
    
    @task
    def prod():
      env.deployment_location = 'production'
      env.hosts = ['prod1', 'prod2']
    
    @task
    def deploy():
      require('deployment_location', used_for='deployment. \
      You need to prefix the task with the location, i.e: fab stage deploy.')
    
      confirm("""OK. We're about to deploy to:
    
        Location: {env.deployment_location}
    
        Is that cool?""".format(env=env))
    
      # deployment tasks down here
    

    In this case, you have to type fab prod deploy and say yes to the confirmation message in order to deploy to production.

    Just typing fab deploy is an error, because the deployment_location env variable isn't set.

    It doesn't prevent total idiocy, but it does prevent accidental typos and so far it's worked well.