Merge lp:~mattyw/charms/trusty/mongodb/auth_experiment into lp:charms/trusty/mongodb

Proposed by Matthew Williams
Status: Work in progress
Proposed branch: lp:~mattyw/charms/trusty/mongodb/auth_experiment
Merge into: lp:charms/trusty/mongodb
Diff against target: 272 lines (+186/-7)
6 files modified
.bzrignore (+1/-0)
charm-helpers-sync.yaml (+1/-0)
hooks/charmhelpers/payload/__init__.py (+1/-0)
hooks/charmhelpers/payload/execd.py (+50/-0)
hooks/hooks.py (+61/-7)
tests/100-auth.test (+72/-0)
To merge this branch: bzr merge lp:~mattyw/charms/trusty/mongodb/auth_experiment
Reviewer Review Type Date Requested Status
Adam Israel (community) Needs Fixing
Review Queue (community) automated testing Needs Fixing
charmers Pending
Review via email: mp+240230@code.launchpad.net

Commit message

If auth is enabled we will create a user and password for the database the relation asks to use

Description of the change

If auth is enabled we will create a user and password for the database the relation asks to use

To post a comment you must log in.
Revision history for this message
Ryan Beisner (1chb1n) wrote :

UOSCI bot says:
charm_lint_check #822 trusty-mongodb for mattyw mp240230
    LINT FAIL: lint-check missing

LINT Results not found.
Build: http://10.98.191.181:8080/job/charm_lint_check/822/

Revision history for this message
Ryan Beisner (1chb1n) wrote :

UOSCI bot says:
charm_unit_test #630 trusty-mongodb for mattyw mp240230
    UNIT FAIL: unit-test missing

UNIT Results not found.
Build: http://10.98.191.181:8080/job/charm_unit_test/630/

Revision history for this message
Review Queue (review-queue) wrote :

This items has failed automated testing! Results available here http://reports.vapour.ws/charm-tests/charm-bundle-test-5008-results

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This items has failed automated testing! Results available here http://reports.vapour.ws/charm-tests/charm-bundle-test-10375-results

review: Needs Fixing (automated testing)
Revision history for this message
Adam Israel (aisrael) wrote :

Thanks for the work on this. I had a chance to review this proposal today, and unfortunately it's not merging cleanly with trunk. If you could fix that, I'd be happy to take another look.

Thanks!

review: Needs Fixing
Revision history for this message
David Britton (dpb) wrote :

Marking as Needs Fixing so it's clear this needs a bit of work, thanks!

Unmerged revisions

55. By Matthew Williams

fixes to the test

54. By Matthew Williams

