Merge ~kotodama/ubuntu-mirror-charm:role-map-hostname-all into ubuntu-mirror-charm:master

Proposed by Loïc Gomez
Status: Merged
Approved by: Loïc Gomez
Approved revision: a6ea749f3957f98587f139cd393b2e1b239ab635
Merged at revision: 5c839e9c5f767e26a35a3913c519047d8dee9517
Proposed branch: ~kotodama/ubuntu-mirror-charm:role-map-hostname-all
Merge into: ubuntu-mirror-charm:master
Diff against target: 251 lines (+94/-28)
2 files modified
hooks/hooks.py (+51/-28)
tests/unit/test_charm.py (+43/-0)
Reviewer Review Type Date Requested Status
Tom Haddon Approve
Canonical IS Reviewers Pending
Review via email: mp+408806@code.launchpad.net

Commit message

Add <<all>> hostname placeholder in role-maps

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Loïc Gomez (kotodama) wrote :

$ make test
[...]
_________________ summary _________________
  lint: commands succeeded
  congratulations :)
[...]
tests/unit/test_charm.py::TestUbuntuMirrorCharm::test_get_roles PASSED [ 47%]
tests/unit/test_charm.py::TestUbuntuMirrorCharm::test_has_roles PASSED [ 52%]
[...]
======= 21 passed in 3.48 seconds =========
_________________ summary _________________
  unit: commands succeeded
  congratulations :)

Revision history for this message
Tom Haddon (mthaddon) wrote :

Some comments inline

Revision history for this message
Loïc Gomez (kotodama) wrote :

Thank you, updated the code with suggested changes.
Also realized we do not need hostname parameter in get_role_config anymore, removed it.

Revision history for this message
Tom Haddon (mthaddon) wrote :

LGTM

