Merge lp:~fginther/charms/precise/ubuntu-ci-services-itself/rabbitmq-worker-lp into lp:~canonical-ci-engineering/charms/precise/ubuntu-ci-services-itself/lander

Proposed by Francis Ginther
Status: Rejected
Rejected by: Francis Ginther
Proposed branch: lp:~fginther/charms/precise/ubuntu-ci-services-itself/rabbitmq-worker-lp
Merge into: lp:~canonical-ci-engineering/charms/precise/ubuntu-ci-services-itself/lander
Diff against target: 300 lines (+266/-0) (has conflicts)
4 files modified
README.ex (+41/-0)
config.yaml (+44/-0)
hooks/hooks.py (+171/-0)
metadata.yaml (+10/-0)
Conflict adding file config.yaml.  Moved existing file to config.yaml.moved.
Conflict adding file hooks.  Moved existing file to hooks.moved.
Conflict adding file metadata.yaml.  Moved existing file to metadata.yaml.moved.
To merge this branch: bzr merge lp:~fginther/charms/precise/ubuntu-ci-services-itself/rabbitmq-worker-lp
Reviewer Review Type Date Requested Status
Canonical CI Engineering Pending
Review via email: mp+203930@code.launchpad.net

Commit message

Add the ability to pass launchpad credentials to the worker node, uses a base64 encoded file called "launchpad.credentials".

Description of the change

Add the ability to pass launchpad credentials to the worker node, uses a base64 encoded file called "launchpad.credentials".

To post a comment you must log in.
Revision history for this message
Andy Doan (doanac) wrote :

could we use "unit_config" for this also and include lpcredentials as a value in it? or alternatively just use the unit_config we have now which I think includes all the information needed to create this credentials file?

NOTE: I've proposed moving this charm:

 https://code.launchpad.net/~doanac/ubuntu-ci-services-itself/rabbitwork-local/+merge/203830

you probably want to rebase this MP on to that

Revision history for this message
Francis Ginther (fginther) wrote :

Merge into the main project trunk and use unit-config.

Revision history for this message
Francis Ginther (fginther) wrote :

"NEED" to merge into the main project and use unit-config. Rejecting this MP.

Unmerged revisions

14. By Francis Ginther

Add import base64.

13. By Francis Ginther

Add some lp_creds debugging.

12. By Francis Ginther

Adjust file path to remove hidden hint.

11. By Francis Ginther

Add launchpa credentials file.

10. By Paul Larson

add support for installing pip packages

9. By Andy Doan

allow user/group for service to be configurable

8. By Andy Doan

update how amqp parameters are set

allow the worker to dynamically load the config from a file. If
you don't do this, you have to issue a "stop <service>; start <service>"
in order for upstart to detect a change from a bad config to a good
config

7. By Andy Doan

give a limit to the retries for the upstart job

6. By Andy Doan

fix quoting issue

5. By Andy Doan

