24
23
CODE_DIR = BASE_DIR + '/code'
25
24
BUNDLE_DIR = BASE_DIR + '/bundle'
26
25
BUNDLE_ARCHIVE = BASE_DIR + '/bundle.tgz'
27
METEOR_CONFIG = BASE_DIR + '/.juju-config'
28
26
NODEJS_REPO = 'ppa:chris-lea/node.js'
29
27
PACKAGES = ['nodejs', 'build-essential', 'curl']
30
28
DOWNLOAD_CMD = 'curl http://install.meteor.com -o /tmp/meteor-install'
31
29
INSTALL_CMD = '/bin/sh /tmp/meteor-install'
35
"""A Juju config dictionary that can write itself to
36
disk (json) and track which values have changed since
40
def __init__(self, path=None):
41
"""Initialize a `hookenv.config(), with an optional
42
"previous" copy loaded from `path`.
45
self.cur_dict = hookenv.config()
49
self.load_previous(path)
51
def load_previous(self, path):
52
"""Load a previous copy of config so that current values
53
can be compares to previous values.
58
self.prev_dict = json.load(f)
62
def changed(self, key):
63
"""Return true if the value for this key has changed since
67
if self.prev_dict is None:
69
return self.prev_dict.get(key) != self.cur_dict.get(key)
71
def previous(self, key):
72
"""Return previous value for this key, or None if there
73
is no "previous" value.
77
return self.prev_dict.get(key)
80
def save(self, path=None):
81
"""Save this config to `path`, or to the path from which
82
it was loaded. This is a no-op of path is None or this
83
config was not loaded from a file.
85
Preserves items in prev_dict that do not exist in cur_dict.
88
path = path or self.path
92
for k, v in self.prev_dict.iteritems():
93
if k not in self.cur_dict:
95
with open(path, 'w') as f:
96
json.dump(self.cur_dict, f)
98
def get(self, key, default=None):
99
return self.cur_dict.get(key, default)
101
def __getitem__(self, key):
102
return self.cur_dict[key]
104
def __setitem__(self, key, val):
105
self.cur_dict[key] = val
108
32
def write_upstart(config):
109
33
upstart = textwrap.dedent("""\
110
34
description "Meteor app '{app_name}'"
206
130
}[config['repo-type']]
207
131
fetch.apt_install(pkg)
208
132
clone(config['repo-type'], config['repo-url'], CODE_DIR,
209
config['repo-revision'])
133
config['repo-revision'])
211
135
hookenv.log('Creating demo app')
212
subprocess.check_call(['mrt', 'create', '--example',
213
config['demo-app'], CODE_DIR])
136
subprocess.check_call(
137
['mrt', 'create', '--example', config['demo-app'], CODE_DIR])
216
140
def init_bundle(config):
230
154
shutil.copytree(os.path.join(CODE_DIR, 'bundle'), BUNDLE_DIR)
157
def init_dependencies(config):
158
"""Install dependencies for installed meteor app.
161
depends_dir = os.path.join(BUNDLE_DIR, 'programs', 'server')
162
if os.path.exists(depends_dir):
163
with chdir(depends_dir):
164
subprocess.call(['npm', 'install'])
233
167
@hooks.hook('install')
235
config = Config(METEOR_CONFIG)
169
config = hookenv.config()
237
171
host.adduser(USER, password='')
238
172
host.mkdir(BASE_DIR, owner=USER, group=USER)
253
187
init_code(config)
254
188
init_bundle(config)
189
init_dependencies(config)
256
191
hookenv.open_port(config['port'])
257
subprocess.check_call(['chown', '-R', '{user}:{user}'.format(user=USER),
192
subprocess.check_call(
193
['chown', '-R', '{user}:{user}'.format(user=USER), BASE_DIR])
260
195
config['mongo_url'] = ''
261
196
write_upstart(config)
263
197
# wait for mongodb to join before starting
266
200
@hooks.hook('config-changed')
267
201
def config_changed():
268
config = Config(METEOR_CONFIG)
202
config = hookenv.config()
269
203
os.environ['HOME'] = os.path.expanduser('~' + USER)
271
205
if config.changed('repo-url') or config.changed('demo-app'):
274
208
init_code(config)
275
209
elif config.changed('repo-revision') and config['repo-url']:
276
210
with chdir(CODE_DIR):
277
subprocess.check_call([config['repo-type'], 'pull',
211
subprocess.check_call(
212
[config['repo-type'], 'pull', config['repo-url']])
279
213
checkout(config['repo-type'], CODE_DIR, config['repo-revision'])
281
215
if (config.changed('repo-url') or
290
225
hookenv.close_port(config.previous('port'))
291
226
if hookenv.is_relation_made('website'):
292
227
hookenv.relation_set(
293
relation_id=config.previous('website-relation-id'),
228
relation_id=config.previous('website-relation-id'),
294
229
relation_settings={
295
230
'port': config['port'],
296
231
'hostname': hookenv.unit_private_ip()})
298
subprocess.check_call(['chown', '-R', '{user}:{user}'.format(user=USER),
233
subprocess.check_call(
234
['chown', '-R', '{user}:{user}'.format(user=USER), BASE_DIR])
301
236
if config.previous('mongo_url'):
302
237
mongo_host = config.previous('mongo_url').rsplit('/', 1)[0]
304
239
config['mongo_url'] = mongo_url
306
241
write_upstart(config)
307
config.save(METEOR_CONFIG)
311
245
@hooks.hook('mongodb-relation-changed')
312
246
def mongodb_relation_changed():
313
config = Config(METEOR_CONFIG)
247
config = hookenv.config()
314
248
db_host = hookenv.relation_get('private-address')
317
251
db_port = hookenv.relation_get('port') or '27017'
318
252
config['mongo_url'] = 'mongodb://{host}:{port}/{app_name}'.format(
319
host=db_host, port=db_port, app_name=config['app-name'])
253
host=db_host, port=db_port, app_name=config['app-name'])
320
254
write_upstart(config)
330
263
@hooks.hook('upgrade-charm')
331
264
def upgrade_charm():
332
265
hookenv.log('Upgrading Meteor')
333
config = Config(METEOR_CONFIG)
267
OLD_METEOR_CONFIG = BASE_DIR + '/.juju-config'
268
NEW_METEOR_CONFIG = os.path.join(hookenv.charm_dir(),
269
hookenv.Config.CONFIG_FILE_NAME)
271
if (os.path.exists(OLD_METEOR_CONFIG) and not
272
os.path.exists(NEW_METEOR_CONFIG)):
273
hookenv.log('Moving config from {} to {}'.format(
274
OLD_METEOR_CONFIG, NEW_METEOR_CONFIG))
275
shutil.move(OLD_METEOR_CONFIG, NEW_METEOR_CONFIG)
277
config = hookenv.config()
278
os.environ['HOME'] = os.path.expanduser('~' + USER)
280
hookenv.log('Upgrading nodejs')
282
fetch.apt_install(PACKAGES)
284
hookenv.log('Upgrading meteor/meteorite')
335
285
subprocess.check_call(DOWNLOAD_CMD.split())
336
286
subprocess.check_call(INSTALL_CMD.split())
287
subprocess.check_call('npm install -g meteorite'.split())
289
init_dependencies(config)
338
291
if host.service_running(SERVICE):
355
308
@hooks.hook('website-relation-joined')
356
309
def website_relation_joined():
357
config = Config(METEOR_CONFIG)
358
hookenv.relation_set(port=config['port'],
359
hostname=hookenv.unit_private_ip())
310
config = hookenv.config()
311
hookenv.relation_set(
312
port=config['port'], hostname=hookenv.unit_private_ip())
360
313
# save relation id so we can inform the remote side if we change ports
361
314
config['website-relation-id'] = os.environ['JUJU_RELATION_ID']
362
config.save(METEOR_CONFIG)
365
317
if __name__ == "__main__":