Merge ~cbelu/charm-k8s-ingress:additional-hostnames into charm-k8s-ingress:master

Proposed by Claudiu Belu
Status: Merged
Approved by: Tom Haddon
Approved revision: 9da2871f11e2852ea7710a3099e4a8b1ab97ed9f
Merged at revision: 778f83c51ecac099fff18c1fc4e8606583ee0ce1
Proposed branch: ~cbelu/charm-k8s-ingress:additional-hostnames
Merge into: charm-k8s-ingress:master
Diff against target: 135 lines (+112/-1)
2 files modified
src/charm.py (+3/-1)
tests/unit/test_charm.py (+109/-0)
Reviewer Review Type Date Requested Status
Tom Haddon Approve
Review via email: mp+416022@code.launchpad.net

Commit message

Fixes adding unwanted routes from additional-hostnames

On multiple ingress relations for different service-hostnames (foo, bar), if the first relation has "bar" as an additional-hostname, it will wrongfully have the routes from the second relation.

This is happening because the same list of ingress paths are being used for all host-names and additional-hostnames.

When creating the Ingress Objects, a separate path list list is created for each hostname.

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
Tom Haddon (mthaddon) wrote :

I get a lint error with this - https://pastebin.ubuntu.com/p/DHyVzC4XKD/ - can you take a look?

review: Needs Fixing
Revision history for this message
Claudiu Belu (cbelu) wrote :

> I get a lint error with this - https://pastebin.ubuntu.com/p/DHyVzC4XKD/ - can
> you take a look?

Done

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 778f83c51ecac099fff18c1fc4e8606583ee0ce1

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/charm.py b/src/charm.py
2index e2fb7fb..3188253 100755
3--- a/src/charm.py
4+++ b/src/charm.py
5@@ -536,7 +536,9 @@ class NginxIngressCharm(CharmBase):
6 # one rule. Those hostnames might also be used in other relations.
7 for rule in ingress.spec.rules:
8 if rule.host not in ingress_paths:
9- ingress_paths[rule.host] = rule.http.paths
10+ # The same paths array is used for any additional-hostnames given, so we need
11+ # to make our own copy.
12+ ingress_paths[rule.host] = rule.http.paths.copy()
13 hostname_ingress[rule.host] = ingress
14 continue
15
16diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py
17index c4cc9cf..8591856 100644
18--- a/tests/unit/test_charm.py
19+++ b/tests/unit/test_charm.py
20@@ -1318,6 +1318,115 @@ class TestCharmMultipleRelations(unittest.TestCase):
21 mock_replace_ingress.assert_not_called()
22
23 @patch('charm.NginxIngressCharm._report_service_ips')
24+ @patch('charm.NginxIngressCharm._remove_service')
25+ @patch('charm.NginxIngressCharm._define_service')
26+ @patch('charm._networking_v1_api')
27+ def test_ingress_multiple_relations_additional_hostnames(
28+ self, mock_api, mock_define_service, mock_remove_service, mock_report_ips
29+ ):
30+ """Test for checking Ingress creation / deletion for multiple relations.
31+
32+ This test will check that the charm will create multiple Resources for different hostnames.
33+ """
34+ # Setting the leader to True will allow us to test the Ingress creation.
35+ self.harness.set_leader(True)
36+ self.harness.charm._authed = True
37+
38+ mock_report_ips.return_value = ["10.0.1.12"]
39+ mock_list_ingress = mock_api.return_value.list_namespaced_ingress
40+ # We'll consider we don't have any ingresses set yet.
41+ mock_list_ingress.return_value.items = []
42+
43+ # Add the first relation.
44+ rel_data = {
45+ "service-name": "gunicorn",
46+ "service-hostname": "foo.in.ternal",
47+ "service-port": "80",
48+ "additional-hostnames": "lish.in.ternal",
49+ "tls-secret-name": "some-secret",
50+ }
51+ rel_id1 = self._add_ingress_relation('gunicorn', rel_data)
52+
53+ # It should create 2 different Ingress Resources, since we have an additional hostname.
54+ conf_or_rels = self.harness.charm._all_config_or_relations
55+ mock_create_ingress = mock_api.return_value.create_namespaced_ingress
56+ first_body = conf_or_rels[0]._get_k8s_ingress()
57+ first_body.spec.rules = [first_body.spec.rules[0]]
58+ second_body = conf_or_rels[0]._get_k8s_ingress()
59+ second_body.metadata.name = "lish-in-ternal-ingress"
60+ second_body.spec.rules = [second_body.spec.rules[1]]
61+ second_body.spec.tls[0].hosts = ["lish.in.ternal"]
62+ mock_create_ingress.assert_has_calls(
63+ [
64+ mock.call(namespace=self.harness.charm._namespace, body=first_body),
65+ mock.call(namespace=self.harness.charm._namespace, body=second_body),
66+ ]
67+ )
68+
69+ # Reset the create ingress mock, and add a second relation with the service-hostname set
70+ # to the first relation's additional-hostname. A third K8s Ingress Resource should not
71+ # be created.
72+ mock_create_ingress.reset_mock()
73+ mock_ingress1 = MagicMock()
74+ mock_ingress1.metadata.name = "foo-in-ternal-ingress"
75+ mock_ingress2 = MagicMock()
76+ mock_ingress2.metadata.name = "lish-in-ternal-ingress"
77+ mock_list_ingress.return_value.items = [mock_ingress1, mock_ingress2]
78+
79+ rel_data = {
80+ "service-name": "punicorn",
81+ "service-hostname": "lish.in.ternal",
82+ "service-port": "9090",
83+ "path-routes": "/lish",
84+ "tls-secret-name": "some-secret",
85+ }
86+ self._add_ingress_relation('punicorn', rel_data)
87+
88+ # We're expecting that the first K8s Ingress Resource will be updated, but it will not
89+ # change, and that the second K8s Ingress Resource will be updated to include the route
90+ # from the second relation.
91+ conf_or_rels = self.harness.charm._all_config_or_relations
92+ mock_create_ingress.assert_not_called()
93+
94+ second_rel_body = conf_or_rels[1]._get_k8s_ingress()
95+ second_body.spec.rules[0].http.paths.extend(second_rel_body.spec.rules[0].http.paths)
96+ calls = [
97+ mock.call(
98+ name=conf_or_rels[0]._ingress_name,
99+ namespace=self.harness.charm._namespace,
100+ body=first_body,
101+ ),
102+ mock.call(
103+ name=conf_or_rels[1]._ingress_name,
104+ namespace=self.harness.charm._namespace,
105+ body=second_body,
106+ ),
107+ ]
108+ mock_replace_ingress = mock_api.return_value.replace_namespaced_ingress
109+ mock_replace_ingress.assert_has_calls(calls)
110+
111+ # Remove the first relation and assert that only the first ingress is removed.
112+ mock_ingress2 = MagicMock()
113+ mock_ingress2.metadata.name = "lish-in-ternal-ingress"
114+ mock_list_ingress.return_value.items = [mock_ingress1, mock_ingress2]
115+ mock_create_ingress.reset_mock()
116+ mock_replace_ingress.reset_mock()
117+ self.harness.remove_relation(rel_id1)
118+
119+ # Assert that only the ingress for the first relation was removed.
120+ mock_delete_ingress = mock_api.return_value.delete_namespaced_ingress
121+ mock_delete_ingress.assert_called_once_with(
122+ conf_or_rels[0]._ingress_name,
123+ self.harness.charm._namespace,
124+ )
125+ mock_create_ingress.assert_not_called()
126+ mock_replace_ingress.assert_called_once_with(
127+ name=conf_or_rels[1]._ingress_name,
128+ namespace=self.harness.charm._namespace,
129+ body=conf_or_rels[1]._get_k8s_ingress(),
130+ )
131+
132+ @patch('charm.NginxIngressCharm._report_service_ips')
133 @patch('charm.NginxIngressCharm._define_ingress')
134 @patch('charm.NginxIngressCharm._define_service')
135 @patch('charm._networking_v1_api')

Subscribers

People subscribed via source and target branches

to all changes: