Merge ~pappacena/launchpad:public-ecr-aws-ui into launchpad:master

Proposed by Thiago F. Pappacena
Status: Merged
Approved by: Thiago F. Pappacena
Approved revision: e93eeded3d3483a954fe250a0a4431b17c5558f0
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~pappacena/launchpad:public-ecr-aws-ui
Merge into: launchpad:master
Prerequisite: ~pappacena/launchpad:public-ecr-aws
Diff against target: 465 lines (+110/-10)
7 files modified
lib/lp/oci/browser/ocirecipe.py (+33/-2)
lib/lp/oci/browser/tests/test_ocirecipe.py (+13/-4)
lib/lp/oci/javascript/ocirecipe.edit.js (+1/-0)
lib/lp/oci/templates/ocirecipe-edit-push-rules.pt (+14/-0)
lib/lp/registry/browser/person.py (+26/-2)
lib/lp/registry/browser/tests/test_person.py (+9/-2)
lib/lp/registry/templates/person-edit-ociregistrycredentials.pt (+14/-0)
Reviewer Review Type Date Requested Status
Tom Wardill (community) Approve
Review via email: mp+394599@code.launchpad.net

Commit message

UI to allow adding region to OCI registry credentials

To post a comment you must log in.
Revision history for this message
Thiago F. Pappacena (pappacena) wrote :
Revision history for this message
Tom Wardill (twom) wrote :

I think the field marked 'Region' should say 'Optional' or something. It's not clear (as a new user) that pushing to ROCKs or Dockerhub shouldn't need a 'region' field.

Also, I think it should appear in the overall push rules (picture 1), as you may have an image being pushed to multiple regions, with otherwise identical fields.

