Merge lp:~canonical-ca-hackers/ubuntu-webcatalog/branch-deployment into lp:ubuntu-webcatalog

Proposed by Łukasz Czyżykowski
Status: Merged
Approved by: Anthony Lenton
Approved revision: 70
Merged at revision: 68
Proposed branch: lp:~canonical-ca-hackers/ubuntu-webcatalog/branch-deployment
Merge into: lp:ubuntu-webcatalog
Diff against target: 295 lines (+89/-187)
3 files modified
fabtasks/__init__.py (+1/-1)
fabtasks/deploy.py (+88/-0)
fabtasks/upgrade_from_package.py (+0/-186)
To merge this branch: bzr merge lp:~canonical-ca-hackers/ubuntu-webcatalog/branch-deployment
Reviewer Review Type Date Requested Status
Anthony Lenton (community) Approve
Review via email: mp+96637@code.launchpad.net

Commit message

Branch based deployment fabric task.

Description of the change

Added branch-based fab deploy task.

To post a comment you must log in.
Revision history for this message
Anthony Lenton (elachuni) wrote :

Thanks Łukasz!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'fabtasks/__init__.py'
--- fabtasks/__init__.py 2011-09-12 13:37:24 +0000
+++ fabtasks/__init__.py 2012-03-08 18:18:19 +0000
@@ -15,6 +15,6 @@
15# You should have received a copy of the GNU Affero General Public License15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.16# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
18from .upgrade_from_package import *
19from .bootstrap import *18from .bootstrap import *
20from .django import *19from .django import *
20from .deploy import *
2121
=== added file 'fabtasks/deploy.py'
--- fabtasks/deploy.py 1970-01-01 00:00:00 +0000
+++ fabtasks/deploy.py 2012-03-08 18:18:19 +0000
@@ -0,0 +1,88 @@
1# -*- coding: utf-8 -*-
2"""Fabric commands for deploying from the branches to testing server."""
3
4from __future__ import absolute_import
5from __future__ import with_statement
6
7__metaclass__ = type
8__all__ = [
9 'deploy',
10 ]
11
12import os
13import shutil
14import tempfile
15
16from fabric.api import env, run, local, put, cd, sudo
17from fabric.contrib import files
18
19
20def _format(s):
21 """Format string using substitutions from fabric's env."""
22 return s.format(**env)
23
24
25def _run(cmd):
26 run(_format(cmd))
27
28
29def _local(cmd):
30 local(_format(cmd))
31
32
33def _sudo(cmd):
34 sudo(_format(cmd))
35
36
37def migrate_database(deploy_dir):
38 path = os.path.join(
39 deploy_dir, "ubuntu-webcatalog/sourcecode/framework")
40 with cd(path):
41 run("python manage.py syncdb --migrate --noinput")
42
43
44def apache_graceful():
45 sudo('service apache2 graceful', shell=False)
46
47
48def deploy(config_branch, deploy_dir="/srv/uwc.staging.ubuntu.com/staging",
49 production=False):
50 env.cm = "/usr/lib/config-manager/cm.py"
51
52 if not files.exists(env.cm):
53 sudo("apt-get install --yes config-manager")
54
55 env.config_branch = config_branch
56 env.deploy_dir = deploy_dir
57 env.build_dir = tempfile.mkdtemp()
58 env.sourcedeps = _format("{build_dir}/cm/sourcedeps.conf")
59 env.revno = local("bzr revno", capture=True).strip()
60
61 _local("bzr branch {config_branch} {build_dir}/cm")
62
63 _local("sed -i 's/bazaar.isd/bazaar.launchpad.net/g' {sourcedeps}")
64
65 if not production:
66 _local("sed -i '1 s/production/trunk/' {sourcedeps}")
67 _local("sed -i '1 s/;revno=[0-9]\+$/;revno={revno}/' {sourcedeps}")
68 _local("sed -i '2 s/;revno=[0-9]\+$//' {sourcedeps}")
69
70 put(env.sourcedeps, deploy_dir)
71
72 with cd(deploy_dir):
73 _sudo("chown -R jenkins: ubuntu-webcatalog || true")
74
75 if files.exists("ubuntu-webcatalog"):
76 _run("{cm} update sourcedeps.conf")
77 else:
78 _run("{cm} build sourcedeps.conf")
79
80 _run("rm sourcedeps.conf")
81
82 shutil.rmtree(env.build_dir, ignore_errors=True)
83
84 migrate_database(deploy_dir)
85
86 _sudo("chown -R www-data: {deploy_dir}/ubuntu-webcatalog")
87
88 apache_graceful()
089
=== removed file 'fabtasks/upgrade_from_package.py'
--- fabtasks/upgrade_from_package.py 2011-09-12 13:37:24 +0000
+++ fabtasks/upgrade_from_package.py 1970-01-01 00:00:00 +0000
@@ -1,186 +0,0 @@
1# -*- coding: utf-8 -*-
2# This file is part of Apps Directory
3# Copyright (C) 2011 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Affero General Public License as
7# published by the Free Software Foundation, either version 3 of the
8# License, or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18"""Fabric commands for building a package for a direct server upgrade.
19
20The local machine must have bzr-builder and pbuilder, and a lucid chroot
21setup as the default pbuilder distribution:
22
23 $ sudo pbuilder create --distribution lucid
24
25"""
26
27from __future__ import absolute_import
28from __future__ import with_statement
29
30__metaclass__ = type
31__all__ = [
32 'build_and_install_deb',
33 'build_binary_package',
34 'build_source_package',
35 'clean_local_branch',
36 'install_binary_package',
37 'migrate_database',
38 'preflight_check',
39 'restart_services',
40 'update_configs',
41 ]
42
43import os
44import shutil
45import tempfile
46from datetime import datetime
47from glob import glob
48from textwrap import dedent
49
50from fabric.api import (
51 env,
52 local,
53 put,
54 run,
55 sudo,
56 )
57from fabric.contrib.files import exists
58
59
60def clean_local_branch():
61 local("find . -name '*.pyc' -delete")
62
63
64def build_source_package(build_dir, distroseries='lucid'):
65 clean_local_branch()
66 # We use a recipe to ensure that we always generate later versions
67 # with a timestamp. The recipe refers to the local branch '.' rather
68 # than pulling from the network.
69 recipe = dedent("""\
70 # bzr-builder format 0.2 deb-version {debupstream}-0~{revno}+{time}
71 .""")
72 recipe_path = os.path.join(build_dir, 'uwc.recipe')
73 with open(recipe_path, 'w+') as recipe_file:
74 recipe_file.write(recipe)
75
76 local('bzr dailydeb --distribution={0} {1} {2}'.format(
77 distroseries, recipe_path, build_dir))
78 dsc_file = glob(os.path.join(build_dir, '*.dsc'))[0]
79
80 print("Created source package at {0}.".format(dsc_file))
81 return dsc_file
82
83
84def build_binary_package(build_dir, source_dsc):
85 print("Building binary package for {0}".format(source_dsc))
86 local('sudo pbuilder build --buildresult {0} {1}'.format(
87 build_dir, source_dsc))
88 deb_file = glob(os.path.join(build_dir, "*.deb"))[0]
89 print("Created binary at {0}.".format(deb_file))
90 return deb_file
91
92
93def migrate_database(config_dir=None):
94 if config_dir is None:
95 # By default, the uwc config directory is assumed to be
96 # /home/username/django_project.
97 config_dir = 'django_project'
98 run(config_dir + '/manage.py syncdb --migrate')
99
100
101def preflight_check(config_dir='django_project'):
102 run('COLUMNS=40 %s/manage.py preflight' % config_dir)
103
104
105def restart_services():
106 sudo('service apache2 graceful', shell=False)
107
108
109def install_binary_package(deb_file):
110 put(deb_file, '.')
111 deb_filename = os.path.basename(deb_file)
112 sudo('dpkg -i {0}'.format(deb_filename), shell=False)
113
114
115LOCAL_CONFIG = """
116[__main__]
117includes = config/staging.cfg
118 config/dailies.cfg
119"""
120
121
122def update_configs(config_branch, config_dir='django_project'):
123 """Backup current config files and pull in the latest from a bzr branch.
124
125 If it exists, local.cfg is left unmodified.
126 """
127 timestamp = datetime.utcnow().isoformat()
128 backup_dir = "%s.backup.%s" % (config_dir, timestamp)
129 if exists(config_dir):
130 run("mv %s %s" % (config_dir, backup_dir))
131 put_bzr_branch(config_branch, config_dir)
132 if exists(backup_dir + '/local.cfg'):
133 run("cp %s/local.cfg %s" % (backup_dir, config_dir))
134 else:
135 # Create a local.cfg on the server from our template.
136 local_fd, local_path = tempfile.mkstemp()
137 local_file = os.fdopen(local_fd, 'w')
138 local_file.write(LOCAL_CONFIG)
139 local_file.close()
140 put(local_path, "%s/local.cfg" % config_dir)
141 os.remove(local_path)
142
143
144def put_bzr_branch(url, name):
145 """
146 Copy bazaar branch from launchpad to remote host
147
148 This have to be done with local machine as proxy, because remote machine
149 cannot have access to private projects on launchpad. We don't want to put
150 there our private ssh keys.
151
152 :param url: bazaar url to the branch, it can start with lp:
153 :type url: str
154
155 :param name: name of the directory in which branch will be located remotely
156 :type name: str
157
158 """
159 local("rm -rf /tmp/%s" % name)
160
161 local("bzr branch %s /tmp/%s" % (url, name))
162 local("cd /tmp && tar cjf %s.tar.bz2 %s" % (name, name))
163 put("/tmp/%s.tar.bz2" % name, '~/')
164
165 local("rm -rf /tmp/%s.tar.bz2 /tmp/%s" % (name, name))
166
167 run("tar xjf %s.tar.bz2" % name)
168
169
170def build_and_install_deb(config_branch, build_dir=None, distroseries='lucid'):
171 delete_dir_after_build = False
172 if build_dir is None:
173 build_dir = tempfile.mkdtemp()
174 delete_dir_after_build = True
175
176 dsc_file = build_source_package(build_dir, distroseries=distroseries)
177 binary_file = build_binary_package(build_dir, dsc_file)
178 install_binary_package(binary_file)
179 migrate_database()
180 update_configs(config_branch)
181 preflight_check()
182 restart_services()
183 print("Package installed and services restarted.")
184
185 if delete_dir_after_build:
186 shutil.rmtree(build_dir, ignore_errors=True)

Subscribers

People subscribed via source and target branches