mongo auth changes for trusty

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2014-07-10 18:38:27 +0000
3+++ .bzrignore 2014-10-31 09:31:58 +0000
4@@ -1,3 +1,4 @@
5 .git
6 bin/*
7 scripts/charm-helpers-sync.py
8+exec.d/*
9
10=== modified file 'charm-helpers-sync.yaml'
11--- charm-helpers-sync.yaml 2014-04-11 20:55:42 +0000
12+++ charm-helpers-sync.yaml 2014-10-31 09:31:58 +0000
13@@ -3,3 +3,4 @@
14 include:
15 - core
16 - fetch
17+ - payload.execd
18
19=== added directory 'exec.d'
20=== added directory 'hooks/charmhelpers/payload'
21=== added file 'hooks/charmhelpers/payload/__init__.py'
22--- hooks/charmhelpers/payload/__init__.py 1970-01-01 00:00:00 +0000
23+++ hooks/charmhelpers/payload/__init__.py 2014-10-31 09:31:58 +0000
24@@ -0,0 +1,1 @@
25+"Tools for working with files injected into a charm just before deployment."
26
27=== added file 'hooks/charmhelpers/payload/execd.py'
28--- hooks/charmhelpers/payload/execd.py 1970-01-01 00:00:00 +0000
29+++ hooks/charmhelpers/payload/execd.py 2014-10-31 09:31:58 +0000
30@@ -0,0 +1,50 @@
31+#!/usr/bin/env python
32+
33+import os
34+import sys
35+import subprocess
36+from charmhelpers.core import hookenv
37+
38+
39+def default_execd_dir():
40+ return os.path.join(os.environ['CHARM_DIR'], 'exec.d')
41+
42+
43+def execd_module_paths(execd_dir=None):
44+ """Generate a list of full paths to modules within execd_dir."""
45+ if not execd_dir:
46+ execd_dir = default_execd_dir()
47+
48+ if not os.path.exists(execd_dir):
49+ return
50+
51+ for subpath in os.listdir(execd_dir):
52+ module = os.path.join(execd_dir, subpath)
53+ if os.path.isdir(module):
54+ yield module
55+
56+
57+def execd_submodule_paths(command, execd_dir=None):
58+ """Generate a list of full paths to the specified command within exec_dir.
59+ """
60+ for module_path in execd_module_paths(execd_dir):
61+ path = os.path.join(module_path, command)
62+ if os.access(path, os.X_OK) and os.path.isfile(path):
63+ yield path
64+
65+
66+def execd_run(command, execd_dir=None, die_on_error=False, stderr=None):
67+ """Run command for each module within execd_dir which defines it."""
68+ for submodule_path in execd_submodule_paths(command, execd_dir):
69+ try:
70+ subprocess.check_call(submodule_path, shell=True, stderr=stderr)
71+ except subprocess.CalledProcessError as e:
72+ hookenv.log("Error ({}) running {}. Output: {}".format(
73+ e.returncode, e.cmd, e.output))
74+ if die_on_error:
75+ sys.exit(e.returncode)
76+
77+
78+def execd_preinstall(execd_dir=None):
79+ """Run charm-pre-install for each module within execd_dir."""
80+ execd_run('charm-pre-install', execd_dir=execd_dir)
81
82=== added symlink 'hooks/database-relation-changed'
83=== target is u'hooks.py'
84=== modified file 'hooks/hooks.py'
85--- hooks/hooks.py 2014-08-20 23:48:43 +0000
86+++ hooks/hooks.py 2014-10-31 09:31:58 +0000
87@@ -41,8 +41,14 @@
88 Hooks,
89 )
90
91+from charmhelpers.core.host import (
92+ pwgen,
93+)
94+
95 from charmhelpers.core.hookenv import log as juju_log
96
97+from charmhelpers.payload.execd import execd_preinstall
98+
99 from charmhelpers.core.host import (
100 service,
101 )
102@@ -724,6 +730,8 @@
103 ###############################################################################
104 @hooks.hook('install')
105 def install_hook():
106+ juju_log('Begin install hook.')
107+ execd_preinstall()
108 juju_log("Installing mongodb")
109 add_source(config('source'), config('key'))
110 apt_update(fatal=True)
111@@ -889,23 +897,69 @@
112 return(retVal)
113
114
115-@hooks.hook('database-relation-joined')
116-def database_relation_joined():
117- juju_log("database_relation_joined")
118+@hooks.hook('database-relation-changed')
119+def database_relation_changed():
120+ juju_log("database_relation_changed")
121+ if config("auth"):
122+ database = relation_get("database")
123+ if database == '':
124+ juju_log("No database set in relationship")
125+ sys.exit(0)
126+
127+ juju_log("Setting up user & password")
128+ user = get_user_name(
129+ os.environ["JUJU_RELATION_ID"],
130+ os.environ["JUJU_REMOTE_UNIT"])
131+ config_data = config()
132+ user_key = "user-%s" % user
133+ if user_key in config_data.keys():
134+ juju_log("User already setup for relation %s" % user)
135+ else:
136+ juju_log("Creating new user for relation")
137+ password = pwgen()
138+ config_data[user_key] = True
139+
140+ set_user(database, user, password)
141+ relation_set(relation_id(),
142+ {
143+ 'user': user,
144+ 'password': password,
145+ })
146+ else:
147+ juju_log("auth is disabled, no need to setup user details")
148+
149 my_hostname = unit_get('public-address')
150 my_port = config('port')
151 my_replset = config('replicaset')
152 juju_log("my_hostname: %s" % my_hostname)
153 juju_log("my_port: %s" % my_port)
154 juju_log("my_replset: %s" % my_replset)
155- return(relation_set(relation_id(),
156+ relation_set(relation_id(),
157 {
158 'hostname': my_hostname,
159 'port': my_port,
160 'replset': my_replset,
161 'type': 'database',
162- }))
163-
164+ })
165+
166+def get_user_name(relid, remote_unit):
167+ def sanitize(s):
168+ s = s.replace(':', '_')
169+ s = s.replace('-', '_')
170+ s = s.replace('/', '_')
171+ s = s.replace('"', '_')
172+ s = s.replace("'", '_')
173+ return s
174+ components = [sanitize(c) for c in [relid, remote_unit]]
175+ return '_'.join(components)
176+
177+def set_user(db, user, password):
178+ juju_log("adding user to %s" % db)
179+ return (subprocess.check_call(['mongo', str(db), "--eval", 'db.addUser("%s", "%s", false)' %(str(user), str(password))]) == 0)
180+
181+@hooks.hook('database-relation-joined')
182+def database_relation_joined():
183+ juju_log("database_relation_joined")
184
185 @hooks.hook('replicaset-relation-joined')
186 def replica_set_relation_joined():
187@@ -989,7 +1043,7 @@
188 def data_relation_joined():
189 juju_log("data_relation_joined")
190
191- return(relation_set(
192+ return(relation_set(relation_id(),
193 {
194 'mountpoint': '/srv/juju/mongodb-data'
195 }))
196
197=== added file 'tests/100-auth.test'
198--- tests/100-auth.test 1970-01-01 00:00:00 +0000
199+++ tests/100-auth.test 2014-10-31 09:31:58 +0000
200@@ -0,0 +1,72 @@
201+#!/usr/bin/python3
202+
203+import amulet
204+
205+from pymongo import MongoClient
206+
207+d = amulet.Deployment(series="trusty")
208+
209+d.add('mongodb', charm="mongodb")
210+d.add("mongodb-auth-tester", charm="cs:~mattyw/precise/mongodb-auth-tester")
211+
212+d.configure('mongodb', {'auth': True})
213+d.configure("mongodb-auth-tester", {"database": "amuletAuthTester"})
214+
215+d.relate('mongodb:database', 'mongodb-auth-tester:database')
216+
217+d.expose('mongodb')
218+
219+try:
220+ d.setup(timeout=900)
221+ d.sentry.wait()
222+except amulet.helpers.TimeoutError:
223+ amulet.raise_status(amulet.SKIP, msg="Environment wasn't stood up in time")
224+except:
225+ raise
226+
227+
228+"""
229+assert_relation_settings asserts that the correct settings are made on the relations
230+"""
231+def assert_relation_settings():
232+ mongo_rel_data = d.sentry.unit['mongodb-auth-tester/0'].relation('database', "mongodb:database")
233+ print(mongo_rel_data)
234+ rel_data = d.sentry.unit['mongodb/0'].relation('database', "mongodb-auth-tester:database")
235+ print(rel_data)
236+ if "database" not in mongo_rel_data.keys():
237+ amulet.raise_status(amulet.SKIP, msg="consumer charm should have called relation-set database")
238+ for k in ["user", "password"]:
239+ if k not in rel_data.keys():
240+ amulet.raise_status(amulet.SKIP, msg="Expected key %s not found" % k)
241+
242+ if rel_data["user"] != "database_1_mongodb_auth_tester_0":
243+ amulet.raise_status(amulet.SKIP, msg="Incorrect user name %s" %rel_data["user"])
244+
245+ if rel_data["password"] == "":
246+ amulet.raise_status(amulet.SKIP, msg="No password set")
247+
248+
249+"""
250+assert_consumer_charm_can_connect asserts that given the information in the relationship
251+we can connect to mongodb
252+"""
253+def assert_consumer_charm_can_connect():
254+ mongo_rel_data = d.sentry.unit['mongodb-auth-tester/0'].relation('database', "mongodb:database")
255+ print(mongo_rel_data)
256+ rel_data = d.sentry.unit['mongodb/0'].relation('database', "mongodb-auth-tester:database")
257+ print(rel_data)
258+ hostname = rel_data["hostname"]
259+ port = rel_data["port"]
260+ username = rel_data["user"]
261+ password = rel_data["password"]
262+ database = mongo_rel_data["database"]
263+ uri = "mongodb://%s:%s@%s:%s/%s" % (username, password, hostname, port, database)
264+ client = MongoClient(uri)
265+ collection = client[database]["coll"]
266+ collection.insert({"amulet-test": True})
267+ count = collection.count()
268+ if count != 1:
269+ amulet.raise_status(amulet.SKIP, msg="Expected to find 1 item in db found %d" % count)
270+
271+assert_relation_settings()
272+assert_consumer_charm_can_connect()

Subscribers

People subscribed via source and target branches