Merge ~ballot/charm-k8s-mm-pd-bot/+git/charm-k8s-mm-pd-bot:quotes into charm-k8s-mm-pd-bot:master

Proposed by Benjamin Allot
Status: Merged
Approved by: Benjamin Allot
Approved revision: 8d9e09b73c39cb2d02f2975762942c830e966e9f
Merged at revision: 8671d0b6c001cc0e0ae09865386645511af1c755
Proposed branch: ~ballot/charm-k8s-mm-pd-bot/+git/charm-k8s-mm-pd-bot:quotes
Merge into: charm-k8s-mm-pd-bot:master
Diff against target: 727 lines (+210/-211)
4 files modified
pyproject.toml (+0/-1)
src/charm.py (+41/-41)
tests/unit/scenario.py (+143/-143)
tests/unit/test_charm.py (+26/-26)
Reviewer Review Type Date Requested Status
Laurent Sesquès Approve
Canonical IS Reviewers Pending
Review via email: mp+390748@code.launchpad.net

Commit message

Normalize quotes with black

To post a comment you must log in.
Revision history for this message
Laurent Sesquès (sajoupa) wrote :

+1

review: Approve
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
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 8671d0b6c001cc0e0ae09865386645511af1c755

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/pyproject.toml b/pyproject.toml
index d2f23b9..55ec8d7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,2 @@
1[tool.black]1[tool.black]
2skip-string-normalization = true
3line-length = 1202line-length = 120
diff --git a/src/charm.py b/src/charm.py
index b17ced0..7b2807f 100755
--- a/src/charm.py
+++ b/src/charm.py
@@ -18,18 +18,18 @@ from ops.model import (
1818
19logger = logging.getLogger()19logger = logging.getLogger()
2020
21REQUIRED_JUJU_SETTINGS = ['image_path', 'mm_pd_bot_cfg']21REQUIRED_JUJU_SETTINGS = ["image_path", "mm_pd_bot_cfg"]
22REQUIRED_CFG_SETTINGS = {22REQUIRED_CFG_SETTINGS = {
23 'PagerDuty': ['account', 'api-token', 'private-channel', 'public-channel'],23 "PagerDuty": ["account", "api-token", "private-channel", "public-channel"],
24 'Prometheus': ['max_cache_size'],24 "Prometheus": ["max_cache_size"],
25 'httpd': ['hostname', 'listen-ip', 'listen-port', 'magic-uuid'],25 "httpd": ["hostname", "listen-ip", "listen-port", "magic-uuid"],
26 'MattermostBot': ['bot_url', 'bot_team'],26 "MattermostBot": ["bot_url", "bot_team"],
27 # Those sections can be empty27 # Those sections can be empty
28 'nickname to email': [],28 "nickname to email": [],
29 'Mattermost Aliases': [],29 "Mattermost Aliases": [],
30 'PagerDuty service to Mattermost Channel': [],30 "PagerDuty service to Mattermost Channel": [],
31}31}
32BOT_CFG_SECTION = 'MattermostBot'32BOT_CFG_SECTION = "MattermostBot"
3333
3434
35class MmPdBotK8sCharmConfigError(Exception):35class MmPdBotK8sCharmConfigError(Exception):
@@ -51,9 +51,9 @@ class MmPdBotK8sCharm(CharmBase):
51 :raises MmPdBotK8sCharmConfigError: Raise if you don't have a bot_token or the combination of bot_login and51 :raises MmPdBotK8sCharmConfigError: Raise if you don't have a bot_token or the combination of bot_login and
52 bot_password.52 bot_password.
53 """53 """
54 bot_token = self.bot_config.get(BOT_CFG_SECTION, 'bot_token', fallback=None)54 bot_token = self.bot_config.get(BOT_CFG_SECTION, "bot_token", fallback=None)
55 bot_login = self.bot_config.get(BOT_CFG_SECTION, 'bot_login', fallback=None)55 bot_login = self.bot_config.get(BOT_CFG_SECTION, "bot_login", fallback=None)
56 bot_password = self.bot_config.get(BOT_CFG_SECTION, 'bot_password', fallback=None)56 bot_password = self.bot_config.get(BOT_CFG_SECTION, "bot_password", fallback=None)
5757
58 if not bot_token and (not bot_login or not bot_password):58 if not bot_token and (not bot_login or not bot_password):
59 err_msg = "bot_token or bot_login/bot_password in {0} section required".format(BOT_CFG_SECTION)59 err_msg = "bot_token or bot_login/bot_password in {0} section required".format(BOT_CFG_SECTION)
@@ -129,7 +129,7 @@ class MmPdBotK8sCharm(CharmBase):
129129
130 def _validate_mm_pd_bot_configuration(self) -> None:130 def _validate_mm_pd_bot_configuration(self) -> None:
131 """Check the configuration part related to mm_pd_bot configuration."""131 """Check the configuration part related to mm_pd_bot configuration."""
132 self.bot_config = self._load_mm_pd_bot_configuration(self.model.config['mm_pd_bot_cfg'])132 self.bot_config = self._load_mm_pd_bot_configuration(self.model.config["mm_pd_bot_cfg"])
133 self._validate_mm_pd_bot_configuration_content()133 self._validate_mm_pd_bot_configuration_content()
134 self._validate_bot_creds()134 self._validate_bot_creds()
135135
@@ -148,7 +148,7 @@ class MmPdBotK8sCharm(CharmBase):
148 """148 """
149 config = self.model.config149 config = self.model.config
150 pod_config = {150 pod_config = {
151 'MM_PD_BOT_CFG': config['mm_pd_bot_cfg'],151 "MM_PD_BOT_CFG": config["mm_pd_bot_cfg"],
152 }152 }
153153
154 return pod_config154 return pod_config
@@ -159,10 +159,10 @@ class MmPdBotK8sCharm(CharmBase):
159 :param pod_spec: pod spec v3 as defined by juju.159 :param pod_spec: pod spec v3 as defined by juju.
160 """160 """
161161
162 hostname = self.bot_config.get('httpd', 'hostname')162 hostname = self.bot_config.get("httpd", "hostname")
163 container_port = self.bot_config.getint('httpd', 'listen-port')163 container_port = self.bot_config.getint("httpd", "listen-port")
164 magic_uuid = self.bot_config.get('httpd', 'magic-uuid')164 magic_uuid = self.bot_config.get("httpd", "magic-uuid")
165 tls_secret_name = self.model.config.get('tls_secret_name', None)165 tls_secret_name = self.model.config.get("tls_secret_name", None)
166166
167 if tls_secret_name:167 if tls_secret_name:
168 scheme = "https"168 scheme = "https"
@@ -192,52 +192,52 @@ class MmPdBotK8sCharm(CharmBase):
192 },192 },
193 }193 }
194 if tls_secret_name:194 if tls_secret_name:
195 ingress['spec']['tls'] = [195 ingress["spec"]["tls"] = [
196 {'hosts': [bot_listening_url_parsed.hostname], 'secretName': tls_secret_name},196 {"hosts": [bot_listening_url_parsed.hostname], "secretName": tls_secret_name},
197 ]197 ]
198 else:198 else:
199 annotations['nginx.ingress.kubernetes.io/ssl-redirect'] = 'false'199 annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false"
200200
201 ingress_whitelist_source_range = self.model.config.get('ingress_whitelist_source_range', None)201 ingress_whitelist_source_range = self.model.config.get("ingress_whitelist_source_range", None)
202 if ingress_whitelist_source_range:202 if ingress_whitelist_source_range:
203 annotations['nginx.ingress.kubernetes.io/whitelist-source-range'] = ingress_whitelist_source_range203 annotations["nginx.ingress.kubernetes.io/whitelist-source-range"] = ingress_whitelist_source_range
204204
205 if annotations:205 if annotations:
206 ingress['annotations'] = annotations206 ingress["annotations"] = annotations
207207
208 # Due to https://github.com/canonical/operator/issues/293 we208 # Due to https://github.com/canonical/operator/issues/293 we
209 # can't use pod.set_spec's k8s_resources argument.209 # can't use pod.set_spec's k8s_resources argument.
210 resources = pod_spec.get('kubernetesResources', {})210 resources = pod_spec.get("kubernetesResources", {})
211 resources['ingressResources'] = [ingress]211 resources["ingressResources"] = [ingress]
212 pod_spec['kubernetesResources'] = resources212 pod_spec["kubernetesResources"] = resources
213213
214 def _make_pod_spec(self) -> None:214 def _make_pod_spec(self) -> None:
215 """Create a pod spec with some core configuration."""215 """Create a pod spec with some core configuration."""
216216
217 config = self.model.config217 config = self.model.config
218 container_port = self.bot_config.getint('httpd', 'listen-port')218 container_port = self.bot_config.getint("httpd", "listen-port")
219 # TODO: The bot replies 501 on HTTP GET so until we have something to probe readiness, magic-uuid is not needed219 # TODO: The bot replies 501 on HTTP GET so until we have something to probe readiness, magic-uuid is not needed
220 # here220 # here
221 # magic_uuid = self.bot_config.get('httpd', 'magic-uuid')221 # magic_uuid = self.bot_config.get('httpd', 'magic-uuid')
222 image_details = {222 image_details = {
223 'imagePath': config['image_path'],223 "imagePath": config["image_path"],
224 }224 }
225 if config.get('image_username', None):225 if config.get("image_username", None):
226 image_details.update({'username': config['image_username'], 'password': config['image_password']})226 image_details.update({"username": config["image_username"], "password": config["image_password"]})
227 pod_config = self._make_pod_config()227 pod_config = self._make_pod_config()
228228
229 return {229 return {
230 'version': 3, # otherwise resources are ignored230 "version": 3, # otherwise resources are ignored
231 'containers': [231 "containers": [
232 {232 {
233 'name': self.app.name,233 "name": self.app.name,
234 'imageDetails': image_details,234 "imageDetails": image_details,
235 # TODO: debatable. The idea is that if you want to force an update with the same image name, you235 # TODO: debatable. The idea is that if you want to force an update with the same image name, you
236 # don't need to empty kubelet cache on each node to have the right version.236 # don't need to empty kubelet cache on each node to have the right version.
237 # This implies a performance drop upon start.237 # This implies a performance drop upon start.
238 'imagePullPolicy': 'Always',238 "imagePullPolicy": "Always",
239 'ports': [{'containerPort': container_port, 'protocol': 'TCP'}],239 "ports": [{"containerPort": container_port, "protocol": "TCP"}],
240 'envConfig': pod_config,240 "envConfig": pod_config,
241 # TODO: Add readiness probe in the bot.241 # TODO: Add readiness probe in the bot.
242 # 'kubernetes': {242 # 'kubernetes': {
243 # 'readinessProbe': {'httpGet': {'path': '/{0}/'.format(magic_uuid), 'port': container_port}},243 # 'readinessProbe': {'httpGet': {'path': '/{0}/'.format(magic_uuid), 'port': container_port}},
@@ -261,13 +261,13 @@ class MmPdBotK8sCharm(CharmBase):
261 self.unit.status = BlockedStatus(str(exc))261 self.unit.status = BlockedStatus(str(exc))
262 return262 return
263263
264 logger.info('Assembling pod spec')264 logger.info("Assembling pod spec")
265 pod_spec = self._make_pod_spec()265 pod_spec = self._make_pod_spec()
266 self._update_pod_spec_for_k8s_ingress(pod_spec)266 self._update_pod_spec_for_k8s_ingress(pod_spec)
267 logger.info('Setting pod spec')267 logger.info("Setting pod spec")
268 self.model.pod.set_spec(pod_spec)268 self.model.pod.set_spec(pod_spec)
269 self.unit.status = ActiveStatus()269 self.unit.status = ActiveStatus()
270270
271271
272if __name__ == '__main__': # pragma: no cover272if __name__ == "__main__": # pragma: no cover
273 main(MmPdBotK8sCharm, use_juju_for_storage=True)273 main(MmPdBotK8sCharm, use_juju_for_storage=True)
diff --git a/tests/unit/scenario.py b/tests/unit/scenario.py
index 9c62641..4aa55b5 100644
--- a/tests/unit/scenario.py
+++ b/tests/unit/scenario.py
@@ -12,16 +12,16 @@ import yaml
12logger = logging.getLogger(__name__)12logger = logging.getLogger(__name__)
1313
14BOT_CFG_FILES = {14BOT_CFG_FILES = {
15 'both_authentication_method': 'both_authentication_method.cfg',15 "both_authentication_method": "both_authentication_method.cfg",
16 'good_configuration': 'good_bot.cfg',16 "good_configuration": "good_bot.cfg",
17 'missing_options': 'missing_options.cfg',17 "missing_options": "missing_options.cfg",
18 'missing_sections': 'missing_sections.cfg',18 "missing_sections": "missing_sections.cfg",
19 'no_valid_authentication': 'no_valid_authentication.cfg',19 "no_valid_authentication": "no_valid_authentication.cfg",
20 'ssl_configuration': 'ssl_bot.cfg',20 "ssl_configuration": "ssl_bot.cfg",
21}21}
2222
2323
24def get_cfg_content(configuration='good_configuration') -> str:24def get_cfg_content(configuration="good_configuration") -> str:
25 """Get the content of the file associated with configuration.25 """Get the content of the file associated with configuration.
2626
27 :param str configuration: Key of the BOT_CFG_FILES dictionary to get the content of the associated configuration27 :param str configuration: Key of the BOT_CFG_FILES dictionary to get the content of the associated configuration
@@ -32,7 +32,7 @@ def get_cfg_content(configuration='good_configuration') -> str:
32 files_dir = "files"32 files_dir = "files"
33 try:33 try:
34 path = Path(dir_path, files_dir, BOT_CFG_FILES[configuration])34 path = Path(dir_path, files_dir, BOT_CFG_FILES[configuration])
35 with open(path, 'r') as cfg:35 with open(path, "r") as cfg:
36 return cfg.read()36 return cfg.read()
37 except KeyError:37 except KeyError:
38 logger.error("No configuration files associated with '%s'", configuration)38 logger.error("No configuration files associated with '%s'", configuration)
@@ -43,12 +43,12 @@ def get_juju_settings_default_value() -> list:
43 """Return the list of juju settings as defined in config.yaml."""43 """Return the list of juju settings as defined in config.yaml."""
4444
45 dir_path = os.path.dirname(os.path.realpath(__file__))45 dir_path = os.path.dirname(os.path.realpath(__file__))
46 config_yaml = Path(dir_path, '..', '..', 'config.yaml')46 config_yaml = Path(dir_path, "..", "..", "config.yaml")
47 with open(config_yaml, 'r') as config:47 with open(config_yaml, "r") as config:
48 loaded_config = yaml.safe_load(config.read())48 loaded_config = yaml.safe_load(config.read())
4949
50 # Load the dict with the default values50 # Load the dict with the default values
51 default_values = {config: value['default'] for (config, value) in loaded_config['options'].items()}51 default_values = {config: value["default"] for (config, value) in loaded_config["options"].items()}
5252
53 return default_values53 return default_values
5454
@@ -57,52 +57,52 @@ def get_juju_settings_default_value() -> list:
57JUJU_SETTINGS_DEFAULT = get_juju_settings_default_value()57JUJU_SETTINGS_DEFAULT = get_juju_settings_default_value()
5858
59TEST_JUJU_SETTINGS = {59TEST_JUJU_SETTINGS = {
60 'empty_image_path': {60 "empty_image_path": {
61 'config': {'mm_pd_bot_cfg': 'dummy'},61 "config": {"mm_pd_bot_cfg": "dummy"},
62 'logger': ["ERROR:root:Required setting empty: image_path"],62 "logger": ["ERROR:root:Required setting empty: image_path"],
63 'expected': 'Required setting(s) empty: image_path',63 "expected": "Required setting(s) empty: image_path",
64 },64 },
65 'empty_mm_pd_bot_cfg': {65 "empty_mm_pd_bot_cfg": {
66 'config': {'image_path': 'mm_pd_bot:devel'},66 "config": {"image_path": "mm_pd_bot:devel"},
67 'logger': ["ERROR:root:Required setting empty: mm_pd_bot_cfg"],67 "logger": ["ERROR:root:Required setting empty: mm_pd_bot_cfg"],
68 'expected': 'Required setting(s) empty: mm_pd_bot_cfg',68 "expected": "Required setting(s) empty: mm_pd_bot_cfg",
69 },69 },
70 'empty_all_settings': {70 "empty_all_settings": {
71 'config': {},71 "config": {},
72 'logger': ["ERROR:root:Required setting empty: image_path", "ERROR:root:Required setting empty: mm_pd_bot_cfg"],72 "logger": ["ERROR:root:Required setting empty: image_path", "ERROR:root:Required setting empty: mm_pd_bot_cfg"],
73 'expected': 'Required setting(s) empty: image_path, mm_pd_bot_cfg',73 "expected": "Required setting(s) empty: image_path, mm_pd_bot_cfg",
74 },74 },
75}75}
7676
7777
78VALIDATE_MM_PD_BOT_CFG = {78VALIDATE_MM_PD_BOT_CFG = {
79 'good_configuration': {79 "good_configuration": {
80 'config': {'image_path': 'mm_pd_bot:devel', 'mm_pd_bot_cfg': get_cfg_content("good_configuration")},80 "config": {"image_path": "mm_pd_bot:devel", "mm_pd_bot_cfg": get_cfg_content("good_configuration")},
81 'expected': True,81 "expected": True,
82 'logger': [],82 "logger": [],
83 },83 },
84 'bad_formatted_configuration': {84 "bad_formatted_configuration": {
85 'config': {'image_path': 'mm_pd_bot:devel', 'mm_pd_bot_cfg': "[PagerDuty"},85 "config": {"image_path": "mm_pd_bot:devel", "mm_pd_bot_cfg": "[PagerDuty"},
86 'expected': "Error while parsing mm_pd_bot_cfg setting",86 "expected": "Error while parsing mm_pd_bot_cfg setting",
87 'logger': ["ERROR:root:Error while parsing mm_pd_bot_cfg setting"],87 "logger": ["ERROR:root:Error while parsing mm_pd_bot_cfg setting"],
88 },88 },
89 'missing_sections': {89 "missing_sections": {
90 'config': {'image_path': 'mm_pd_bot:devel', 'mm_pd_bot_cfg': get_cfg_content('missing_sections')},90 "config": {"image_path": "mm_pd_bot:devel", "mm_pd_bot_cfg": get_cfg_content("missing_sections")},
91 'expected': "Required section(s) missing in bot configuration file: "91 "expected": "Required section(s) missing in bot configuration file: "
92 "MattermostBot, Prometheus, httpd, nickname to email",92 "MattermostBot, Prometheus, httpd, nickname to email",
93 'logger': [93 "logger": [
94 "ERROR:root:required section missing in bot configuration file: httpd",94 "ERROR:root:required section missing in bot configuration file: httpd",
95 "ERROR:root:required section missing in bot configuration file: MattermostBot",95 "ERROR:root:required section missing in bot configuration file: MattermostBot",
96 "ERROR:root:required section missing in bot configuration file: Prometheus",96 "ERROR:root:required section missing in bot configuration file: Prometheus",
97 "ERROR:root:required section missing in bot configuration file: nickname to email",97 "ERROR:root:required section missing in bot configuration file: nickname to email",
98 ],98 ],
99 },99 },
100 'missing_options': {100 "missing_options": {
101 'config': {'image_path': 'mm_pd_bot:devel', 'mm_pd_bot_cfg': get_cfg_content('missing_options')},101 "config": {"image_path": "mm_pd_bot:devel", "mm_pd_bot_cfg": get_cfg_content("missing_options")},
102 'expected': "Required option(s) missing in section MattermostBot: bot_team, bot_url, "102 "expected": "Required option(s) missing in section MattermostBot: bot_team, bot_url, "
103 "Required option(s) missing in section PagerDuty: api-token, "103 "Required option(s) missing in section PagerDuty: api-token, "
104 "Required option(s) missing in section httpd: hostname, listen-ip, listen-port, magic-uuid",104 "Required option(s) missing in section httpd: hostname, listen-ip, listen-port, magic-uuid",
105 'logger': [105 "logger": [
106 "ERROR:root:required option api-token missing in section PagerDuty",106 "ERROR:root:required option api-token missing in section PagerDuty",
107 "ERROR:root:required option bot_team missing in section MattermostBot",107 "ERROR:root:required option bot_team missing in section MattermostBot",
108 "ERROR:root:required option bot_url missing in section MattermostBot",108 "ERROR:root:required option bot_url missing in section MattermostBot",
@@ -112,52 +112,52 @@ VALIDATE_MM_PD_BOT_CFG = {
112 "ERROR:root:required option magic-uuid missing in section httpd",112 "ERROR:root:required option magic-uuid missing in section httpd",
113 ],113 ],
114 },114 },
115 'no_valid_authentication': {115 "no_valid_authentication": {
116 'config': {'mm_pd_bot_cfg': get_cfg_content('no_valid_authentication')},116 "config": {"mm_pd_bot_cfg": get_cfg_content("no_valid_authentication")},
117 'expected': "bot_token or bot_login/bot_password in MattermostBot section required",117 "expected": "bot_token or bot_login/bot_password in MattermostBot section required",
118 'logger': ["ERROR:root:bot_token or bot_login/bot_password in MattermostBot section required"],118 "logger": ["ERROR:root:bot_token or bot_login/bot_password in MattermostBot section required"],
119 },119 },
120 'both_authentication_method': {120 "both_authentication_method": {
121 'config': {'mm_pd_bot_cfg': get_cfg_content('both_authentication_method')},121 "config": {"mm_pd_bot_cfg": get_cfg_content("both_authentication_method")},
122 'expected': "bot_token and bot_login are both set. Pick one of them",122 "expected": "bot_token and bot_login are both set. Pick one of them",
123 'logger': ["ERROR:root:bot_token and bot_login are both set. Pick one of them"],123 "logger": ["ERROR:root:bot_token and bot_login are both set. Pick one of them"],
124 },124 },
125}125}
126126
127VALIDATE_POD_SPEC = {127VALIDATE_POD_SPEC = {
128 'basic': {128 "basic": {
129 'config': {'image_path': 'mm_pd_bot:devel', 'mm_pd_bot_cfg': get_cfg_content('good_configuration')},129 "config": {"image_path": "mm_pd_bot:devel", "mm_pd_bot_cfg": get_cfg_content("good_configuration")},
130 'pod_spec': {130 "pod_spec": {
131 'version': 3, # otherwise resources are ignored131 "version": 3, # otherwise resources are ignored
132 'containers': [132 "containers": [
133 {133 {
134 'name': 'mm-pd-bot',134 "name": "mm-pd-bot",
135 'imageDetails': {135 "imageDetails": {
136 'imagePath': 'mm_pd_bot:devel',136 "imagePath": "mm_pd_bot:devel",
137 },137 },
138 'imagePullPolicy': 'Always',138 "imagePullPolicy": "Always",
139 'ports': [{'containerPort': 2160, 'protocol': 'TCP'}],139 "ports": [{"containerPort": 2160, "protocol": "TCP"}],
140 'envConfig': {'MM_PD_BOT_CFG': get_cfg_content('good_configuration')},140 "envConfig": {"MM_PD_BOT_CFG": get_cfg_content("good_configuration")},
141 }141 }
142 ],142 ],
143 },143 },
144 },144 },
145 'private_registry': {145 "private_registry": {
146 'config': {146 "config": {
147 'image_path': 'mm_pd_bot:devel',147 "image_path": "mm_pd_bot:devel",
148 'image_username': 'rockity',148 "image_username": "rockity",
149 'image_password': 'rock',149 "image_password": "rock",
150 'mm_pd_bot_cfg': get_cfg_content('good_configuration'),150 "mm_pd_bot_cfg": get_cfg_content("good_configuration"),
151 },151 },
152 'pod_spec': {152 "pod_spec": {
153 'version': 3, # otherwise resources are ignored153 "version": 3, # otherwise resources are ignored
154 'containers': [154 "containers": [
155 {155 {
156 'name': 'mm-pd-bot',156 "name": "mm-pd-bot",
157 'imageDetails': {'imagePath': 'mm_pd_bot:devel', 'username': 'rockity', 'password': 'rock'},157 "imageDetails": {"imagePath": "mm_pd_bot:devel", "username": "rockity", "password": "rock"},
158 'imagePullPolicy': 'Always',158 "imagePullPolicy": "Always",
159 'ports': [{'containerPort': 2160, 'protocol': 'TCP'}],159 "ports": [{"containerPort": 2160, "protocol": "TCP"}],
160 'envConfig': {'MM_PD_BOT_CFG': get_cfg_content('good_configuration')},160 "envConfig": {"MM_PD_BOT_CFG": get_cfg_content("good_configuration")},
161 }161 }
162 ],162 ],
163 },163 },
@@ -166,124 +166,124 @@ VALIDATE_POD_SPEC = {
166166
167167
168VALIDATE_POD_SPEC_AND_INGRESS = {168VALIDATE_POD_SPEC_AND_INGRESS = {
169 'basic': {169 "basic": {
170 'config': {'image_path': 'mm_pd_bot:devel', 'mm_pd_bot_cfg': get_cfg_content('good_configuration')},170 "config": {"image_path": "mm_pd_bot:devel", "mm_pd_bot_cfg": get_cfg_content("good_configuration")},
171 'pod_spec': {171 "pod_spec": {
172 'version': 3, # otherwise resources are ignored172 "version": 3, # otherwise resources are ignored
173 'containers': [173 "containers": [
174 {174 {
175 'name': 'mm-pd-bot',175 "name": "mm-pd-bot",
176 'imageDetails': {'imagePath': 'mm_pd_bot:devel'},176 "imageDetails": {"imagePath": "mm_pd_bot:devel"},
177 'imagePullPolicy': 'Always',177 "imagePullPolicy": "Always",
178 'ports': [{'containerPort': 2160, 'protocol': 'TCP'}],178 "ports": [{"containerPort": 2160, "protocol": "TCP"}],
179 'envConfig': {'MM_PD_BOT_CFG': get_cfg_content('good_configuration')},179 "envConfig": {"MM_PD_BOT_CFG": get_cfg_content("good_configuration")},
180 }180 }
181 ],181 ],
182 'kubernetesResources': {182 "kubernetesResources": {
183 'ingressResources': [183 "ingressResources": [
184 {184 {
185 'name': 'mm-pd-bot-ingress',185 "name": "mm-pd-bot-ingress",
186 'spec': {186 "spec": {
187 'rules': [187 "rules": [
188 {188 {
189 'host': 'mm-pd-bot.example.com',189 "host": "mm-pd-bot.example.com",
190 'http': {190 "http": {
191 'paths': [191 "paths": [
192 {192 {
193 'path': '/bdddcacb-ab42-40ac-9106-4275c1db1519/',193 "path": "/bdddcacb-ab42-40ac-9106-4275c1db1519/",
194 'backend': {'serviceName': 'mm-pd-bot', 'servicePort': 2160},194 "backend": {"serviceName": "mm-pd-bot", "servicePort": 2160},
195 },195 },
196 ],196 ],
197 },197 },
198 },198 },
199 ],199 ],
200 },200 },
201 'annotations': {'nginx.ingress.kubernetes.io/ssl-redirect': 'false'},201 "annotations": {"nginx.ingress.kubernetes.io/ssl-redirect": "false"},
202 },202 },
203 ],203 ],
204 },204 },
205 },205 },
206 },206 },
207 'ssl': {207 "ssl": {
208 'config': {208 "config": {
209 'image_path': 'mm_pd_bot:devel',209 "image_path": "mm_pd_bot:devel",
210 'mm_pd_bot_cfg': get_cfg_content('ssl_configuration'),210 "mm_pd_bot_cfg": get_cfg_content("ssl_configuration"),
211 'ingress_whitelist_source_range': '10.0.69.0/24',211 "ingress_whitelist_source_range": "10.0.69.0/24",
212 'tls_secret_name': 'mm_pd_bot_secret',212 "tls_secret_name": "mm_pd_bot_secret",
213 },213 },
214 'pod_spec': {214 "pod_spec": {
215 'version': 3, # otherwise resources are ignored215 "version": 3, # otherwise resources are ignored
216 'containers': [216 "containers": [
217 {217 {
218 'name': 'mm-pd-bot',218 "name": "mm-pd-bot",
219 'imageDetails': {'imagePath': 'mm_pd_bot:devel'},219 "imageDetails": {"imagePath": "mm_pd_bot:devel"},
220 'imagePullPolicy': 'Always',220 "imagePullPolicy": "Always",
221 'ports': [{'containerPort': 2160, 'protocol': 'TCP'}],221 "ports": [{"containerPort": 2160, "protocol": "TCP"}],
222 'envConfig': {'MM_PD_BOT_CFG': get_cfg_content('ssl_configuration')},222 "envConfig": {"MM_PD_BOT_CFG": get_cfg_content("ssl_configuration")},
223 }223 }
224 ],224 ],
225 'kubernetesResources': {225 "kubernetesResources": {
226 'ingressResources': [226 "ingressResources": [
227 {227 {
228 'name': 'mm-pd-bot-ingress',228 "name": "mm-pd-bot-ingress",
229 'spec': {229 "spec": {
230 'rules': [230 "rules": [
231 {231 {
232 'host': 'mm-pd-bot.example.com',232 "host": "mm-pd-bot.example.com",
233 'http': {233 "http": {
234 'paths': [234 "paths": [
235 {235 {
236 'path': '/bdddcacb-ab42-40ac-9106-4275c1db1519/',236 "path": "/bdddcacb-ab42-40ac-9106-4275c1db1519/",
237 'backend': {'serviceName': 'mm-pd-bot', 'servicePort': 2160},237 "backend": {"serviceName": "mm-pd-bot", "servicePort": 2160},
238 },238 },
239 ],239 ],
240 },240 },
241 },241 },
242 ],242 ],
243 'tls': [{'hosts': ['mm-pd-bot.example.com'], 'secretName': 'mm_pd_bot_secret'}],243 "tls": [{"hosts": ["mm-pd-bot.example.com"], "secretName": "mm_pd_bot_secret"}],
244 },244 },
245 'annotations': {'nginx.ingress.kubernetes.io/whitelist-source-range': '10.0.69.0/24'},245 "annotations": {"nginx.ingress.kubernetes.io/whitelist-source-range": "10.0.69.0/24"},
246 },246 },
247 ],247 ],
248 },248 },
249 },249 },
250 },250 },
251 'ssl_without_whitelist': {251 "ssl_without_whitelist": {
252 'config': {252 "config": {
253 'image_path': 'mm_pd_bot:devel',253 "image_path": "mm_pd_bot:devel",
254 'mm_pd_bot_cfg': get_cfg_content('ssl_configuration'),254 "mm_pd_bot_cfg": get_cfg_content("ssl_configuration"),
255 'tls_secret_name': 'mm_pd_bot_secret',255 "tls_secret_name": "mm_pd_bot_secret",
256 },256 },
257 'pod_spec': {257 "pod_spec": {
258 'version': 3, # otherwise resources are ignored258 "version": 3, # otherwise resources are ignored
259 'containers': [259 "containers": [
260 {260 {
261 'name': 'mm-pd-bot',261 "name": "mm-pd-bot",
262 'imageDetails': {'imagePath': 'mm_pd_bot:devel'},262 "imageDetails": {"imagePath": "mm_pd_bot:devel"},
263 'imagePullPolicy': 'Always',263 "imagePullPolicy": "Always",
264 'ports': [{'containerPort': 2160, 'protocol': 'TCP'}],264 "ports": [{"containerPort": 2160, "protocol": "TCP"}],
265 'envConfig': {'MM_PD_BOT_CFG': get_cfg_content('ssl_configuration')},265 "envConfig": {"MM_PD_BOT_CFG": get_cfg_content("ssl_configuration")},
266 }266 }
267 ],267 ],
268 'kubernetesResources': {268 "kubernetesResources": {
269 'ingressResources': [269 "ingressResources": [
270 {270 {
271 'name': 'mm-pd-bot-ingress',271 "name": "mm-pd-bot-ingress",
272 'spec': {272 "spec": {
273 'rules': [273 "rules": [
274 {274 {
275 'host': 'mm-pd-bot.example.com',275 "host": "mm-pd-bot.example.com",
276 'http': {276 "http": {
277 'paths': [277 "paths": [
278 {278 {
279 'path': '/bdddcacb-ab42-40ac-9106-4275c1db1519/',279 "path": "/bdddcacb-ab42-40ac-9106-4275c1db1519/",
280 'backend': {'serviceName': 'mm-pd-bot', 'servicePort': 2160},280 "backend": {"serviceName": "mm-pd-bot", "servicePort": 2160},
281 },281 },
282 ],282 ],
283 },283 },
284 },284 },
285 ],285 ],
286 'tls': [{'hosts': ['mm-pd-bot.example.com'], 'secretName': 'mm_pd_bot_secret'}],286 "tls": [{"hosts": ["mm-pd-bot.example.com"], "secretName": "mm_pd_bot_secret"}],
287 },287 },
288 },288 },
289 ],289 ],
diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py
index 27bbaf3..d56fcde 100755
--- a/tests/unit/test_charm.py
+++ b/tests/unit/test_charm.py
@@ -39,8 +39,8 @@ class TestMmPdBotK8sCharmBlockedStatus(unittest.TestCase):
39 """Cleanup the harness."""39 """Cleanup the harness."""
40 self.harness.cleanup()40 self.harness.cleanup()
4141
42 @patch('charm.MmPdBotK8sCharm._validate_mm_pd_bot_configuration')42 @patch("charm.MmPdBotK8sCharm._validate_mm_pd_bot_configuration")
43 @patch('charm.MmPdBotK8sCharm._check_juju_settings')43 @patch("charm.MmPdBotK8sCharm._check_juju_settings")
44 def test_check_for_config_problems(self, mock_juju, mock_mm_pd_bot):44 def test_check_for_config_problems(self, mock_juju, mock_mm_pd_bot):
45 """Check the calls for config problems."""45 """Check the calls for config problems."""
46 self.harness.charm._check_for_config_problems()46 self.harness.charm._check_for_config_problems()
@@ -51,47 +51,47 @@ class TestMmPdBotK8sCharmBlockedStatus(unittest.TestCase):
51 """Check the required juju settings."""51 """Check the required juju settings."""
52 for scenario, values in TEST_JUJU_SETTINGS.items():52 for scenario, values in TEST_JUJU_SETTINGS.items():
53 with self.subTest(scenario=scenario):53 with self.subTest(scenario=scenario):
54 with self.assertLogs(level='ERROR') as logger:54 with self.assertLogs(level="ERROR") as logger:
55 with self.assertRaises(MmPdBotK8sCharmConfigError) as exc:55 with self.assertRaises(MmPdBotK8sCharmConfigError) as exc:
56 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # Load the default values56 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # Load the default values
57 self.harness.update_config(values['config'])57 self.harness.update_config(values["config"])
58 self.harness.charm._check_juju_settings()58 self.harness.charm._check_juju_settings()
59 self.assertEqual(sorted(logger.output), sorted(values['logger']))59 self.assertEqual(sorted(logger.output), sorted(values["logger"]))
60 self.assertEqual(str(exc.exception), values['expected'])60 self.assertEqual(str(exc.exception), values["expected"])
61 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run61 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run
6262
63 def test_validate_mm_pd_bot_configuration(self):63 def test_validate_mm_pd_bot_configuration(self):
64 """Check the MM_PD_BOT_CFG string is a valid INI."""64 """Check the MM_PD_BOT_CFG string is a valid INI."""
65 for scenario, values in VALIDATE_MM_PD_BOT_CFG.items():65 for scenario, values in VALIDATE_MM_PD_BOT_CFG.items():
66 with self.subTest(scenario=scenario):66 with self.subTest(scenario=scenario):
67 self.harness.update_config(values['config'])67 self.harness.update_config(values["config"])
68 if values['expected'] is True:68 if values["expected"] is True:
69 self.harness.charm._validate_mm_pd_bot_configuration()69 self.harness.charm._validate_mm_pd_bot_configuration()
70 else:70 else:
71 with self.assertLogs(level='ERROR') as logger:71 with self.assertLogs(level="ERROR") as logger:
72 with self.assertRaises(MmPdBotK8sCharmConfigError) as exc:72 with self.assertRaises(MmPdBotK8sCharmConfigError) as exc:
73 self.harness.charm._validate_mm_pd_bot_configuration()73 self.harness.charm._validate_mm_pd_bot_configuration()
74 self.assertEqual(str(exc.exception), values['expected'])74 self.assertEqual(str(exc.exception), values["expected"])
75 self.assertEqual(sorted(logger.output), sorted(values['logger']))75 self.assertEqual(sorted(logger.output), sorted(values["logger"]))
76 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run76 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run
7777
78 def test_load_mm_pd_bot_configuration(self):78 def test_load_mm_pd_bot_configuration(self):
79 """Test the loading of the configuration for the bot."""79 """Test the loading of the configuration for the bot."""
80 config = VALIDATE_MM_PD_BOT_CFG['good_configuration']['config']['mm_pd_bot_cfg']80 config = VALIDATE_MM_PD_BOT_CFG["good_configuration"]["config"]["mm_pd_bot_cfg"]
81 loaded_config = self.harness.charm._load_mm_pd_bot_configuration(config)81 loaded_config = self.harness.charm._load_mm_pd_bot_configuration(config)
82 self.assertIsInstance(loaded_config, configparser.ConfigParser)82 self.assertIsInstance(loaded_config, configparser.ConfigParser)
8383
84 config = VALIDATE_MM_PD_BOT_CFG['bad_formatted_configuration']['config']['mm_pd_bot_cfg']84 config = VALIDATE_MM_PD_BOT_CFG["bad_formatted_configuration"]["config"]["mm_pd_bot_cfg"]
85 with self.assertRaises(MmPdBotK8sCharmConfigError) as exc:85 with self.assertRaises(MmPdBotK8sCharmConfigError) as exc:
86 loaded_config = self.harness.charm._load_mm_pd_bot_configuration(config)86 loaded_config = self.harness.charm._load_mm_pd_bot_configuration(config)
87 self.assertEqual(str(exc.exception), VALIDATE_MM_PD_BOT_CFG['bad_formatted_configuration']['expected'])87 self.assertEqual(str(exc.exception), VALIDATE_MM_PD_BOT_CFG["bad_formatted_configuration"]["expected"])
8888
89 def test_configure_pod(self):89 def test_configure_pod(self):
90 """Test the pod configuration."""90 """Test the pod configuration."""
91 mock_event = MagicMock()91 mock_event = MagicMock()
9292
93 # Good configuration but not leader93 # Good configuration but not leader
94 self.harness.update_config(VALIDATE_MM_PD_BOT_CFG['good_configuration']['config'])94 self.harness.update_config(VALIDATE_MM_PD_BOT_CFG["good_configuration"]["config"])
95 self.harness.set_leader(False)95 self.harness.set_leader(False)
96 self.harness.charm.unit.status = BlockedStatus("Testing")96 self.harness.charm.unit.status = BlockedStatus("Testing")
97 self.harness.charm.configure_pod(mock_event)97 self.harness.charm.configure_pod(mock_event)
@@ -99,11 +99,11 @@ class TestMmPdBotK8sCharmBlockedStatus(unittest.TestCase):
99 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run99 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run
100100
101 # Good configuration and leader101 # Good configuration and leader
102 self.harness.update_config(VALIDATE_MM_PD_BOT_CFG['good_configuration']['config'])102 self.harness.update_config(VALIDATE_MM_PD_BOT_CFG["good_configuration"]["config"])
103 self.harness.set_leader(True)103 self.harness.set_leader(True)
104 self.harness.charm.unit.status = BlockedStatus("Testing")104 self.harness.charm.unit.status = BlockedStatus("Testing")
105 self.harness.charm.configure_pod(mock_event)105 self.harness.charm.configure_pod(mock_event)
106 with self.assertLogs(level='INFO') as logger:106 with self.assertLogs(level="INFO") as logger:
107 self.harness.charm.configure_pod(mock_event)107 self.harness.charm.configure_pod(mock_event)
108 self.assertEqual(108 self.assertEqual(
109 sorted(logger.output),109 sorted(logger.output),
@@ -113,8 +113,8 @@ class TestMmPdBotK8sCharmBlockedStatus(unittest.TestCase):
113 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run113 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run
114114
115 # Bad configuration and leader115 # Bad configuration and leader
116 config = VALIDATE_MM_PD_BOT_CFG['bad_formatted_configuration']['config']116 config = VALIDATE_MM_PD_BOT_CFG["bad_formatted_configuration"]["config"]
117 expected = VALIDATE_MM_PD_BOT_CFG['bad_formatted_configuration']['expected']117 expected = VALIDATE_MM_PD_BOT_CFG["bad_formatted_configuration"]["expected"]
118 self.harness.update_config(config)118 self.harness.update_config(config)
119 self.harness.set_leader(True)119 self.harness.set_leader(True)
120 self.harness.charm.configure_pod(mock_event)120 self.harness.charm.configure_pod(mock_event)
@@ -125,26 +125,26 @@ class TestMmPdBotK8sCharmBlockedStatus(unittest.TestCase):
125 """Check the crafting of the pod spec."""125 """Check the crafting of the pod spec."""
126 for scenario, values in VALIDATE_POD_SPEC.items():126 for scenario, values in VALIDATE_POD_SPEC.items():
127 with self.subTest(scenario=scenario):127 with self.subTest(scenario=scenario):
128 self.harness.update_config(values['config'])128 self.harness.update_config(values["config"])
129 self.harness.charm.bot_config = self.harness.charm._load_mm_pd_bot_configuration(129 self.harness.charm.bot_config = self.harness.charm._load_mm_pd_bot_configuration(
130 values['config']['mm_pd_bot_cfg']130 values["config"]["mm_pd_bot_cfg"]
131 )131 )
132 self.assertEqual(self.harness.charm._make_pod_spec(), values['pod_spec'])132 self.assertEqual(self.harness.charm._make_pod_spec(), values["pod_spec"])
133 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run133 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run
134134
135 def test_update_pod_spec_for_k8s_ingress(self):135 def test_update_pod_spec_for_k8s_ingress(self):
136 """Check the crafting of the ingress part of the pod spec."""136 """Check the crafting of the ingress part of the pod spec."""
137 for scenario, values in VALIDATE_POD_SPEC_AND_INGRESS.items():137 for scenario, values in VALIDATE_POD_SPEC_AND_INGRESS.items():
138 with self.subTest(scenario=scenario):138 with self.subTest(scenario=scenario):
139 self.harness.update_config(values['config'])139 self.harness.update_config(values["config"])
140 self.harness.charm.bot_config = self.harness.charm._load_mm_pd_bot_configuration(140 self.harness.charm.bot_config = self.harness.charm._load_mm_pd_bot_configuration(
141 values['config']['mm_pd_bot_cfg']141 values["config"]["mm_pd_bot_cfg"]
142 )142 )
143 pod_spec = self.harness.charm._make_pod_spec()143 pod_spec = self.harness.charm._make_pod_spec()
144 self.harness.charm._update_pod_spec_for_k8s_ingress(pod_spec)144 self.harness.charm._update_pod_spec_for_k8s_ingress(pod_spec)
145 self.assertEqual(pod_spec, values['pod_spec'])145 self.assertEqual(pod_spec, values["pod_spec"])
146 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run146 self.harness.update_config(JUJU_SETTINGS_DEFAULT) # You need to clean the config after each run
147147
148148
149if __name__ == '__main__':149if __name__ == "__main__":
150 unittest.main()150 unittest.main()

Subscribers

People subscribed via source and target branches