deploy.py 11.5 KB
Newer Older
Emmanuel Milou's avatar
Emmanuel Milou committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# coding: utf-8
#
# Copyright (C) 2016 Savoir-faire Linux Inc. (<www.savoirfairelinux.com>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero 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 Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
19
from __future__ import unicode_literals
20
from fabric.api import task, roles, env, local, run
Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
21
from fabric.colors import red, green
Emmanuel Milou's avatar
Emmanuel Milou committed
22
from fabric.utils import abort
Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
23
24
25
26
27
28
29

import helpers as h
import os, glob

@task
@roles('local')
def to_aegir(environement, build='0', role='local', migrate_sites=False, delete_old_platforms=False):
Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
30
    """Deploy the artifact to the aegir server.
Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
31

Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
32
    $ fab deploy.to_aegir
Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
33

Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
34
35
36
37
38
    :param environement: the environnement (dev, stage, production)
    :param build: the number of the build in Jenkins
    :param role: the role to run the task in fabric
    :param migrate_sites: if aegir should migrate the sites to the new platform
    :param delete_old_platforms: if aegir should remove old platforms
Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
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
    """
    aegir_server = env.aegir_server
    aegir_user = env.aegir_user
    aegir_path = env.aegir_path
    aegir_dest_server = env.aegir_dest_server
    build_dir = '{}/build'.format(env.workspace)
    if environement == 'dev' and aegir_server and aegir_user and aegir_path and aegir_dest_server:
        os.chdir(build_dir)
        for artifact in glob.glob("*.tar.gz"):
            # Get platform name
            platform_name = '{}-{}-{}'.format(environement, artifact.split('.')[0], build)

            # Remove old platforms
            if h.fab_exists(role, env.site_root):
                h.fab_run(role, 'rm -rf {}'.format(env.site_root))

            # Untar new platform
            with h.fab_cd(role, '{}/src'.format(env.workspace)):
                h.fab_run(role, 'tar -xvzf {}/build/{}'.format(env.workspace, artifact))

            # Rsync new platform
            with h.fab_cd(role, env.workspace):
                print green('Rsync {} to aegir'.format(platform_name))
                h.fab_run(role, 'rsync -a src/drupal/ {}@{}:{}/platforms/{}'.format(aegir_user, aegir_server,
                                                                                    aegir_path, platform_name))

            # Declare platform in aegir
            print green('Declaring {} in aegir'.format(platform_name))
            h.fab_run(
                role,
                """
                ssh {}@{} "drush --root='{}/platforms/{}' provision-save '@platform_{}' --context_type='platform' --web_server=@{}"
                """.format(aegir_user, aegir_server, aegir_path, platform_name, platform_name, aegir_dest_server)
            )
            h.fab_run(
                role,
                """
                ssh {}@{} "drush @hostmaster hosting-import platform_{}"
                """.format(aegir_user, aegir_server, platform_name)
            )
            h.fab_run(
                role,
                """
                ssh {}@{} "drush @hostmaster hosting-dispatch"
                """.format(aegir_user, aegir_server)
            )
            if migrate_sites == 'true':
                print green("Migrating all websites currently on {} to {}".format(environement, platform_name))
                h.fab_run(
                    role,
                    """
                    ssh {}@{} "./migrateS.drush {} {}"
                    """.format(aegir_user, aegir_server, environement, platform_name))

            # Remove old platforms
            h.fab_run(role, 'rm -rf {}'.format(env.site_root))
            h.fab_run(role, 'rm -rf {}/build/{}'.format(env.workspace, artifact))

        # Delete old platforms in aegir
        if delete_old_platforms == 'true':
            print green("Deleting old platforms on {}".format(environement))
            h.fab_run(
                    role,
                    """
                    ssh {}@{} "./deleteP.drush {} {}"
                    """.format(aegir_user, aegir_server, environement, platform_name))

    else:
        print red('There are not enought information to deploy the platformt')

109

