Merge lp:~cjwatson/charms/trusty/turnip/build-label into lp:~canonical-launchpad-branches/charms/trusty/turnip/devel

Proposed by Colin Watson
Status: Merged
Merged at revision: 81
Proposed branch: lp:~cjwatson/charms/trusty/turnip/build-label
Merge into: lp:~canonical-launchpad-branches/charms/trusty/turnip/devel
Diff against target: 491 lines (+209/-72)
12 files modified
.bzrignore (+1/-2)
Makefile.common (+24/-21)
README.md (+0/-2)
config.yaml (+28/-0)
deploy-requirements.txt (+0/-2)
hooks/actions.py (+149/-38)
hooks/services.py (+2/-2)
templates/turnip-httpserver.conf.j2 (+1/-1)
templates/turnip-packbackendserver.conf.j2 (+1/-1)
templates/turnip-packfrontendserver.conf.j2 (+1/-1)
templates/turnip-sshserver.conf.j2 (+1/-1)
templates/turnip-virtserver.conf.j2 (+1/-1)
To merge this branch: bzr merge lp:~cjwatson/charms/trusty/turnip/build-label
Reviewer Review Type Date Requested Status
Kit Randel (community) Approve
Review via email: mp+275468@code.launchpad.net

Commit message

Allow updating the code payload separately from the charm using a build label.

Description of the change

Allow updating the code payload separately from the charm using a build label.

The approach used here is based heavily on the software-center-agent charm. The preferred approach is to store the payload in Swift (Canonical developers can use their Canonistack credentials for this), but you can also use "make deploy" or "make rollout" to do an initial deployment or an updated code rollout respectively and it'll push the payload around manually. The previous payload is kept in place for the sake of quick rollouts, but at the moment we prune all but the previous and current payloads: this has the benefit of not needing to worry about working out which the newest ones are.

The virtualenv moves inside the payload directory so that each payload gets its own. /srv/turnip/code remains as a symlink to the current payload, which is convenient and saves us having to substitute the current build label into the Upstart jobs.

There are no tests directly here, but it'll at least get integration testing by way of the corresponding changes to the Mojo spec.

To post a comment you must log in.
Revision history for this message
Kit Randel (blr) wrote :

Just a minor formatting issue.

I'm working through updating Rutabaga's charm similarly as a somewhat circumlocuitous form of review. No doubt fine, given the mojo spec ran, but thought it might be worth leaving this open in the event that I came across anything in the process.

Given our services will be duplicating a reasonable amount of charm code, I did wonder if we should consider maintaining our own charmhelpers like library.

review: Approve
Revision history for this message
Kit Randel (blr) wrote :

Although flake8 is in turnip-dependencies, I don't see it in any requirements manifest, so `make lint` will fail here afaict.

Revision history for this message
Kit Randel (blr) wrote :

> Although flake8 is in turnip-dependencies, I don't see it in any requirements
> manifest, so `make lint` will fail here afaict.

Apologies, disregard that, comment was intended for the turnipcake MP.

82. By Colin Watson