review: Needs Fixing
Revision history for this message
Tom Wardill (twom) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/oci/browser/ocirecipe.py b/lib/lp/oci/browser/ocirecipe.py
2index 460b108..b3a43d7 100644
3--- a/lib/lp/oci/browser/ocirecipe.py
4+++ b/lib/lp/oci/browser/ocirecipe.py
5@@ -344,7 +344,9 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
6 super(OCIRecipeEditPushRulesView, self).setUpFields()
7 image_name_fields = []
8 url_fields = []
9+ region_fields = []
10 private_url_fields = []
11+ private_region_fields = []
12 username_fields = []
13 private_username_fields = []
14 password_fields = []
15@@ -361,6 +363,13 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
16 __name__=self._getFieldName('url', elem.id),
17 default=elem.registry_credentials.url,
18 required=True, readonly=True))
19+ region = elem.registry_credentials.getCredentialsValue(
20+ 'region')
21+ region_fields.append(
22+ TextLine(
23+ __name__=self._getFieldName('region', elem.id),
24+ default=region,
25+ required=False, readonly=True))
26 username_fields.append(
27 TextLine(
28 __name__=self._getFieldName('username', elem.id),
29@@ -380,6 +389,10 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
30 TextLine(
31 __name__=self._getFieldName('url', elem.id),
32 default='', required=True, readonly=True))
33+ private_region_fields.append(
34+ TextLine(
35+ __name__=self._getFieldName('region', elem.id),
36+ default='', required=False, readonly=True))
37 private_username_fields.append(
38 TextLine(
39 __name__=self._getFieldName('username', elem.id),
40@@ -405,6 +418,10 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
41 TextLine(
42 __name__=u'add_url',
43 required=False, readonly=False))
44+ region_fields.append(
45+ TextLine(
46+ __name__=u'add_region',
47+ required=False, readonly=False))
48 username_fields.append(
49 TextLine(
50 __name__=u'add_username',
51@@ -424,6 +441,10 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
52 FormFields(
53 *private_url_fields,
54 custom_widget=InvisibleCredentialsWidget) +
55+ FormFields(*region_fields) +
56+ FormFields(
57+ *private_region_fields,
58+ custom_widget=InvisibleCredentialsWidget) +
59 FormFields(*username_fields) +
60 FormFields(
61 *private_username_fields,
62@@ -453,6 +474,8 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
63 widgets_by_name = {widget.name: widget for widget in self.widgets}
64 url_field_name = (
65 "field." + self._getFieldName("url", rule.id))
66+ region_field_name = (
67+ "field." + self._getFieldName("region", rule.id))
68 image_field_name = (
69 "field." + self._getFieldName("image_name", rule.id))
70 username_field_name = (
71@@ -461,6 +484,7 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
72 "field." + self._getFieldName("delete", rule.id))
73 return {
74 "url": widgets_by_name[url_field_name],
75+ "region": widgets_by_name[region_field_name],
76 "image_name": widgets_by_name[image_field_name],
77 "username": widgets_by_name[username_field_name],
78 "delete": widgets_by_name[delete_field_name],
79@@ -473,6 +497,7 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
80 "existing_credentials":
81 widgets_by_name["field.existing_credentials"],
82 "url": widgets_by_name["field.add_url"],
83+ "region": widgets_by_name["field.add_region"],
84 "username": widgets_by_name["field.add_username"],
85 "password": widgets_by_name["field.add_password"],
86 "confirm_password": widgets_by_name["field.add_confirm_password"],
87@@ -483,18 +508,20 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
88 parsed_data = {}
89 add_image_name = data.get("add_image_name")
90 add_url = data.get("add_url")
91+ add_region = data.get("add_region")
92 add_username = data.get("add_username")
93 add_password = data.get("add_password")
94 add_confirm_password = data.get("add_confirm_password")
95 add_existing_credentials = data.get("existing_credentials")
96
97 # parse data from the Add new rule section of the form
98- if (add_url or add_username or add_password or
99+ if (add_url or add_region or add_username or add_password or
100 add_confirm_password or add_image_name or
101 add_existing_credentials):
102 parsed_data.setdefault(None, {
103 "image_name": add_image_name,
104 "url": add_url,
105+ "region": add_region,
106 "username": add_username,
107 "password": add_password,
108 "confirm_password": add_confirm_password,
109@@ -523,6 +550,7 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
110 add_data = parsed_data[None]
111 image_name = add_data.get("image_name")
112 url = add_data.get("url")
113+ region = add_data.get("region")
114 password = add_data.get("password")
115 confirm_password = add_data.get("confirm_password")
116 username = add_data.get("username")
117@@ -550,9 +578,12 @@ class OCIRecipeEditPushRulesView(LaunchpadFormView):
118
119 credentials_set = getUtility(IOCIRegistryCredentialsSet)
120 try:
121+ credential_data = {'username': username, 'password': password}
122+ if region is not None:
123+ credential_data['region'] = region
124 credentials = credentials_set.getOrCreate(
125 registrant=self.user, owner=self.context.owner, url=url,
126- credentials={'username': username, 'password': password})
127+ credentials=credential_data)
128 except OCIRegistryCredentialsAlreadyExist:
129 self.setFieldError(
130 "add_url",
131diff --git a/lib/lp/oci/browser/tests/test_ocirecipe.py b/lib/lp/oci/browser/tests/test_ocirecipe.py
132index 038e602..0ab9a59 100644
133--- a/lib/lp/oci/browser/tests/test_ocirecipe.py
134+++ b/lib/lp/oci/browser/tests/test_ocirecipe.py
135@@ -1111,6 +1111,10 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
136 text=soupmatchers._not_passed)),
137 soupmatchers.Within(
138 row,
139+ soupmatchers.Tag("Region", "td",
140+ text=soupmatchers._not_passed)),
141+ soupmatchers.Within(
142+ row,
143 soupmatchers.Tag("Username", "td",
144 text=soupmatchers._not_passed)),
145 soupmatchers.Within(
146@@ -1330,6 +1334,7 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
147 browser.getControl(name="field.add_credentials").value = "new"
148 browser.getControl(name="field.add_image_name").value = "imagename3"
149 browser.getControl(name="field.add_url").value = url
150+ browser.getControl(name="field.add_region").value = "somewhere-02"
151 browser.getControl(name="field.add_username").value = "username"
152 browser.getControl(name="field.add_password").value = "password"
153 browser.getControl(
154@@ -1347,8 +1352,9 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
155 url=url,
156 username="username")))
157 with person_logged_in(self.person):
158- self.assertEqual(
159- {"username": "username", "password": "password"},
160+ self.assertEqual({
161+ "username": "username", "password": "password",
162+ "region": "somewhere-02"},
163 rule.registry_credentials.getCredentials())
164
165 def test_add_oci_push_rules_existing_credentials_duplicate(self):
166@@ -1494,6 +1500,7 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
167 browser.getLink("Edit OCI registry credentials").click()
168
169 browser.getControl(name="field.add_url").value = url
170+ browser.getControl(name="field.add_region").value = "new_region1"
171 browser.getControl(name="field.add_username").value = "new_username"
172 browser.getControl(name="field.add_password").value = "password"
173 browser.getControl(name="field.add_confirm_password"
174@@ -1508,8 +1515,10 @@ class TestOCIRecipeEditPushRulesView(OCIConfigHelperMixin,
175 self.assertEqual(url, creds[1].url)
176 self.assertThat(
177 (creds[1]).getCredentials(),
178- MatchesDict({"username": Equals("new_username"),
179- "password": Equals("password")}))
180+ MatchesDict({
181+ "username": Equals("new_username"),
182+ "password": Equals("password"),
183+ "region": Equals("new_region1")}))
184
185
186 class TestOCIProjectRecipesView(BaseTestOCIRecipeView):
187diff --git a/lib/lp/oci/javascript/ocirecipe.edit.js b/lib/lp/oci/javascript/ocirecipe.edit.js
188index 8b3d82d..c31f532 100644
189--- a/lib/lp/oci/javascript/ocirecipe.edit.js
190+++ b/lib/lp/oci/javascript/ocirecipe.edit.js
191@@ -23,6 +23,7 @@ YUI.add('lp.oci.ocirecipe.edit', function(Y) {
192 });
193 module.set_enabled('field.existing_credentials', value === 'existing');
194 module.set_enabled('field.add_url', value === 'new');
195+ module.set_enabled('field.add_region', value === 'new');
196 module.set_enabled('field.add_username', value === 'new');
197 module.set_enabled('field.add_password', value === 'new');
198 module.set_enabled('field.add_confirm_password', value === 'new');
199diff --git a/lib/lp/oci/templates/ocirecipe-edit-push-rules.pt b/lib/lp/oci/templates/ocirecipe-edit-push-rules.pt
200index eee7db7..2cb3fec 100644
201--- a/lib/lp/oci/templates/ocirecipe-edit-push-rules.pt
202+++ b/lib/lp/oci/templates/ocirecipe-edit-push-rules.pt
203@@ -46,6 +46,12 @@
204 <tr>
205 <th class="push-rule-image-name">Image name</th>
206 <th class="push-rule-url">Registry URL</th>
207+ <th class="push-rule-region">
208+ Region (optional)
209+ <img height="14" width="14" alt=""
210+ title="For AWS ECR, insert the AWS region of the repository. Leave blank otherwise."
211+ src="/@@/question" />
212+ </th>
213 <th class="push-rule-username">Username</th>
214 <th class="push-rule-password">Password</th>
215 <th class="push-rule-confirm-password">Confirm password</th>
216@@ -66,6 +72,10 @@
217 tal:define="widget nocall:rule_widgets/url">
218 <metal:widget use-macro="context/@@launchpad_form/widget_div" />
219 </td>
220+ <td class="push-rule-region"
221+ tal:define="widget nocall:rule_widgets/region">
222+ <metal:widget use-macro="context/@@launchpad_form/widget_div" />
223+ </td>
224 <td class="push-rule-username"
225 tal:define="widget nocall:rule_widgets/username">
226 <metal:widget use-macro="context/@@launchpad_form/widget_div" />
227@@ -115,6 +125,10 @@
228 tal:define="widget nocall:new_rule_widgets/url">
229 <metal:widget use-macro="context/@@launchpad_form/widget_div" />
230 </td>
231+ <td class="push-rule-region"
232+ tal:define="widget nocall:new_rule_widgets/region">
233+ <metal:widget use-macro="context/@@launchpad_form/widget_div" />
234+ </td>
235 <td class="push-rule-username"
236 tal:define="widget nocall:new_rule_widgets/username">
237 <metal:widget use-macro="context/@@launchpad_form/widget_div" />
238diff --git a/lib/lp/registry/browser/person.py b/lib/lp/registry/browser/person.py
239index aef4eb0..55596e9 100644
240--- a/lib/lp/registry/browser/person.py
241+++ b/lib/lp/registry/browser/person.py
242@@ -3738,17 +3738,25 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
243 default=credentials.url,
244 required=True, readonly=False)
245
246+ region = TextLine(
247+ __name__=self._getFieldName('region', id),
248+ default=credentials.getCredentialsValue("region"),
249+ required=False, readonly=False)
250+
251 delete = Bool(
252 __name__=self._getFieldName('delete', id),
253 default=False,
254 required=True, readonly=False)
255
256- return owner, username, password, confirm_password, url, delete
257+ return owner, username, password, confirm_password, url, region, delete
258
259 def getAddFieldsRow(self):
260 add_url = TextLine(
261 __name__=u'add_url',
262 required=False, readonly=False)
263+ add_region = TextLine(
264+ __name__=u'add_region',
265+ required=False, readonly=False)
266 add_owner = Choice(
267 __name__=u'add_owner',
268 vocabulary=(
269@@ -3766,7 +3774,7 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
270 required=False, readonly=False)
271
272 return (
273- add_url, add_owner, add_username,
274+ add_url, add_region, add_owner, add_username,
275 add_password, add_confirm_password)
276
277 def _parseFieldName(self, field_name):
278@@ -3825,6 +3833,8 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
279 "field." + self._getFieldName("confirm_password",
280 credentials.id))
281 url_field_name = "field." + self._getFieldName("url", credentials.id)
282+ region_field_name = "field." + self._getFieldName(
283+ "region", credentials.id)
284 delete_field_name = (
285 "field." + self._getFieldName("delete", credentials.id))
286 return {
287@@ -3833,6 +3843,7 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
288 "password": widgets_by_name[password_field_name],
289 "confirm_password": widgets_by_name[confirm_password_field_name],
290 "url": widgets_by_name[url_field_name],
291+ "region": widgets_by_name[region_field_name],
292 "delete": widgets_by_name[delete_field_name]
293 }
294
295@@ -3840,6 +3851,7 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
296 """Rearrange form data to make it easier to process."""
297 parsed_data = {}
298 add_url = data["add_url"]
299+ add_region = data["add_region"]
300 add_owner = data["add_owner"]
301 add_username = data["add_username"]
302 add_password = data["add_password"]
303@@ -3850,6 +3862,7 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
304 "password": add_password,
305 "confirm_password": add_confirm_password,
306 "url": add_url,
307+ "region": add_region,
308 "owner": add_owner,
309 "action": "add",
310 })
311@@ -3865,6 +3878,7 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
312 confirm_password_field_name = self._getFieldName(
313 "confirm_password", credentials_id)
314 url_field_name = self._getFieldName("url", credentials_id)
315+ region_field_name = self._getFieldName("region", credentials_id)
316 delete_field_name = self._getFieldName("delete", credentials_id)
317 if data.get(delete_field_name):
318 action = "delete"
319@@ -3875,6 +3889,7 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
320 "password": data.get(password_field_name),
321 "confirm_password": data.get(confirm_password_field_name),
322 "url": data.get(url_field_name),
323+ "region": data.get(region_field_name),
324 "owner": data.get(owner_field_name),
325 "action": action,
326 })
327@@ -3882,6 +3897,7 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
328 return parsed_data
329
330 def changeCredentials(self, parsed_credentials, credentials):
331+ region = parsed_credentials["region"]
332 username = parsed_credentials["username"]
333 password = parsed_credentials["password"]
334 confirm_password = parsed_credentials["confirm_password"]
335@@ -3902,6 +3918,11 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
336 credentials.url = parsed_credentials["url"]
337 elif parsed_credentials["url"] != credentials.url:
338 credentials.url = parsed_credentials["url"]
339+ if credentials.getCredentialsValue('region') != region:
340+ credentials_data = removeSecurityProxy(
341+ credentials.getCredentials())
342+ credentials_data["region"] = region
343+ credentials.setCredentials(credentials_data)
344 if owner != credentials.owner:
345 credentials.owner = owner
346
347@@ -3919,6 +3940,7 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
348
349 def addCredentials(self, parsed_add_credentials):
350 url = parsed_add_credentials["url"]
351+ region = parsed_add_credentials["region"]
352 owner = parsed_add_credentials["owner"]
353 password = parsed_add_credentials["password"]
354 confirm_password = parsed_add_credentials["confirm_password"]
355@@ -3936,6 +3958,8 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
356 credentials = {
357 'username': username,
358 'password': password}
359+ if region:
360+ credentials["region"] = region
361 try:
362 getUtility(IOCIRegistryCredentialsSet).new(
363 registrant=self.user, owner=owner, url=url,
364diff --git a/lib/lp/registry/browser/tests/test_person.py b/lib/lp/registry/browser/tests/test_person.py
365index 07ef8d4..350afcb 100644
366--- a/lib/lp/registry/browser/tests/test_person.py
367+++ b/lib/lp/registry/browser/tests/test_person.py
368@@ -1455,13 +1455,16 @@ class TestPersonOCIRegistryCredentialsView(
369 "password": Equals("bar"),
370 })))
371
372- # change only the registry url
373+ # change only the registry url and region
374 browser = self.getViewBrowser(
375 self.owner, view_name='+oci-registry-credentials', user=self.user)
376 browser.getLink("Edit OCI registry credentials").click()
377 url_control = browser.getControl(
378 name="field.url.%d" % registry_credentials_id)
379 url_control.value = newurl
380+ url_control = browser.getControl(
381+ name="field.region.%d" % registry_credentials_id)
382+ url_control.value = 'us-west-2'
383 browser.getControl("Save").click()
384 with person_logged_in(self.user):
385 self.assertThat(
386@@ -1470,6 +1473,7 @@ class TestPersonOCIRegistryCredentialsView(
387 MatchesDict({
388 "username": Equals("different_username"),
389 "password": Equals("bar"),
390+ "region": Equals("us-west-2"),
391 })))
392
393 # change only the password
394@@ -1484,7 +1488,7 @@ class TestPersonOCIRegistryCredentialsView(
395 self.assertIn(
396 "Passwords do not match.", six.ensure_text(browser.contents))
397
398- # change all fields with one edit action
399+ # change all fields (except region) with one edit action
400 username_control = browser.getControl(
401 name="field.username.%d" % registry_credentials_id)
402 username_control.value = 'third_different_username'
403@@ -1506,6 +1510,7 @@ class TestPersonOCIRegistryCredentialsView(
404 MatchesDict({
405 "username": Equals("third_different_username"),
406 "password": Equals("third_newpassword"),
407+ "region": Equals("us-west-2"),
408 })))
409
410 def test_add_oci_registry_credentials(self):
411@@ -1530,6 +1535,7 @@ class TestPersonOCIRegistryCredentialsView(
412 [owner_name], browser.getControl(name="field.add_owner").value)
413
414 browser.getControl(name="field.add_url").value = url
415+ browser.getControl(name="field.add_region").value = "sa-east-1"
416 browser.getControl(name="field.add_owner").value = [new_owner_name]
417 browser.getControl(name="field.add_username").value = "new_username"
418 browser.getControl(name="field.add_password").value = "password"
419@@ -1555,6 +1561,7 @@ class TestPersonOCIRegistryCredentialsView(
420 MatchesDict({
421 "username": Equals("new_username"),
422 "password": Equals("password"),
423+ "region": Equals("sa-east-1"),
424 }))))
425
426 def test_delete_oci_registry_credentials(self):
427diff --git a/lib/lp/registry/templates/person-edit-ociregistrycredentials.pt b/lib/lp/registry/templates/person-edit-ociregistrycredentials.pt
428index 1b0fd0f..27aa9d3 100644
429--- a/lib/lp/registry/templates/person-edit-ociregistrycredentials.pt
430+++ b/lib/lp/registry/templates/person-edit-ociregistrycredentials.pt
431@@ -43,6 +43,12 @@
432 <thead>
433 <tr>
434 <th class="credentials-url">Registry URL</th>
435+ <th class="credentials-region">
436+ Region (optional)
437+ <img height="14" width="14" alt=""
438+ title="For AWS ECR, insert the AWS region of the repository. Leave blank otherwise."
439+ src="/@@/question" />
440+ </th>
441 <th class="credentials-owner">Owner</th>
442 <th class="credentials-username">Username</th>
443 <th class="credentials-password">Password</th>
444@@ -60,6 +66,10 @@
445 tal:define="widget nocall:credentials_widgets/url">
446 <metal:widget use-macro="context/@@launchpad_form/widget_div" />
447 </td>
448+ <td class="credentials-region"
449+ tal:define="widget nocall:credentials_widgets/region">
450+ <metal:widget use-macro="context/@@launchpad_form/widget_div" />
451+ </td>
452 <td class="credentials-owner"
453 tal:define="widget nocall:credentials_widgets/owner">
454 <metal:widget use-macro="context/@@launchpad_form/widget_div" />
455@@ -91,6 +101,10 @@
456 tal:define="widget nocall:view/widgets/add_url">
457 <metal:widget use-macro="context/@@launchpad_form/widget_div" />
458 </td>
459+ <td class="credentials-region"
460+ tal:define="widget nocall:view/widgets/add_region">
461+ <metal:widget use-macro="context/@@launchpad_form/widget_div" />
462+ </td>
463 <td class="credentials-owner"
464 tal:define="widget nocall:view/widgets/add_owner">
465 <metal:widget use-macro="context/@@launchpad_form/widget_div" />