fix eol issue on relation_get()

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'README.ex'
--- README.ex 1970-01-01 00:00:00 +0000
+++ README.ex 2014-01-30 11:47:01 +0000
@@ -0,0 +1,41 @@
1Describe the intended usage of this charm and anything unique about how
2this charm relates to others here.
3
4This README will be displayed in the Charm Store, it should be either Markdown or RST. Ideal READMEs include instructions on how to use the charm, expected usage, and charm features that your audience might be interested in. For an example of a well written README check out Hadoop: http://jujucharms.com/charms/precise/hadoop
5
6Here's an example you might wish to template off of:
7
8Overview
9--------
10
11This charm provides (service) from (service homepage). Add a description here of what the service itself actually does.
12
13
14Usage
15-----
16
17Step by step instructions on using the charm:
18
19 juju deploy servicename
20
21and so on. If you're providing a web service or something that the end user needs to go to, tell them here, especially if you're deploying a service that might listen to a non-default port.
22
23You can then browse to http://ip-address to configure the service.
24
25Configuration
26-------------
27
28The configuration options will be listed on the charm store, however If you're making assumptions or opinionated decisions in the charm (like setting a default administrator password), you should detail that here so the user knows how to change it immediately, etc.
29
30
31Contact Information
32-------------------
33
34Though this will be listed in the charm store itself don't assume a user will know that, so include that information here:
35
36Author:
37Report bugs at: http://bugs.launchpad.net/charms/+source/charmname
38Location: http://jujucharms.com/charms/distro/charmname
39
40* Be sure to remove the templated parts before submitting to https://launchpad.net/charms for inclusion in the charm store.
41
042
=== added file 'config.yaml'
--- config.yaml 1970-01-01 00:00:00 +0000
+++ config.yaml 2014-01-30 11:47:01 +0000
@@ -0,0 +1,44 @@
1options:
2 branch:
3 type: string
4 description: "BZR branch the service lives in"
5 revno:
6 type: string
7 description: "Revision or tag to branch from"
8 packages:
9 type: string
10 description: "Packages required for this service"
11 pip-packages:
12 type: string
13 description: "Pip packages required for this service"
14 install_root:
15 type: string
16 description: "The root directory the service will be installed in"
17 default: "/srv/"
18 main:
19 type: string
20 description: "The worker script to run as a service. Can include paths relative to local bzr directory"
21
22 uid:
23 type: string
24 default: nobody
25 description: User to run service as
26 gid:
27 type: string
28 default: nogroup
29 description: Group to run service as
30
31 # required for rabbitmq-server charm:
32 amqp-user:
33 type: string
34 default: 'workerbee'
35 description: The user to log into the rabbitMQ server.
36 amqp-vhost:
37 type: string
38 default: '/'
39 description: The vhost in the rabbitMQ server.
40
41 lp_creds:
42 type: string
43 default: ''
44 description: The launchpad credenetials file.
045
=== renamed file 'config.yaml' => 'config.yaml.moved'
=== added directory 'hooks'
=== renamed directory 'hooks' => 'hooks.moved'
=== added symlink 'hooks/amqp-relation-broken'
=== target is u'hooks.py'
=== added symlink 'hooks/amqp-relation-changed'
=== target is u'hooks.py'
=== added symlink 'hooks/amqp-relation-joined'
=== target is u'hooks.py'
=== added symlink 'hooks/config-changed'
=== target is u'hooks.py'
=== added file 'hooks/hooks.py'
--- hooks/hooks.py 1970-01-01 00:00:00 +0000
+++ hooks/hooks.py 2014-01-30 11:47:01 +0000
@@ -0,0 +1,171 @@
1#!/usr/bin/env python
2
3import base64
4import os
5import json
6import subprocess
7import sys
8import textwrap
9
10
11def juju_info(msg):
12 subprocess.check_call(['juju-log', '-l', 'INFO', msg])
13 pass
14
15
16def _config():
17 output = subprocess.check_output(['config-get', '--format=json'])
18 return json.loads(output)
19
20
21def _relation_get(key):
22 return subprocess.check_output(['relation-get', key]).strip()
23
24
25def _relation_set(keyvalues, relation_id=None):
26 args = ['relation-set']
27 if relation_id:
28 args.extend(['-r', relation_id])
29 args.extend(["{}={}".format(k, v or '') for k, v in keyvalues.items()])
30 subprocess.check_call(args)
31
32
33def _service_name(config):
34 unit = os.environ['JUJU_UNIT_NAME'].split('/')[0]
35 for x in (':', '-', '/', '"', "'"):
36 unit = unit.replace(x, '_')
37 return unit
38
39
40def _service_dir(config):
41 return os.path.join(config['install_root'], _service_name(config))
42
43
44def pip_install(package):
45 cmd_line = ['pip', 'install', '-b', '/tmp/']
46 if package.startswith('svn+') or package.startswith('git+') or \
47 package.startswith('hg+') or package.startswith('bzr+'):
48 cmd_line.append('-e')
49 cmd_line.append(package)
50 return(subprocess.call(cmd_line))
51
52
53def config_changed(config):
54 lp_creds = config.get('lp_creds', None)
55 juju_info('lp_creds (raw): %s' % (lp_creds))
56 if lp_creds:
57 lp_creds = str(base64.b64decode(lp_creds))
58 juju_info('lp_creds (decoded): %s' % (lp_creds))
59 with open(os.path.join(_service_dir(config), 'launchpad.credentials'),
60 'w') as f:
61 f.write(lp_creds)
62
63
64def install(config):
65 pkgs = [x for x in config.get('packages', '').split(' ') if x]
66 pkgs.append('python-amqplib')
67 pkgs.append('python-pip')
68 pkgs.append('bzr')
69
70 juju_info('installing apt packages...')
71 subprocess.check_call(['apt-get', 'install', '-y', '-q'] + pkgs)
72
73 pip_pkgs = [x for x in config.get('pip-packages', '').split(' ') if x]
74 if pip_pkgs:
75 juju_info('installing pip packages...')
76 for package in pip_pkgs:
77 pip_install(package.strip())
78
79 juju_info('grabbing service from bzr...')
80 args = ['bzr', 'branch']
81 rev = config.get('revno', '')
82 if rev:
83 args.extend(['-r', rev])
84 args.append(config['branch'])
85 args.append(_service_dir(config))
86 subprocess.check_call(args)
87
88
89def _create_upstart(config):
90 template = textwrap.dedent('''
91 #--------------------------------------------------------------
92 # This file is managed by the rabbitmq-worker Juju charm
93 #--------------------------------------------------------------
94 description "starts a rabbitmq worker"
95 start on (local-filesystems and net-device-up IFACE=eth0)
96 stop on runlevel [!12345]
97
98 # If the process quits unexpectadly trigger a respawn
99 # give it 15 chances with a 5 second delay in between retries
100 respawn limit 15 5
101
102 setuid {uid}
103 setgid {gid}
104 chdir {sdir}
105
106 exec {main}
107 ''')
108 params = {
109 'main': config['main'],
110 'sdir': _service_dir(config),
111 'uid': config.get('uid', 'nobody'),
112 'gid': config.get('gid', 'nogroup'),
113 }
114 with open('/etc/init/%s.conf' % _service_name(config), 'w') as f:
115 f.write(template.format(**params))
116 # need this so the file is ready for the subprocess call
117 f.flush()
118 os.fsync(f.fileno())
119
120
121def _create_amqp_config(config):
122 with open(os.path.join(_service_dir(config), 'amqp_config.py'), 'w') as f:
123 f.write('# DO NOT EDIT. Generated by restish charm hook\n')
124 f.write('AMQP_USER = "%s"\n' % config['amqp-user'])
125 f.write('AMQP_VHOST = "%s"\n' % config['amqp-vhost'])
126 f.write('AMQP_HOST = "%s"\n' % _relation_get('private-address'))
127 f.write('AMQP_PASSWORD = "%s"\n' % _relation_get('password'))
128
129
130def amqp_relation_joined(config):
131 _relation_set({
132 'username': config['amqp-user'],
133 'vhost': config['amqp-vhost'],
134 })
135
136
137def amqp_relation_changed(config):
138 _create_upstart(config)
139 _create_amqp_config(config)
140 try:
141 subprocess.check_call(['/sbin/restart', _service_name(config)])
142 except subprocess.CalledProcessError:
143 # if it wasn't running, restart fails, so just try to start
144 subprocess.check_call(['/sbin/start', _service_name(config)])
145
146
147def amqp_relation_broken(config):
148 subprocess.call(['/sbin/stop', _service_name(config)])
149 os.unlink(os.path.join(_service_dir(config), 'amqp_config.py'))
150
151
152def main():
153 hook = os.path.basename(sys.argv[0])
154 juju_info("Running hook: %s" % hook)
155
156 hook_py = hook.replace('-', '_')
157 funcs = globals()
158 if hook_py not in funcs:
159 print("Unknown hook: %s" % hook)
160 return 1
161
162 config = _config()
163 try:
164 return funcs[hook_py](config)
165 except subprocess.CalledProcessError as e:
166 juju_info('Error running: %s: %s' % (e.cmd, e.output))
167 return e.returncode
168
169
170if __name__ == '__main__':
171 exit(main())
0172
=== added symlink 'hooks/install'
=== target is u'hooks.py'
=== added file 'metadata.yaml'
--- metadata.yaml 1970-01-01 00:00:00 +0000
+++ metadata.yaml 2014-01-30 11:47:01 +0000
@@ -0,0 +1,10 @@
1name: rabbitmq-worker
2summary: Deploy a python-restish service
3maintainer: Andy Doan <andy.doan@ubuntu.com>
4description: |
5 Deploys a rabbitmq worker from a given bzr branch.
6categories:
7 - app-servers
8requires:
9 amqp:
10 interface: rabbitmq
011
=== renamed file 'metadata.yaml' => 'metadata.yaml.moved'

Subscribers

People subscribed via source and target branches