review: Approve
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 5c839e9c5f767e26a35a3913c519047d8dee9517

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/hooks/hooks.py b/hooks/hooks.py
2index 24a6cc8..1b91c90 100755
3--- a/hooks/hooks.py
4+++ b/hooks/hooks.py
5@@ -319,6 +319,29 @@ def get_ssh_keyfile(username):
6 return newkeyfile
7
8
9+def has_roles(conf, hostname):
10+ """Returns true if there are specific roles for `hostname` hostname
11+ or global roles defined for <<all>> hostnames"""
12+ roles = conf.roles()
13+ if hostname in roles or '<<all>>' in roles:
14+ return True
15+ return False
16+
17+
18+def get_roles(conf, hostname):
19+ """Return the list of specific roles for `hostname` hostname.
20+ If there are none, but global roles are defined for <<all>> hostname,
21+ return these instead of an empty list."""
22+ if not has_roles(conf, hostname):
23+ return []
24+
25+ roles = conf.roles()
26+ if hostname in roles:
27+ return roles[hostname]
28+ else:
29+ return roles['<<all>>']
30+
31+
32 #
33 # Set up cronned rsyncing to pull archive contents from masters
34 # Creation of any necessary destination directories is the responsibility
35@@ -338,16 +361,14 @@ def configure_rsync_client(conf, hostname): # noqa: C901
36 os.chmod(mirror_logdir, 0o755)
37 os.chown(mirror_logdir, mirror_userinfo.pw_uid, mirror_userinfo.pw_gid)
38
39- roles = conf.roles()
40- if hostname not in roles:
41+ if not has_roles(conf, hostname):
42 log("CHARM: hostname {} not found in role_map".format(hostname))
43- roles[hostname] = []
44
45 enabled_configs = [f for f in os.listdir(mirror_confdir) if os.path.isfile(os.path.join(mirror_confdir, f))]
46 wanted_configs = []
47
48 cron = juju_header()
49- for role in roles[hostname]:
50+ for role in get_roles(conf, hostname):
51 try:
52 mirror = conf.mirror_map(role)
53 if mirror["unsafe"]:
54@@ -401,13 +422,12 @@ def configure_rsync_client(conf, hostname): # noqa: C901
55 def configure_rsync_server(conf, hostname):
56 log("CHARM: Configuring rsync server")
57
58- roles = conf.roles()
59- if hostname not in roles:
60+ if not has_roles(conf, hostname):
61 log("CHARM: hostname {} not found in role_map - not configuring rsync".format(hostname))
62 return
63
64 targets = {}
65- for role in roles[hostname]:
66+ for role in get_roles(conf, hostname):
67 try:
68 mirror = conf.mirror_map(role)
69 if mirror["unsafe"]:
70@@ -474,9 +494,11 @@ def configure_rsync_server(conf, hostname):
71 log("CHARM: Finished configuring rsync server")
72
73
74-def get_role_config(roles, hostname, role):
75- if isinstance(roles[hostname], types.DictType) and roles[hostname][role]:
76- return roles[hostname][role]
77+def get_role_config(host_roles, role):
78+ """Get the role configuration for `role` as defined in host_roles
79+ or return an empty dict if not defined"""
80+ if isinstance(host_roles, types.DictType) and host_roles[role]:
81+ return host_roles[role]
82 return {}
83
84
85@@ -496,23 +518,23 @@ def configure_apache(conf, hostname): # noqa: C901
86 mkdir(apache_logdir)
87 os.chmod(apache_logdir, 0o755)
88
89- roles = conf.roles()
90- if hostname not in roles:
91+ if not has_roles(conf, hostname):
92 log("CHARM: hostname {} not found in role_map - not configuring apache".format(hostname))
93 return
94
95 wanted_vhosts = []
96-
97 all_addresses = set()
98 all_ports = set()
99+ host_roles = get_roles(conf, hostname)
100+
101 # Create vhost configs for all active roles
102- for role in roles[hostname]:
103+ for role in host_roles:
104 try:
105 mirror = conf.mirror_map(role)
106 except ValueError:
107 log("CHARM: Missing details - skipping {}".format(role))
108 else:
109- role_config = get_role_config(roles, hostname, role)
110+ role_config = get_role_config(host_roles, role)
111 if mirror['unsafe'] and 'addresses' not in role_config:
112 log("CHARM: {} has no addresses defined - not configuring".format(role))
113 continue
114@@ -776,11 +798,12 @@ def extract_ssh_public_key(key):
115
116 def make_triggers(conf, hostname):
117 """Return this unit's triggers in a dict, keyed by role."""
118- roles = conf.roles()
119
120- triggers = {}
121+ if not has_roles(conf, hostname):
122+ return {}
123
124- for role in roles[hostname]:
125+ triggers = {}
126+ for role in get_roles(conf, hostname):
127 try:
128 mirror = conf.mirror_map(role)
129 if mirror["unsafe"]:
130@@ -802,8 +825,7 @@ def make_triggers(conf, hostname):
131 # Set up any external ssh sync triggers
132 #
133 def configure_triggers(conf, hostname):
134- roles = conf.roles()
135- if hostname not in roles:
136+ if not has_roles(conf, hostname):
137 return
138
139 triggers = make_triggers(conf, hostname)
140@@ -867,10 +889,10 @@ def generate_disk_check(conf, path):
141 log("CHARM: Adding disk check for {}".format(path))
142
143
144-def generate_global_rsync_check(conf, hostname, roles):
145+def generate_global_rsync_check(conf, hostname, host_roles):
146 # One extra for the rsync daemon itself, another two
147 # per role for pulling from syncproxy, one more for luck.
148- rsync_crit = conf.rsync_max_connections() + 1 + len(roles[hostname]) * 2 + 1
149+ rsync_crit = conf.rsync_max_connections() + 1 + len(host_roles) * 2 + 1
150 rsync_warn = conf.rsync_max_connections() + 1 # all client slots used
151
152 file_from_template(
153@@ -917,15 +939,16 @@ def configure_nrpe(conf, hostname): # noqa: C901
154 log("CHARM: Essential nagios dirs missing - skipping Nagios checks")
155 return
156
157- wanted_checks = []
158 # Add Nagios role checks
159- roles = conf.roles()
160+ wanted_checks = []
161 paths = []
162- if hostname in roles:
163+
164+ if has_roles(conf, hostname):
165+ host_roles = get_roles(conf, hostname)
166 # We generate a single check here, and delete the legacy per-role checks later.
167- generate_global_rsync_check(conf, hostname, roles)
168+ generate_global_rsync_check(conf, hostname, host_roles)
169 tmpl_data = {}
170- for role in roles[hostname]:
171+ for role in host_roles:
172 try:
173 mirror = conf.mirror_map(role)
174 except ValueError:
175@@ -934,7 +957,7 @@ def configure_nrpe(conf, hostname): # noqa: C901
176
177 paths.append(mirror['path'])
178 tmpl_data["name"] = mirror["name"]
179- role_config = get_role_config(roles, hostname, role)
180+ role_config = get_role_config(host_roles, role)
181 addresses = role_config.get('addresses', [])
182 if not addresses:
183 ipaddr = get_default_address(socket.AF_INET)
184diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py
185index 20582cb..ba0e688 100644
186--- a/tests/unit/test_charm.py
187+++ b/tests/unit/test_charm.py
188@@ -26,6 +26,8 @@ from hooks import (
189 merge_shared_key_triggers,
190 mountpoint_of,
191 remove_bind_mount,
192+ get_roles,
193+ has_roles
194 )
195
196 from utils import (
197@@ -80,6 +82,19 @@ MERGED_TRIGGERS = {
198
199 MERGEABLE_TRIGGERS_SSH_AUTHORIZED_KEYS = 'no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,command="( /srv/ubuntu-mirror/bin/mirror-1stage.sh releases ; /srv/ubuntu-mirror/bin/mirror-1stage.sh cdimage ) &",from="91.189.90.151,91.189.89.127,10.22.96.61,10.22.96.161" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA7TIXsws/s48o4N1MA+TpaudHS8XVF79JujYQGTumroZZqbsr04lbg1FaJ9HyS9P31e8lP2bmB5rexJaMyfAxDTV+DtrHXmeLi/XcGSOXoliOa/j3eQmdZyYme9z+Tfp8s3S6nxuDP3BRKkOuIzOPgPO5fr8AH7y83v3fyHi+H0sbm6agX2b+xV9oP8+DEgSxPNfZEVTgpdJ7/ULwAl68mWQW0w6VTVx0CMYF+cvcorcZMGmy+THzk8WH2XRalA5HqeiBtS7vJ+yFHJ5WbQOEUeR6gHRcrogR4Yd10eFWUNAFyvt53zF7W91PEgnJWjcCUN/uctybAFSgDK1UB6TPkQ== cjwatson@little\n' # noqa: E501
200
201+MERGEABLE_ROLES_CONFIG = {
202+ 'role_map': json.dumps({
203+ 'localhost': ['cdimage', 'releases'],
204+ })
205+}
206+
207+MERGEABLE_ROLES_ALL_CONFIG = {
208+ 'role_map': json.dumps({
209+ 'localhost': ['cdimage', 'releases'],
210+ '<<all>>': ['cdimage', 'releases'],
211+ })
212+}
213+
214 XINETD_CONFIG = {
215 'rsync_use_xinetd': True,
216 'role_map': json.dumps({
217@@ -150,6 +165,34 @@ class TestUbuntuMirrorCharm(unittest.TestCase):
218 configure_triggers(conf, 'localhost')
219 _write_file.assert_called_with(_get_ssh_keyfile.return_value, MERGEABLE_TRIGGERS_SSH_AUTHORIZED_KEYS)
220
221+ @patch('Config.config')
222+ def test_has_roles(self, _config):
223+ config = CallableDict(self.default_config)
224+ config.update(MERGEABLE_ROLES_CONFIG)
225+ _config.side_effect = config # i.e. we want our CallableDict to supply the charm config
226+ conf = Config.Config()
227+ self.assertTrue(has_roles(conf, 'localhost'))
228+ self.assertFalse(has_roles(conf, 'invalidhostname'))
229+ config = CallableDict(self.default_config)
230+ config.update(MERGEABLE_ROLES_ALL_CONFIG)
231+ _config.side_effect = config # i.e. we want our CallableDict to supply the charm config
232+ conf = Config.Config()
233+ self.assertTrue(has_roles(conf, 'invalidhostname'))
234+
235+ @patch('Config.config')
236+ def test_get_roles(self, _config):
237+ config = CallableDict(self.default_config)
238+ config.update(MERGEABLE_ROLES_CONFIG)
239+ _config.side_effect = config # i.e. we want our CallableDict to supply the charm config
240+ conf = Config.Config()
241+ self.assertEqual(get_roles(conf, 'localhost'), ['cdimage', 'releases'])
242+ self.assertEqual(get_roles(conf, 'invalidhostname'), [])
243+ config = CallableDict(self.default_config)
244+ config.update(MERGEABLE_ROLES_ALL_CONFIG)
245+ _config.side_effect = config # i.e. we want our CallableDict to supply the charm config
246+ conf = Config.Config()
247+ self.assertEqual(get_roles(conf, 'invalidhostname'), ['cdimage', 'releases'])
248+
249 def test_mountpoint_of_root(self):
250 """If the mountpoint of / isn't /, we're running in a weird environment."""
251 self.assertEqual(mountpoint_of('/'), '/')

Subscribers

People subscribed via source and target branches