Emmanuel Milou's avatar
Emmanuel Milou committed
110
def set_hosts(environment):
111
112
113
114
    """
    Set the hosts Fabric environment variable with the target environment.
    This is the host that will be used to run SSH commands on.
    """
Emmanuel Milou's avatar
Emmanuel Milou committed
115
116
117
    if environment not in env.aliases:
        abort('Environment {} could not be found in the aliases definition.'.format(environment))

118
119
    target = env.aliases.get(environment)
    env.hosts = ['{}@{}'.format(target.get('user'), target.get('host'))]
Emmanuel Milou's avatar
Emmanuel Milou committed
120
121


122
123
124
125
126
127
128
129
130
131
132
133
def is_aegir_deployment(target):
  """Check if the target environment is an Aegir server."""
  return False if 'aegir' not in target or target.get('aegir') is False else True


def _aegir_platform_name(target, environment):
  """Build the platform needed by Aegir, from the placeholder in configuration file.
  """
  if 'aegir_platform' not in target:
    abort('Aegir needs a unique platform name to function properly. Check your aegir_platform key in your aliases.')
  aegir_platform = target.get('aegir_platform')
  return aegir_platform.format(name=env.project_name, env=environment, build=env.build_number)
Emmanuel Milou's avatar
Emmanuel Milou committed
134
135
136


def target_dir(environment):
137
138
139
  target = env.aliases.get(environment)
  return target.get('root') + _aegir_platform_name(target, environment) if is_aegir_deployment(target) else target.get('root')

Emmanuel Milou's avatar
Emmanuel Milou committed
140

141
142
143
144
145
146
147
148
149
def get_archive_from_dir(directory):
  """List tarball archives in a directory.
  """
  files = glob.glob1(directory, '*.tar.gz')
  if len(files) == 0:
    abort('No tarball found in {}'.format(directory))
  if len(files) > 1:
    abort('More than one tarball has been found in {}. Can not decide which one to deploy.'.format(directory))
  return files[0]
Emmanuel Milou's avatar
Emmanuel Milou committed
150
151
152
153
154
155


@task
def provision(environment, role='local'):
    """Provision a Jenkins deployment.

156
157
    This task loads the target environment and extract the archive to deploy.
    """
Emmanuel Milou's avatar
Emmanuel Milou committed
158
159
    set_hosts(environment)

160
    artefact = get_archive_from_dir(env.builddir)
Emmanuel Milou's avatar
Emmanuel Milou committed
161
162
163
164
165
166
167
168

    with h.fab_cd(role, '{}/src'.format(env.workspace)):

        # Clear the currently installed platform
        if h.fab_exists(role, env.site_root):
            h.fab_run(role, 'rm -rf {}'.format(env.site_root))
        # Extract the platform to deploy
        h.fab_run(role, 'tar -xzf {}/{}'.format(env.builddir, artefact))
169
170

        # Fast-check if the archive looks like a Drupal installation
Emmanuel Milou's avatar
Emmanuel Milou committed
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
        if not h.fab_exists(role, '{}/src/drupal'.format(env.workspace)):
            abort('The archive to deploy does not contain a drupal directory.')

        if not os.path.isfile('{}/src/drupal/cron.php'.format(env.workspace)):
            abort('The archive to deploy does not seem to contain a valid Drupal installation.')

        print green('The platform {} is now ready to be deployed to the target environment {}.'.format(artefact, environment))



@task
def push(environment, role='local'):
    """Push the platform to the target environment.

        :param environment: The target environment. It must match a valid Drush alias.
    """
    target = env.aliases.get(environment)
188
    target_directory = target_dir(environment)
Emmanuel Milou's avatar
Emmanuel Milou committed
189

190
    local('rsync -a src/drupal/ {}@{}:{}'.format(target.get('user'), target.get('host'), target_directory))
Emmanuel Milou's avatar
Emmanuel Milou committed
191

192
193
194
    if is_aegir_deployment(target):
        platform = _aegir_platform_name(target, environment)
        _aegir_provision_platform(platform, target.get('aegir_path'), target.get('aegir_destsrv'))
