1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# -*- coding: utf-8 -*-

import os
import os.path
import sys
import errno

from datetime import datetime
from time import sleep
from shutil import rmtree

from fabric.api import (run, env, task, sudo, abort, settings, cd, put, local,
                        puts, roles, execute, prefix, with_settings)
from fabric.operations import prompt
from fabric.contrib.console import confirm
from fabric.colors import red, green, blue, white, cyan, yellow
from fabric.contrib.files import exists

env.shell = '/bin/sh -c'
env.venv = '/home/www-bbpf/venvs/www.xxx.be'
env.sudo_user = 'www-bbpf'
env.sudo_group = 'www-bbpf'

env.working_dir = '/home/jcigar/www.xxx.be_new'
env.tmp_dir = '/home/jcigar/www.xxx.be_tmp'
env.deploy_dir = '/home/jcigar/www.xxx.be_final'

#env.deploy_dir = '/usr/local/www/apache22/www/www.xxx.be'
env.conf_local = 'local.conf'

env.roledefs = {
    'web' : ['www.xxx.be'],
    'db' : ['db.xxx.be']
}

def _puts(*args, **kwargs):
    puts(*args, **kwargs)
    sleep(3)

def _venv():
    return '. %s' % os.path.join(env.venv, 'bin/activate')

@@task()
def virtualenv():
    #with settings(sudo_user=env.sudo_user):
    _puts(green('=> Try to load virtual environment...'))
    load_ok = sudo(_venv(), warn_only=True).succeeded
    if not load_ok:
        _puts(red('=> Virtual environment is missing or broken, (re)create it...'))
        sudo('virtualenv --clear %s' % env.venv)

@@task()
def requirements():
    with cd(env.working_dir):
        _puts(green('=> Checking requirements...'))
        with prefix(_venv()):
            sudo('pip install -r requirements.txt')

@@task()
def git():
    today = datetime.now().strftime('%Y%m%d%H%M%S%f')

    # Place to store our "work in progress"
    work_dir = os.path.join(os.path.dirname(__file__), 'tmp', 'deploy')

    try:
        rmtree(work_dir)
    except OSError as e:
        if e.errno != errno.ENOENT:
            abort(red(e.message))

    os.makedirs(work_dir)

    git_archive_file = 'amnesia-%s.tar' % today
    git_archive_full = os.path.join(work_dir, git_archive_file)

    _puts(green('=> Create an archive of the main project...'))
    local('git archive --format tar -o "%s" HEAD' % git_archive_full)

    # At this time of writing, there is no option in ┬┤git archive┬┤ to include
    # submodules ...
    _puts(green('=> Create one archive per submodule (if any)...'))
    local("git submodule foreach 'git archive --format tar -o %s/$sha1.tar --prefix $path/ $sha1'" % work_dir)

    _puts(green('=> Cleaning temporary/working directories...'))
    # This ugly hack is necessary as put() with use_sudo=True seems completely
    # broken (see https://github.com/fabric/fabric/issues/773)
    run('mkdir -p %s' % env.tmp_dir)
    if confirm('I will now delete everything in %s, OK?' % env.tmp_dir):
        run('find "%s" -mindepth 1 -delete' % env.tmp_dir)
    if confirm('I will now delete everything in %s, OK?' % env.working_dir):
        sudo('find "%s" -mindepth 1 -delete' % env.working_dir)
    #run('find %s -type f -name *.tar -delete' % env.tmp_dir)

    _puts(green('=> Copying archive(s) to destination (this could take some time)...'))
    uploaded_files = put(os.path.join(work_dir, '*.tar'), env.tmp_dir)

    _puts(green('=> Decompressing archive(s)...'))
    with cd(env.working_dir):
        for f in uploaded_files:
            sudo('tar xf %s' % f)

#        run('find . -type f -name "*.tar" -execdir tar xf {} \; -delete')

def _server(op, *args):
    if op == 'start':
        opt = '-u'
    elif op == 'stop':
        opt = '-d'
    else:
        abort(red('invalid option %s' % op))

    with cd('/var/service/www.xxx.be'):
        sudo('/usr/local/bin/svc %s .' % opt, user="root")

@@task()
@@with_settings(warn_only=True)
def server_stop():
    _puts(green('=> Stopping server...'))
    with prefix(_venv()):
        with cd(env.working_dir):
            sudo(_server('stop'))

@@task()
@@with_settings(warn_only=True)
def start_server():
    _puts(green('=> Starting server...'))
    with prefix(_venv()):
        with cd(env.working_dir):
            sudo(_server('start'))

@@task()
def directories():
    with cd(env.working_dir):
        _puts(green('=> Ensures that some directories exists...'))
        for rep in ('data/chameleon_cache', 'data/sessions',
                    'logs', 'run', 'tmp'):
            sudo('mkdir -m 750 -p "%s"' % rep)
@@task()
def permissions():
    with cd(env.working_dir):
        _puts(green('=> Fixing permissions...'))
        sudo('find . -type d -print0|xargs -0 chmod 755')
        sudo('find . -type f -print0|xargs -0 chmod 644')
        sudo('chmod 400 conf/*', warn_only=True)
        sudo('chmod 755 bin/*', warn_only=True)

@@task()
def move_old_files():
    _puts(green("=> Move some files that have been generated by the application during it's lifetime (files uploaded by users, etc)"))
    for subdir in ('data/files/', 'conf/local.conf'):
        src = os.path.join(env.deploy_dir, subdir)
        dst = os.path.join(env.working_dir, subdir)
        if confirm('Move %s to %s ?' % (src, dst)):
            sudo('mv -v "%s" "%s"' % (src, dst), warn_only=True)

@@task()
def replace_webapp():
    _puts(green('=> Remove the old application and install the new one...'))
    if confirm('=> I will now replace the content of %s by %s, OK?' % (env.deploy_dir, env.working_dir)):
        sudo('find "%s" -mindepth 1 -delete' % env.deploy_dir)
        sudo('mv %s/* %s' % (env.working_dir.rstrip('/'), env.deploy_dir))

@@task(default=True)
#@with_settings(warn_only=True)
@@roles('web')
def deploy():
    """Deploy webapp"""

    # Ensure that env.user can SSH
    run('uname -a', warn_only=True)

    if not confirm('This will deploy amnesia, are you sure ?'):
        abort(red('cancelled'))

    while True:
        prompt('Please supply a sudo user:', 'sudo_user', default=env.sudo_user)
        if run('getent passwd %s' % env.sudo_user, quiet=True, warn_only=True).return_code == 0:
            break
        else:
            _puts(red("=> Error: user ""%s"" doesn't exist" % env.sudo_user))

    prompt('Please supply a virtual environment (virtualenv):', 'venv', default=env.venv)

    puts(red('IMPORTANT:'))
    puts(red('----------'))
    puts('')
    puts(red('If the following conditions are not met the script will NOT work:'))
    puts(red('-> User "%s" must be able to "sudo -u %s"' % (env.user, env.sudo_user)))
    puts(red('-> User "%s" must be able to "sudo -u root" to start/stop the service' % env.user))
    puts(red('-> "%s" must be writable by "%s"' % (env.tmp_dir, env.user)))
    puts(red('-> "%s" must exist and be writable by "%s"' % (env.working_dir, env.sudo_user)))
    puts(red('-> "%s" must exist and be writable by "%s"' % (env.deploy_dir, env.sudo_user)))
    puts(red('-> "%s" must exist and be writable by "%s"' % (env.venv, env.sudo_user)))
    puts('')
    puts(red('NOTE:'))
    puts(red('-----'))
    puts('')
    puts(red('Although this script should be safe, you should make a backup (this is NOT done by the script)'))

    if not confirm('Continue ?'):
        abort(red('cancelled'))

    execute(virtualenv)
    execute(git)
    execute(requirements)
    execute(directories)
    execute(server_stop)
    execute(move_old_files)
    execute(permissions)
    execute(replace_webapp)
    execute(server_start)