Fix over-indentation.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2015-04-27 01:59:44 +0000
+++ .bzrignore 2015-11-02 11:16:19 +0000
@@ -1,5 +1,4 @@
1*.pyc1*.pyc
2.coverage2.coverage
3.venv3.venv
4files/pip-cache4files/*
5files/turnip.tar.gz
65
=== modified file 'Makefile.common'
--- Makefile.common 2015-03-29 02:58:13 +0000
+++ Makefile.common 2015-11-02 11:16:19 +0000
@@ -5,13 +5,13 @@
5PWD := $(shell pwd)5PWD := $(shell pwd)
6HOOKS_DIR := $(PWD)/hooks6HOOKS_DIR := $(PWD)/hooks
7SOURCE_DIR ?= $(shell dirname $(PWD))/.source/$(APP_NAME)7SOURCE_DIR ?= $(shell dirname $(PWD))/.source/$(APP_NAME)
8PIP_CACHE := $(PWD)/files/pip-cache8FILES_DIR := $(PWD)/files
99
10ifeq ($(PIP_SOURCE_DIR),)10BUILD_LABEL = $(shell bzr log -rlast: --show-ids $(SOURCE_DIR) | sed -n 's/^revision-id: //p')
11PIP_CACHE_ARGS :=11TARBALL = $(APP_NAME).tar.gz
12else12ASSET = $(FILES_DIR)/$(BUILD_LABEL)/$(TARBALL)
13PIP_CACHE_ARGS := --no-index --find-links=file://$(PIP_SOURCE_DIR)13UNIT = $(APP_NAME)/0
14endif14CHARM_UNIT_PATH := /var/lib/juju/agents/unit-$(APP_NAME)-0/charm
1515
16all: setup lint test16all: setup lint test
1717
@@ -20,9 +20,22 @@
20 @juju upgrade-charm --repository=../.. $(APP_NAME)20 @juju upgrade-charm --repository=../.. $(APP_NAME)
2121
2222
23deploy: tarball pip-cache23deploy: payload
24 @echo "Deploying $(APP_NAME)..."24 @echo "Deploying $(APP_NAME)..."
25 @juju deploy --repository=../.. local:trusty/$(APP_NAME)25 @juju deploy --repository=../.. local:trusty/$(APP_NAME)
26 @$(MAKE) rollout SKIP_BUILD=true
27
28
29# deploy a new revision/branch
30rollout: _PATH=$(CHARM_UNIT_PATH)/files/$(BUILD_LABEL)
31rollout:
32ifneq ($(SKIP_BUILD),true)
33 $(MAKE) payload
34endif
35 # manually copy our asset to be in the right place, rather than upgrade-charm
36 juju scp $(ASSET) $(UNIT):$(TARBALL)
37 juju ssh $(UNIT) 'sudo mkdir -p $(_PATH) && sudo mv $(TARBALL) $(_PATH)/'
38 juju set $(APP_NAME) build_label=$(BUILD_LABEL)
2639
2740
28ifeq ($(NO_FETCH_CODE),)41ifeq ($(NO_FETCH_CODE),)
@@ -39,23 +52,14 @@
39endif52endif
4053
4154
42pip-cache: fetch-code
43 @echo "Updating python dependency cache..."
44 @mkdir -p $(PIP_CACHE)
45 @pip install $(PIP_CACHE_ARGS) --no-use-wheel --download $(PIP_CACHE) \
46 -r $(SOURCE_DIR)/requirements.txt \
47 -r deploy-requirements.txt
48
49
50check-rev:55check-rev:
51ifndef REV56ifndef REV
52 $(error Revision number required to fetch source: e.g. $ REV=10 make deploy)57 $(error Revision number required to fetch source: e.g. $ REV=10 make deploy)
53endif 58endif
5459
55tarball: fetch-code60payload: fetch-code
56 @echo "Creating tarball for deploy..."61 @echo "Building asset for $(BUILD_LABEL)..."
57 @mkdir -p files/62 @$(MAKE) -C $(SOURCE_DIR) build-tarball TARBALL_BUILDS_DIR=$(FILES_DIR)
58 @tar czf files/$(APP_NAME).tar.gz -C $(SOURCE_DIR) .
5963
6064
61# The following targets are for charm maintenance.65# The following targets are for charm maintenance.
@@ -65,7 +69,6 @@
65 @find . -depth -name '__pycache__' -exec rm -rf '{}' \; 69 @find . -depth -name '__pycache__' -exec rm -rf '{}' \;
66 @rm -f .coverage70 @rm -f .coverage
67 @rm -rf $(SOURCE_DIR)71 @rm -rf $(SOURCE_DIR)
68 @rm -rf $(PIP_CACHE)
69 @rm -rf .venv72 @rm -rf .venv
7073
7174
@@ -94,4 +97,4 @@
94 @bzr cat lp:charm-helpers/tools/charm_helpers_sync/charm_helpers_sync.py > /tmp/charm_helpers_sync.py97 @bzr cat lp:charm-helpers/tools/charm_helpers_sync/charm_helpers_sync.py > /tmp/charm_helpers_sync.py
9598
9699
97.PHONY: clean lint setup tarball test upgrade100.PHONY: clean lint setup payload test upgrade
98101
=== modified file 'README.md'
--- README.md 2015-02-11 21:48:08 +0000
+++ README.md 2015-11-02 11:16:19 +0000
@@ -11,8 +11,6 @@
1111
12 $ REV=revno make deploy12 $ REV=revno make deploy
1313
14The deploy target will create a fresh python package cache in files/pip-cache and sets the turnip config variable 'revision' to that specified by REV.
15
16# Todo14# Todo
1715
18 * Refactor to use charmhelpers.core.services16 * Refactor to use charmhelpers.core.services
1917
=== modified file 'config.yaml'
--- config.yaml 2015-05-22 15:42:11 +0000
+++ config.yaml 2015-11-02 11:16:19 +0000
@@ -3,6 +3,10 @@
3 type: string3 type: string
4 default: 'turnip'4 default: 'turnip'
5 description: Name of this application.5 description: Name of this application.
6 build_label:
7 type: string
8 default: ""
9 description: Build label to run.
6 nagios_context:10 nagios_context:
7 default: "juju"11 default: "juju"
8 type: string12 type: string
@@ -120,6 +124,30 @@
120 description: |124 description: |
121 Hosts that should be allowed to rsync logs. Note that this relies on125 Hosts that should be allowed to rsync logs. Note that this relies on
122 basenode.126 basenode.
127 swift_username:
128 type: string
129 default: ""
130 description: Username to use when accessing Swift.
131 swift_password:
132 type: string
133 default: ""
134 description: Password to use when accessing Swift.
135 swift_auth_url:
136 type: string
137 default: ""
138 description: URL for authenticating against Keystone.
139 swift_region_name:
140 type: string
141 default: ""
142 description: Swift region.
143 swift_tenant_name:
144 type: string
145 default: ""
146 description: Entity that owns resources.
147 swift_container_name:
148 type: string
149 default: ""
150 description: Container to put objects in.
123151
124 # apt configuration used by charmhelpers.152 # apt configuration used by charmhelpers.
125 install_sources:153 install_sources:
126154
=== removed file 'deploy-requirements.txt'
--- deploy-requirements.txt 2015-03-27 07:43:45 +0000
+++ deploy-requirements.txt 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
1envdir==0.7
2gunicorn==19.3.0
30
=== modified file 'hooks/actions.py'
--- hooks/actions.py 2015-05-22 15:42:11 +0000
+++ hooks/actions.py 2015-11-02 11:16:19 +0000
@@ -1,4 +1,5 @@
1import base641import base64
2import errno
2import grp3import grp
3import os4import os
4import pwd5import pwd
@@ -26,19 +27,29 @@
2627
27# Globals28# Globals
28CHARM_FILES_DIR = os.path.join(hookenv.charm_dir(), 'files')29CHARM_FILES_DIR = os.path.join(hookenv.charm_dir(), 'files')
30CHARM_SCRIPTS_DIR = os.path.join(hookenv.charm_dir(), 'scripts')
29REQUIRED_PACKAGES = [31REQUIRED_PACKAGES = [
30 'python-virtualenv', 'python-dev', 'python-pygit2', 'git', 'cgit',32 'python-virtualenv',
33 'python-dev',
34 'libgit2-dev',
35 'libffi-dev',
36 'git',
37 'cgit',
31 # Unfortunately we need build-essential to compile some extensions,38 # Unfortunately we need build-essential to compile some extensions,
32 # notably Twisted. Using wheels rather than a pip-cache might let us39 # notably Twisted. Using wheels rather than a pip-cache might let us
33 # avoid this in future.40 # avoid this in future.
34 'build-essential',41 'build-essential',
42 'python-swiftclient',
35 ]43 ]
36BASE_DIR = config['base_dir']44BASE_DIR = config['base_dir']
45PAYLOADS_DIR = os.path.join(BASE_DIR, 'payloads')
37CODE_DIR = os.path.join(BASE_DIR, 'code')46CODE_DIR = os.path.join(BASE_DIR, 'code')
38VENV_DIR = os.path.join(BASE_DIR, 'venv')47VENV_DIR = os.path.join(CODE_DIR, 'env')
48OLD_VENV_DIR = os.path.join(BASE_DIR, 'venv')
39LOGS_DIR = os.path.join(BASE_DIR, 'logs')49LOGS_DIR = os.path.join(BASE_DIR, 'logs')
40DATA_DIR = os.path.join(BASE_DIR, 'data')50DATA_DIR = os.path.join(BASE_DIR, 'data')
41KEY_DIR = os.path.join(BASE_DIR, 'keys')51KEY_DIR = os.path.join(BASE_DIR, 'keys')
52CODE_TARBALL = 'turnip.tar.gz'
4253
43CODE_USER = config['code_user']54CODE_USER = config['code_user']
44CODE_GROUP = config['code_group']55CODE_GROUP = config['code_group']
@@ -62,7 +73,7 @@
62def make_srv_location():73def make_srv_location():
63 hookenv.log('Creating directories...')74 hookenv.log('Creating directories...')
6475
65 for dir in (BASE_DIR, CODE_DIR):76 for dir in (BASE_DIR, PAYLOADS_DIR):
66 host.mkdir(dir, owner=CODE_USER, group=CODE_GROUP, perms=0o755)77 host.mkdir(dir, owner=CODE_USER, group=CODE_GROUP, perms=0o755)
67 for dir in (LOGS_DIR, DATA_DIR, KEY_DIR):78 for dir in (LOGS_DIR, DATA_DIR, KEY_DIR):
68 host.mkdir(dir, owner=USER, group=GROUP, perms=0o755)79 host.mkdir(dir, owner=USER, group=GROUP, perms=0o755)
@@ -87,23 +98,143 @@
87 host.add_user_to_group(USER, CGIT_GROUP)98 host.add_user_to_group(USER, CGIT_GROUP)
8899
89100
90def unpack_source(service_name):101def get_swift_creds(config):
91 hookenv.log('Deploying source...')102 return {
92103 'user': config['swift_username'],
104 'project': config['swift_tenant_name'],
105 'password': config['swift_password'],
106 'authurl': config['swift_auth_url'],
107 'region': config['swift_region_name'],
108 }
109
110
111def swift_base_cmd(**swift_creds):
112 return [
113 'swift',
114 '--os-username=' + swift_creds['user'],
115 '--os-tenant-name=' + swift_creds['project'],
116 '--os-password=' + swift_creds['password'],
117 '--os-auth-url=' + swift_creds['authurl'],
118 '--os-region-name=' + swift_creds['region'],
119 ]
120
121
122def swift_get_etag(name, container=None, **swift_creds):
123 cmd = swift_base_cmd(**swift_creds) + ['stat', container, name]
124 file_stat = subprocess.check_output(cmd).splitlines()
125 for line in file_stat:
126 words = line.split()
127 if words[0] == 'ETag:':
128 return words[1]
129
130
131def swift_fetch(source, target, container=None, **swift_creds):
132 cmd = swift_base_cmd(**swift_creds) + [
133 'download', '--output=' + target, container, source]
134 subprocess.check_call(cmd)
135
136
137def unlink_force(path):
138 """Unlink path, without worrying about whether it exists."""
139 try:
140 os.unlink(path)
141 except OSError as e:
142 if e.errno != errno.ENOENT:
143 raise
144
145
146def symlink_force(source, link_name):
147 """Create symlink link_name -> source, even if link_name exists."""
148 unlink_force(link_name)
149 os.symlink(source, link_name)
150
151
152def install_python_packages(target_dir):
153 hookenv.log('Installing Python dependencies...')
154 subprocess.check_call(
155 ['sudo', '-u', CODE_USER, 'make', '-C', target_dir, 'build',
156 'PIP_SOURCE_DIR=%s' % os.path.join(target_dir, 'pip-cache')])
157
158
159def prune_payloads(keep):
160 for entry in os.listdir(PAYLOADS_DIR):
161 if entry in keep:
162 continue
163 entry_path = os.path.join(PAYLOADS_DIR, entry)
164 if os.path.isdir(entry_path):
165 hookenv.log('Purging old build in %s...' % entry_path)
166 shutil.rmtree(entry_path)
167
168
169def deploy_code(service_name):
93 make_srv_location()170 make_srv_location()
94171
172 current_build_label = None
173 if os.path.islink(CODE_DIR):
174 current_build_label = os.path.basename(os.path.realpath(CODE_DIR))
175 elif os.path.isdir(os.path.join(CODE_DIR, '.bzr')):
176 log_output = subprocess.check_output(
177 ['bzr', 'log', '-rlast:', '--show-ids', CODE_DIR])
178 for line in log_output.splitlines():
179 if line.startswith('revision-id: '):
180 current_build_label = line[len('revision-id: '):]
181 desired_build_label = config['build_label']
182 if not desired_build_label:
183 if current_build_label is not None:
184 hookenv.log(
185 'No desired build label, but build %s is already deployed' %
186 current_build_label)
187 # We're probably upgrading from a charm that used old-style code
188 # assets, so make sure we at least have a virtualenv available
189 # from the current preferred location.
190 if not os.path.isdir(VENV_DIR) and os.path.isdir(OLD_VENV_DIR):
191 os.symlink(OLD_VENV_DIR, VENV_DIR)
192 return
193 else:
194 raise AssertionError('Build label unset, so cannot deploy code')
195 if current_build_label == desired_build_label:
196 hookenv.log('Build %s already deployed' % desired_build_label)
197 return
198 hookenv.log('Deploying build %s...' % desired_build_label)
199
95 # Copy source archive200 # Copy source archive
96 archive_path = os.path.join(BASE_DIR, 'turnip.tar.gz')201 archive_path = os.path.join(PAYLOADS_DIR, desired_build_label + '.tar.gz')
97202 object_name = os.path.join(desired_build_label, CODE_TARBALL)
98 with open(os.path.join(CHARM_FILES_DIR, 'turnip.tar.gz')) as file:203
99 host.write_file(archive_path, file.read(), perms=0o644)204 try:
100205 if config['swift_container_name']:
101 # Unpack source206 swift_creds = get_swift_creds(config)
102 archive.extract_tarfile(archive_path, CODE_DIR)207 swift_container = config['swift_container_name']
103 os.chown(208 swift_fetch(
104 CODE_DIR,209 os.path.join('turnip-builds', object_name), archive_path,
105 pwd.getpwnam(CODE_USER).pw_uid, grp.getgrnam(CODE_GROUP).gr_gid)210 container=swift_container, **swift_creds)
106 host.lchownr(CODE_DIR, CODE_USER, CODE_GROUP)211 else:
212 with open(os.path.join(CHARM_FILES_DIR, object_name)) as file:
213 host.write_file(archive_path, file.read(), perms=0o644)
214
215 # Unpack source
216 target_dir = os.path.join(PAYLOADS_DIR, desired_build_label)
217 if os.path.isdir(target_dir):
218 shutil.rmtree(target_dir)
219 archive.extract_tarfile(archive_path, target_dir)
220 os.chown(
221 target_dir,
222 pwd.getpwnam(CODE_USER).pw_uid, grp.getgrnam(CODE_GROUP).gr_gid)
223 host.lchownr(target_dir, CODE_USER, CODE_GROUP)
224
225 install_python_packages(target_dir)
226
227 if not os.path.islink(CODE_DIR) and os.path.isdir(CODE_DIR):
228 old_payload_dir = os.path.join(PAYLOADS_DIR, current_build_label)
229 if os.path.exists(old_payload_dir):
230 shutil.rmtree(CODE_DIR)
231 else:
232 os.rename(CODE_DIR, old_payload_dir)
233 symlink_force(
234 os.path.relpath(target_dir, os.path.dirname(CODE_DIR)), CODE_DIR)
235 prune_payloads([desired_build_label, current_build_label])
236 finally:
237 unlink_force(archive_path)
107238
108239
109def install_packages(service_name):240def install_packages(service_name):
@@ -112,26 +243,6 @@
112 fetch.apt_install(REQUIRED_PACKAGES, fatal=True)243 fetch.apt_install(REQUIRED_PACKAGES, fatal=True)
113244
114245
115def install_python_packages(service_name):
116 hookenv.log('Installing Python dependencies...')
117 pip_cache = os.path.join(CHARM_FILES_DIR, 'pip-cache')
118 code_reqs = os.path.join(CODE_DIR, 'requirements.txt')
119 deploy_reqs = os.path.join(hookenv.charm_dir(), 'deploy-requirements.txt')
120
121 pip_bin = os.path.join(VENV_DIR, 'bin', 'pip')
122
123 subprocess.call([
124 'sudo', '-u', CODE_USER, 'virtualenv', '--system-site-packages',
125 VENV_DIR])
126 subprocess.check_call([
127 'sudo', '-u', CODE_USER, pip_bin, 'install', '--no-index',
128 '--find-links={}'.format(pip_cache), '-r', code_reqs,
129 '-r', deploy_reqs])
130 subprocess.check_call([
131 'sudo', '-u', CODE_USER, pip_bin, 'install', '--no-deps',
132 '-e', CODE_DIR])
133
134
135def write_ssh_keys(service_name):246def write_ssh_keys(service_name):
136 if PUBLIC_KEY and PRIVATE_KEY:247 if PUBLIC_KEY and PRIVATE_KEY:
137 keys = [248 keys = [
@@ -302,7 +413,7 @@
302413
303414
304def install_nrpe_scripts(service_name):415def install_nrpe_scripts(service_name):
305 src = os.path.join(CHARM_FILES_DIR, "nrpe")416 src = os.path.join(CHARM_SCRIPTS_DIR, "nrpe")
306 dst = "/usr/local/lib/nagios/plugins"417 dst = "/usr/local/lib/nagios/plugins"
307 if not os.path.exists(dst):418 if not os.path.exists(dst):
308 os.makedirs(dst)419 os.makedirs(dst)
309420
=== modified file 'hooks/services.py'
--- hooks/services.py 2015-05-22 15:42:11 +0000
+++ hooks/services.py 2015-11-02 11:16:19 +0000
@@ -34,8 +34,8 @@
34 actions.execd_preinstall('turnip')34 actions.execd_preinstall('turnip')
35 actions.install_packages('turnip')35 actions.install_packages('turnip')
36 actions.create_users('turnip')36 actions.create_users('turnip')
37 actions.unpack_source('turnip')37 if hookenv.hook_name() in ('install', 'upgrade-charm', 'config-changed'):
38 actions.install_python_packages('turnip')38 actions.deploy_code('turnip')
3939
40 extra_requirements = []40 extra_requirements = []
4141
4242
=== renamed directory 'files/nrpe' => 'scripts/nrpe'
=== modified file 'templates/turnip-httpserver.conf.j2'
--- templates/turnip-httpserver.conf.j2 2015-04-26 22:33:55 +0000
+++ templates/turnip-httpserver.conf.j2 2015-11-02 11:16:19 +0000
@@ -4,7 +4,7 @@
4setuid {{ user }}4setuid {{ user }}
5setgid {{ group }}5setgid {{ group }}
66
7env PYTHON_HOME={{ base_dir }}/venv7env PYTHON_HOME={{ base_dir }}/code/env
88
9start on runlevel [2345]9start on runlevel [2345]
10stop on runlevel [016]10stop on runlevel [016]
1111
=== modified file 'templates/turnip-packbackendserver.conf.j2'
--- templates/turnip-packbackendserver.conf.j2 2015-04-26 22:33:55 +0000
+++ templates/turnip-packbackendserver.conf.j2 2015-11-02 11:16:19 +0000
@@ -4,7 +4,7 @@
4setuid {{ user }}4setuid {{ user }}
5setgid {{ group }}5setgid {{ group }}
66
7env PYTHON_HOME={{ base_dir }}/venv7env PYTHON_HOME={{ base_dir }}/code/env
88
9start on runlevel [2345]9start on runlevel [2345]
10stop on runlevel [016]10stop on runlevel [016]
1111
=== modified file 'templates/turnip-packfrontendserver.conf.j2'
--- templates/turnip-packfrontendserver.conf.j2 2015-04-26 22:33:55 +0000
+++ templates/turnip-packfrontendserver.conf.j2 2015-11-02 11:16:19 +0000
@@ -4,7 +4,7 @@
4setuid {{ user }}4setuid {{ user }}
5setgid {{ group }}5setgid {{ group }}
66
7env PYTHON_HOME={{ base_dir }}/venv7env PYTHON_HOME={{ base_dir }}/code/env
88
9start on runlevel [2345]9start on runlevel [2345]
10stop on runlevel [016]10stop on runlevel [016]
1111
=== modified file 'templates/turnip-sshserver.conf.j2'
--- templates/turnip-sshserver.conf.j2 2015-04-26 22:33:55 +0000
+++ templates/turnip-sshserver.conf.j2 2015-11-02 11:16:19 +0000
@@ -4,7 +4,7 @@
4setuid {{ user }}4setuid {{ user }}
5setgid {{ group }}5setgid {{ group }}
66
7env PYTHON_HOME={{ base_dir }}/venv7env PYTHON_HOME={{ base_dir }}/code/env
88
9start on runlevel [2345]9start on runlevel [2345]
10stop on runlevel [016]10stop on runlevel [016]
1111
=== modified file 'templates/turnip-virtserver.conf.j2'
--- templates/turnip-virtserver.conf.j2 2015-04-26 22:33:55 +0000
+++ templates/turnip-virtserver.conf.j2 2015-11-02 11:16:19 +0000
@@ -4,7 +4,7 @@
4setuid {{ user }}4setuid {{ user }}
5setgid {{ group }}5setgid {{ group }}
66
7env PYTHON_HOME={{ base_dir }}/venv7env PYTHON_HOME={{ base_dir }}/code/env
88
9start on runlevel [2345]9start on runlevel [2345]
10stop on runlevel [016]10stop on runlevel [016]

Subscribers

People subscribed via source and target branches

to all changes: