Merge ~pjdc/ubuntu-mirror-charm/+git/ubuntu-mirror-charm:shared-triggers into ubuntu-mirror-charm:master

Proposed by Paul Collins
Status: Superseded
Proposed branch: ~pjdc/ubuntu-mirror-charm/+git/ubuntu-mirror-charm:shared-triggers
Merge into: ubuntu-mirror-charm:master
Diff against target: 182 lines (+86/-17)
7 files modified
.gitignore (+2/-0)
hooks/hooks.py (+50/-13)
hooks/utils.py (+11/-0)
requirements.txt (+0/-0)
tests/unit/requirements.txt (+6/-0)
tests/unit/test_charm.py (+13/-0)
tox.ini (+4/-4)
Reviewer Review Type Date Requested Status
Ubuntu Mirror Charm Maintainers Pending
Review via email: mp+388454@code.launchpad.net

This proposal has been superseded by a proposal from 2020-08-02.

Commit message

merge triggers where key is the same

To post a comment you must log in.

Unmerged commits

1dc2669... by Paul Collins

add test case for extract_ssh_public_key

7f14448... by Paul Collins

merge triggers where key is the same

d0f72d8... by Paul Collins

ignore compiled Python

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.gitignore b/.gitignore
2index 490cc43..526bc62 100644
3--- a/.gitignore
4+++ b/.gitignore
5@@ -1,5 +1,7 @@
6 *~
7 *.charm
8+*.pyc
9+*.pyo
10 .tox
11 .coverage
12 __pycache__
13diff --git a/hooks/hooks.py b/hooks/hooks.py
14index c4d3d47..c1d5a2a 100755
15--- a/hooks/hooks.py
16+++ b/hooks/hooks.py
17@@ -45,6 +45,10 @@ from charmhelpers.payload.execd import execd_run
18 from Cheetah.Template import Template
19 from Config import Config
20
21+from utils import (
22+ extract_ssh_public_key,
23+)
24+
25 hooks = Hooks()
26
27 apache_tls_settings = {
28@@ -565,17 +569,35 @@ def configure_vsftp(conf, hostname):
29 log("CHARM: Finished configuring vsftp")
30
31
32-#
33-# Set up any external ssh sync triggers
34-#
35-def configure_triggers(conf, hostname):
36+SSH_TRIGGER_TEMPLATE = 'no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,command="{command}",{trigger}'
37+
38+
39+# cdimage-builder.internal triggers releases and cdimage using the
40+# same key, so when this unit has both roles, sync both of them.
41+def merge_shared_key_triggers(conf, triggers):
42+ """Merge (in place) triggers that use the same ssh key."""
43+
44+ cdimage_key = extract_ssh_public_key(triggers.get('cdimage'))
45+ releases_key = extract_ssh_public_key(triggers.get('releases'))
46+ if cdimage_key and releases_key and cdimage_key == releases_key:
47+ releases = conf.mirror_map('releases')
48+ cdimage = conf.mirror_map('cdimage')
49+ command = '( {releases_command} releases ; {cdimage_command} cdimage ) &'.format(
50+ releases_command=os.path.join(conf.script_dir(), releases['command']),
51+ cdimage_command=os.path.join(conf.script_dir(), cdimage['command']),
52+ )
53+ triggers['releases'] = SSH_TRIGGER_TEMPLATE.format(
54+ command=command,
55+ trigger=releases['trigger'],
56+ )
57+ del(triggers['cdimage'])
58+
59+
60+def make_triggers(conf, hostname):
61+ """Return this unit's triggers in a dict, keyed by role."""
62 roles = conf.roles()
63- if hostname not in roles:
64- return
65
66- template = 'no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,command="${command}",${trigger}'
67- tmpl_data = {}
68- contents = ""
69+ triggers = {}
70
71 for role in roles[hostname]:
72 try:
73@@ -587,15 +609,30 @@ def configure_triggers(conf, hostname):
74 continue
75 else:
76 if mirror["trigger"]:
77- tmpl_data["command"] = "%s %s &" % (os.path.join(conf.script_dir(), mirror["command"]), role)
78- tmpl_data["trigger"] = mirror["trigger"]
79- contents += str(Template(template, tmpl_data)) + "\n"
80+ triggers[role] = SSH_TRIGGER_TEMPLATE.format(
81+ command="%s %s &" % (os.path.join(conf.script_dir(), mirror["command"]), role),
82+ trigger=mirror["trigger"],
83+ )
84+
85+ return triggers
86+
87+
88+#
89+# Set up any external ssh sync triggers
90+#
91+def configure_triggers(conf, hostname):
92+ roles = conf.roles()
93+ if hostname not in roles:
94+ return
95+
96+ triggers = make_triggers(conf, hostname)
97+ merge_shared_key_triggers(conf, triggers)
98
99 keyfile = get_ssh_keyfile(conf.mirror_user())
100 ssh_dir = os.path.dirname(keyfile)
101 if not os.path.isdir(ssh_dir):
102 mkdir(ssh_dir)
103- write_file(keyfile, contents)
104+ write_file(keyfile, '\n'.join(triggers.values()) + '\n')
105
106
107 def configure_nrpe(conf, hostname): # noqa: C901
108diff --git a/hooks/utils.py b/hooks/utils.py
109new file mode 100644
110index 0000000..2d10a21
111--- /dev/null
112+++ b/hooks/utils.py
113@@ -0,0 +1,11 @@
114+def extract_ssh_public_key(key):
115+ """Return the field after the field that begins with "ssh-", which
116+ should be the key itself. This function can therefore cope with
117+ fairly arbitrarily-decorated ssh keys, e.g. those with "from", etc."""
118+
119+ kelts = key.split(' ')
120+ while kelts:
121+ # This doesn't work for every type of key, but nobody should be using ECDSA anyway.
122+ if kelts.pop(0).startswith('ssh-'):
123+ return kelts[0]
124+ return None
125diff --git a/requirements.txt b/requirements.txt
126new file mode 100644
127index 0000000..e69de29
128--- /dev/null
129+++ b/requirements.txt
130diff --git a/tests/unit/requirements.txt b/tests/unit/requirements.txt
131new file mode 100644
132index 0000000..786ff01
133--- /dev/null
134+++ b/tests/unit/requirements.txt
135@@ -0,0 +1,6 @@
136+# test requirements
137+mock
138+pytest
139+pytest-cov
140+# charm requirements
141+pyyaml
142diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py
143new file mode 100644
144index 0000000..3a15841
145--- /dev/null
146+++ b/tests/unit/test_charm.py
147@@ -0,0 +1,13 @@
148+import unittest
149+
150+from utils import (
151+ extract_ssh_public_key,
152+)
153+
154+BARE_RELEASES_SSH_KEY = "AAAAB3NzaC1yc2EAAAABIwAAAQEA7TIXsws/s48o4N1MA+TpaudHS8XVF79JujYQGTumroZZqbsr04lbg1FaJ9HyS9P31e8lP2bmB5rexJaMyfAxDTV+DtrHXmeLi/XcGSOXoliOa/j3eQmdZyYme9z+Tfp8s3S6nxuDP3BRKkOuIzOPgPO5fr8AH7y83v3fyHi+H0sbm6agX2b+xV9oP8+DEgSxPNfZEVTgpdJ7/ULwAl68mWQW0w6VTVx0CMYF+cvcorcZMGmy+THzk8WH2XRalA5HqeiBtS7vJ+yFHJ5WbQOEUeR6gHRcrogR4Yd10eFWUNAFyvt53zF7W91PEgnJWjcCUN/uctybAFSgDK1UB6TPkQ==" # noqa: E501
155+RELEASES_SSH_KEY = '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' # noqa: E501
156+
157+
158+class TestUbuntuMirrorCharmUtils(unittest.TestCase):
159+ def test_extract_ssh_public_key(self):
160+ self.assertEqual(extract_ssh_public_key(RELEASES_SSH_KEY), BARE_RELEASES_SSH_KEY)
161diff --git a/tox.ini b/tox.ini
162index 7898f03..7c38a0b 100644
163--- a/tox.ini
164+++ b/tox.ini
165@@ -10,13 +10,13 @@ setenv =
166
167 [testenv:unit]
168 commands =
169- pytest --ignore mod --ignore {toxinidir}/tests/functional \
170- {posargs:-v --cov=src --cov-report=term-missing --cov-branch}
171+ pytest --ignore {toxinidir}/tests/functional \
172+ {posargs:-v --cov=hooks --cov-report=term-missing --cov-branch}
173 deps = -r{toxinidir}/tests/unit/requirements.txt
174 -r{toxinidir}/requirements.txt
175 setenv =
176- PYTHONPATH={toxinidir}/src:{toxinidir}/build/lib:{toxinidir}/build/venv
177- TZ=UTC
178+ PYTHONPATH = {toxinidir}/hooks
179+ TZ = UTC
180
181 [testenv:functional]
182 passenv =

Subscribers

People subscribed via source and target branches