Emmanuel Milou's avatar
Emmanuel Milou committed
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212


def _aegir_provision_platform(platform, aegir_path, aegir_destsrv):
    # Provision the platform on Aegir
    run('drush --root="{}/platforms/{}" provision-save "@platform_{}" --context_type="platform" --web_server=@{}'.format(aegir_path, platform, platform, aegir_destsrv))
    run('drush @hostmaster hosting-import platform_{}'.format(platform))
    run('drush @hostmaster hosting-dispatch')


def _aegir_migrate_sites(environment, platform):
    run('{}/migrate-sites {} {}'.format(environment, platform))

@task
@roles('local')
def migrate(environment, role='local'):
    """Migrate the Drupal database on the target environment.

        :param environment: The target environment. It must match a valid Drush alias.
213
214
    """
    target = env.aliases.get(environment)
Emmanuel Milou's avatar
Emmanuel Milou committed
215
    if is_aegir_deployment(target) and env.migrate == "true":
216
        platform = _aegir_platform_name(target, environment)
Emmanuel Milou's avatar
Emmanuel Milou committed
217
218
219
220
221
222
223
224
225
226
227
228
229
        _aegir_migrate_sites(environment, platform)


        # Run the updatedb script
        # with h.fab_cd(role, env.site_root):
        # Run the Drupal update script
#        h.fab_run(
    #        role,
    #        'drush --yes @{} updatedb'
    #          .format(environment)
    #    )
  #      print green('The target environment {} is up-to-date.'.format(environment))
#
230

Emmanuel Milou's avatar
Emmanuel Milou committed
231
232
233
234
235
236
237
238
        # Clear caches
 #       h.fab_run(
   #       role,
    #      'drush --yes @{} cache-clear all'
    #        .format(environment)
     #   )
   #     print green('The caches have been cleared on the target environment {}.'.format(environment))

Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
239
240
241
242

@task
@roles('local')
def to_server(environement, role='local'):
Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
243
244
245
246
247
248
249
    """Deploy the artifact to the aegir server.

        $ fab deploy.to_aegir

        :param environement: the environnement (dev, stage, production)
        :param role: the role to run the task in fabric
    """
Ernesto Rodriguez Ortiz's avatar
Ernesto Rodriguez Ortiz committed
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
    build_dir = '{}/build'.format(env.workspace)
    os.chdir(build_dir)
    for artifact in glob.glob("*.tar.gz"):
        # Remove old platforms
        if h.fab_exists(role, env.site_root):
            h.fab_run(role, 'rm -rf {}'.format(env.site_root))

        # Untar new platform
        print green('Untar new platform')
        with h.fab_cd(role, '{}/src'.format(env.workspace)):
            h.fab_run(role, 'tar -xvzf {}/build/{}'.format(env.workspace, artifact))
            d.aliases()

        with h.fab_cd(role, env.site_root):
            # Set the site in maintenance mode
            print green('Set the site in maitenance mode')
            h.fab_run(
                role,
                'drush --yes @{} vset site_offline 1'
                    .format(environement)
            )
            # rsync
            print green('Rsync the site source code')
            h.fab_run(
                role,
                'drush --yes rsync --exclude=sites/all/files --exclude-files --exclude=.htaccess --delete --verbose @self @{}'
                    .format(environement)
            )
            # update data base
            print green('Update site database')
            h.fab_run(
                role,
                'drush --yes @{} updatedb'
                    .format(environement)
            )
            # Set site online
            print green('Set the site online')
            h.fab_run(
                role,
                'drush --yes @{} vset site_offline 0'
                    .format(environement)
            )
            # clean cache
            print green('Clear site cache')
            h.fab_run(
                role,
                'drush --yes @{} cache-clear all'
                    .format(environement)
            )
        # Remove old platforms
        h.fab_run(role, 'rm -rf {}'.format(env.site_root))
        h.fab_run(role, 'rm -rf {}/build/{}'.format(env.workspace, artifact))
        print green('Deployment in {} finished'.format(environement))