Merge ~mthaddon/charm-k8s-mattermost/+git/charm-k8s-mattermost:sidecar into charm-k8s-mattermost:master
- Git
- lp:~mthaddon/charm-k8s-mattermost/+git/charm-k8s-mattermost
- sidecar
- Merge into master
Proposed by
Tom Haddon
Status: | Work in progress |
---|---|
Proposed branch: | ~mthaddon/charm-k8s-mattermost/+git/charm-k8s-mattermost:sidecar |
Merge into: | charm-k8s-mattermost:master |
Diff against target: |
552 lines (+174/-111) 5 files modified
README.md (+7/-1) config.yaml (+2/-21) metadata.yaml (+10/-3) requirements.txt (+1/-1) src/charm.py (+154/-85) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mattermost Charmers | Pending | ||
Review via email: mp+401092@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 7202600... by Tom Haddon
-
Add some comments explaining necessary follow up
- 6f00e95... by Tom Haddon
-
Add a comment about action bug
- ca6af29... by Tom Haddon
-
Use is_running and log state for a bug report
- e0e7062... by Tom Haddon
-
Remove spurious logging and add a reference to issue with charm upgrades losing state
- cb7ab97... by Tom Haddon
-
Remove manifest.yaml which is now machine-generated
- 5f59fca... by Tom Haddon
-
Remove unused bases definition from metadata.yaml
Unmerged commits
- 5f59fca... by Tom Haddon
-
Remove unused bases definition from metadata.yaml
- cb7ab97... by Tom Haddon
-
Remove manifest.yaml which is now machine-generated
- e0e7062... by Tom Haddon
-
Remove spurious logging and add a reference to issue with charm upgrades losing state
- ca6af29... by Tom Haddon
-
Use is_running and log state for a bug report
- 6f00e95... by Tom Haddon
-
Add a comment about action bug
- 7202600... by Tom Haddon
-
Add some comments explaining necessary follow up
- 0df48e9... by Tom Haddon
-
Convert to sidecar
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/README.md b/README.md |
2 | index 4a7ef53..1f61f15 100644 |
3 | --- a/README.md |
4 | +++ b/README.md |
5 | @@ -21,7 +21,13 @@ To deploy the charm and relate it to [the PostgreSQL K8s charm](https://charmhub |
6 | Kubernetes model: |
7 | |
8 | juju deploy cs:~postgresql-charmers/postgresql-k8s postgresql |
9 | - juju deploy cs:~mattermost-charmers/mattermost --config juju-external-hostname=foo.internal |
10 | + # Can't publish to charmhub or the charmstore yet. |
11 | + # juju deploy cs:~mattermost-charmers/mattermost --config juju-external-hostname=foo.internal |
12 | + # Build locally |
13 | + git checkout -b sidecar https://git.launchpad.net/~mthaddon/charm-k8s-mattermost/+git/charm-k8s-mattermost |
14 | + cd charm-k8s-mattermost |
15 | + charmcraft build |
16 | + juju deploy ./mattermost.charm --resource mattermost-image=mattermostcharmers/mattermost:v5.33.3-20.04_edge |
17 | juju relate mattermost postgresql:db |
18 | juju expose mattermost |
19 | |
20 | diff --git a/config.yaml b/config.yaml |
21 | index 5827200..68c34eb 100644 |
22 | --- a/config.yaml |
23 | +++ b/config.yaml |
24 | @@ -34,25 +34,6 @@ options: |
25 | Some features are not available without a licence. For more |
26 | information, consult the Mattermost documentation. |
27 | default: '' |
28 | - mattermost_image_path: |
29 | - type: string |
30 | - description: | |
31 | - The location of the image to use, e.g. "registry.example.com/mattermost:v1". |
32 | - |
33 | - Switching to a newer image version will initiate an upgrade of Mattermost. |
34 | - |
35 | - This setting is required. |
36 | - default: mattermostcharmers/mattermost:v5.33.3-20.04_edge |
37 | - mattermost_image_username: |
38 | - type: string |
39 | - description: | |
40 | - The username for accessing the registry specified in mattermost_image_path. |
41 | - default: '' |
42 | - mattermost_image_password: |
43 | - type: string |
44 | - description: | |
45 | - The password associated with mattermost_image_username for accessing the registry specified in mattermost_image_path. |
46 | - default: '' |
47 | outbound_proxy: |
48 | type: string |
49 | description: The proxy to use for outbound requests. |
50 | @@ -183,10 +164,10 @@ options: |
51 | |
52 | This will not work unless the administrators of login.ubuntu.com have created a suitable SAML config first. |
53 | default: false |
54 | - use_canonical_defaults: |
55 | + use_enterprise_defaults: |
56 | type: boolean |
57 | description: | |
58 | - If set, apply miscellaneous Mattermost settings as used by Canonical. |
59 | + If set, apply miscellaneous Mattermost settings as used in an Enterprise deployment. |
60 | default: false |
61 | use_experimental_saml_library: |
62 | type: boolean |
63 | diff --git a/metadata.yaml b/metadata.yaml |
64 | index 75f7830..1dfabb2 100644 |
65 | --- a/metadata.yaml |
66 | +++ b/metadata.yaml |
67 | @@ -7,9 +7,16 @@ description: | |
68 | Mattermost is a flexible, open source messaging platform that enables |
69 | secure team collaboration. |
70 | https://mattermost.com |
71 | -min-juju-version: 2.8.0 # charm storage in state |
72 | -series: |
73 | - - kubernetes |
74 | + |
75 | +containers: |
76 | + mattermost: |
77 | + resource: mattermost-image |
78 | + |
79 | +resources: |
80 | + mattermost-image: |
81 | + type: oci-image |
82 | + description: Docker image for Mattermost to run |
83 | + |
84 | requires: |
85 | db: |
86 | interface: pgsql |
87 | diff --git a/requirements.txt b/requirements.txt |
88 | index fd6adcd..873493e 100644 |
89 | --- a/requirements.txt |
90 | +++ b/requirements.txt |
91 | @@ -1,2 +1,2 @@ |
92 | -ops |
93 | +https://github.com/canonical/operator/archive/refs/heads/master.zip |
94 | ops-lib-pgsql |
95 | diff --git a/src/charm.py b/src/charm.py |
96 | index 9416f62..14fa59c 100755 |
97 | --- a/src/charm.py |
98 | +++ b/src/charm.py |
99 | @@ -31,6 +31,7 @@ from ops.model import ( |
100 | from utils import extend_list_merging_dicts_matched_by_key |
101 | |
102 | import logging |
103 | +import yaml |
104 | |
105 | |
106 | pgsql = ops.lib.use("pgsql", 1, "postgresql-charmers@lists.launchpad.net") |
107 | @@ -45,7 +46,7 @@ METRICS_PORT = 8067 |
108 | DATABASE_NAME = 'mattermost' |
109 | LICENCE_SECRET_KEY_NAME = 'licence' |
110 | REQUIRED_S3_SETTINGS = ['s3_bucket', 's3_region', 's3_access_key_id', 's3_secret_access_key'] |
111 | -REQUIRED_SETTINGS = ['mattermost_image_path'] |
112 | +REQUIRED_SETTINGS = [] |
113 | REQUIRED_SSO_SETTINGS = ['licence', 'site_url'] |
114 | SAML_IDP_CRT = 'saml-idp.crt' |
115 | |
116 | @@ -76,26 +77,6 @@ def check_ranges(ranges, name): |
117 | return '{}: invalid network(s): {}'.format(name, ', '.join(invalid_networks)) |
118 | |
119 | |
120 | -def get_container(pod_spec, container_name): |
121 | - """Find and return the first container in pod_spec whose name is container_name, otherwise return None.""" |
122 | - for container in pod_spec['containers']: |
123 | - if container['name'] == container_name: |
124 | - return container |
125 | - raise ValueError("Unable to find container named '{}' in pod spec".format(container_name)) |
126 | - |
127 | - |
128 | -def get_env_config(pod_spec, container_name): |
129 | - """Return the envConfig of the container in pod_spec whose name is container_name, otherwise return None. |
130 | - |
131 | - If the container exists but has no envConfig, raise KeyError. |
132 | - """ |
133 | - container = get_container(pod_spec, container_name) |
134 | - if 'envConfig' in container: |
135 | - return container['envConfig'] |
136 | - else: |
137 | - raise ValueError("Unable to find envConfig for container named '{}'".format(container_name)) |
138 | - |
139 | - |
140 | class MattermostK8sCharm(CharmBase): |
141 | |
142 | state = StoredState() |
143 | @@ -104,21 +85,94 @@ class MattermostK8sCharm(CharmBase): |
144 | def __init__(self, *args): |
145 | super().__init__(*args) |
146 | |
147 | - self.framework.observe(self.on.start, self.configure_pod) |
148 | - self.framework.observe(self.on.config_changed, self.configure_pod) |
149 | - self.framework.observe(self.on.leader_elected, self.configure_pod) |
150 | - self.framework.observe(self.on.upgrade_charm, self.configure_pod) |
151 | + self.framework.observe(self.on.start, self._on_config_changed) |
152 | + self.framework.observe(self.on.config_changed, self._on_config_changed) |
153 | + self.framework.observe(self.on.leader_elected, self._on_config_changed) |
154 | + self.framework.observe(self.on.upgrade_charm, self._on_upgrade_charm) |
155 | + |
156 | + self.framework.observe(self.on.mattermost_pebble_ready, self._on_mattermost_pebble_ready) |
157 | |
158 | # actions |
159 | self.framework.observe(self.on.grant_admin_role_action, self._on_grant_admin_role_action) |
160 | |
161 | + # state |
162 | + self.state.set_default(db_conn_str=None, db_uri=None, db_ro_uris=[], mattermost_pebble_ready=False) |
163 | + |
164 | # database |
165 | - self.state.set_default(db_conn_str=None, db_uri=None, db_ro_uris=[]) |
166 | self.db = pgsql.PostgreSQLClient(self, 'db') |
167 | self.framework.observe(self.db.on.database_relation_joined, self._on_database_relation_joined) |
168 | self.framework.observe(self.db.on.master_changed, self._on_master_changed) |
169 | self.framework.observe(self.db.on.standby_changed, self._on_standby_changed) |
170 | - self.framework.observe(self.on.db_master_available, self.configure_pod) |
171 | + self.framework.observe(self.on.db_master_available, self._on_config_changed) |
172 | + |
173 | + def _get_pebble_config(self, event): |
174 | + """Generate our pebble config.""" |
175 | + env_config = self._get_env_config() |
176 | + pebble_config = { |
177 | + "summary": "Mattermost layer", |
178 | + "description": "Mattermost layer", |
179 | + "services": { |
180 | + "mattermost": { |
181 | + "override": "replace", |
182 | + "summary": "Mattermost service", |
183 | + "command": "/mattermost/bin/mattermost", |
184 | + "startup": "enabled", |
185 | + "environment": env_config, |
186 | + } |
187 | + }, |
188 | + } |
189 | + return pebble_config |
190 | + |
191 | + def _on_upgrade_charm(self, event): |
192 | + """Handle the upgrade charm event.""" |
193 | + # XXX: State should persist across charm upgrades, but doesn't currently. |
194 | + # This means the relation data won't work as expected. See |
195 | + # https://github.com/canonical/operator/issues/506 for more details. |
196 | + self.state.mattermost_pebble_ready = False |
197 | + return |
198 | + |
199 | + def _on_mattermost_pebble_ready(self, event): |
200 | + """Handle the on pebble ready event.""" |
201 | + self.state.mattermost_pebble_ready = True |
202 | + self._on_config_changed(event) |
203 | + |
204 | + def _on_config_changed(self, event): |
205 | + """Handle config-changed event.""" |
206 | + if not self.state.mattermost_pebble_ready: |
207 | + logger.info( |
208 | + "Got a config changed event, but the workload isn't ready yet. Doing nothing, config will be " |
209 | + "picked up when workload is ready." |
210 | + ) |
211 | + return |
212 | + |
213 | + if not self.state.db_uri: |
214 | + self.unit.status = WaitingStatus('Waiting for database relation') |
215 | + event.defer() |
216 | + return |
217 | + |
218 | + problems = self._check_for_config_problems() |
219 | + if problems: |
220 | + self.unit.status = BlockedStatus(problems) |
221 | + return |
222 | + |
223 | + pebble_config = self._get_pebble_config(event) |
224 | + if not pebble_config: |
225 | + # Charm will be in blocked status. |
226 | + return |
227 | + |
228 | + container = self.unit.get_container("mattermost") |
229 | + |
230 | + plan = container.get_plan() |
231 | + if not plan or (plan.services != pebble_config["services"]): |
232 | + logger.debug("About to add_layer with pebble_config:\n{}".format(yaml.dump(pebble_config))) |
233 | + container.add_layer("mattermost", pebble_config, combine=True) |
234 | + |
235 | + service = container.get_service("mattermost") |
236 | + if service.is_running(): |
237 | + container.stop("mattermost") |
238 | + container.start("mattermost") |
239 | + |
240 | + self.unit.status = ActiveStatus() |
241 | |
242 | def _on_database_relation_joined(self, event: pgsql.DatabaseRelationJoinedEvent): |
243 | """Handle db-relation-joined.""" |
244 | @@ -185,6 +239,8 @@ class MattermostK8sCharm(CharmBase): |
245 | pod_config = self._make_pod_config() |
246 | pod_config.update(self._make_s3_pod_config()) |
247 | |
248 | + # XXX: Readiness probe details below, needs to be implemented when |
249 | + # https://bugs.launchpad.net/juju/+bug/1922265 is addressed. |
250 | return { |
251 | 'version': 3, # otherwise resources are ignored |
252 | 'containers': [ |
253 | @@ -200,12 +256,12 @@ class MattermostK8sCharm(CharmBase): |
254 | ], |
255 | } |
256 | |
257 | - def _make_pod_config(self): |
258 | + def _make_env_config(self): |
259 | """Return an envConfig with some core configuration.""" |
260 | config = self.model.config |
261 | # https://github.com/mattermost/mattermost-server/pull/14666 |
262 | db_uri = self.state.db_uri.replace('postgresql://', 'postgres://') |
263 | - pod_config = { |
264 | + env_config = { |
265 | 'MATTERMOST_HTTPD_LISTEN_PORT': CONTAINER_PORT, |
266 | 'MM_CONFIG': db_uri, |
267 | 'MM_SQLSETTINGS_DATASOURCE': db_uri, |
268 | @@ -219,18 +275,18 @@ class MattermostK8sCharm(CharmBase): |
269 | } |
270 | |
271 | if config['primary_team']: |
272 | - pod_config['MM_TEAMSETTINGS_EXPERIMENTALPRIMARYTEAM'] = config['primary_team'] |
273 | + env_config['MM_TEAMSETTINGS_EXPERIMENTALPRIMARYTEAM'] = config['primary_team'] |
274 | |
275 | if config['site_url']: |
276 | - pod_config['MM_SERVICESETTINGS_SITEURL'] = config['site_url'] |
277 | + env_config['MM_SERVICESETTINGS_SITEURL'] = config['site_url'] |
278 | |
279 | if config['outbound_proxy']: |
280 | - pod_config['HTTP_PROXY'] = config['outbound_proxy'] |
281 | - pod_config['HTTPS_PROXY'] = config['outbound_proxy'] |
282 | + env_config['HTTP_PROXY'] = config['outbound_proxy'] |
283 | + env_config['HTTPS_PROXY'] = config['outbound_proxy'] |
284 | if config['outbound_proxy_exceptions']: |
285 | - pod_config['NO_PROXY'] = config['outbound_proxy_exceptions'] |
286 | + env_config['NO_PROXY'] = config['outbound_proxy_exceptions'] |
287 | |
288 | - return pod_config |
289 | + return env_config |
290 | |
291 | def _missing_charm_settings(self): |
292 | """Return a list of settings required to satisfy configuration dependencies, or else an empty list.""" |
293 | @@ -241,9 +297,6 @@ class MattermostK8sCharm(CharmBase): |
294 | if config['clustering'] and not config['licence']: |
295 | missing.add('licence') |
296 | |
297 | - if config['mattermost_image_username'] and not config['mattermost_image_password']: |
298 | - missing.add('mattermost_image_password') |
299 | - |
300 | if config['performance_monitoring_enabled'] and not config['licence']: |
301 | missing.add('licence') |
302 | |
303 | @@ -258,24 +311,26 @@ class MattermostK8sCharm(CharmBase): |
304 | |
305 | return sorted(missing) |
306 | |
307 | - def _make_s3_pod_config(self): |
308 | + def _update_env_config_for_s3(self, env_config): |
309 | """Return an envConfig of S3 settings, if any.""" |
310 | config = self.model.config |
311 | if not config['s3_enabled']: |
312 | - return {} |
313 | + return |
314 | |
315 | - return { |
316 | - 'MM_FILESETTINGS_DRIVERNAME': 'amazons3', |
317 | - 'MM_FILESETTINGS_MAXFILESIZE': str(config['max_file_size'] * 1048576), # LP:1881227 |
318 | - 'MM_FILESETTINGS_AMAZONS3SSL': 'true', # defaults to true; belt and braces |
319 | - 'MM_FILESETTINGS_AMAZONS3ENDPOINT': config['s3_endpoint'], |
320 | - 'MM_FILESETTINGS_AMAZONS3BUCKET': config['s3_bucket'], |
321 | - 'MM_FILESETTINGS_AMAZONS3REGION': config['s3_region'], |
322 | - 'MM_FILESETTINGS_AMAZONS3ACCESSKEYID': config['s3_access_key_id'], |
323 | - 'MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY': config['s3_secret_access_key'], |
324 | - 'MM_FILESETTINGS_AMAZONS3SSE': 'true' if config['s3_server_side_encryption'] else 'false', |
325 | - 'MM_FILESETTINGS_AMAZONS3TRACE': 'true' if config['debug'] else 'false', |
326 | - } |
327 | + env_config.update( |
328 | + { |
329 | + 'MM_FILESETTINGS_DRIVERNAME': 'amazons3', |
330 | + 'MM_FILESETTINGS_MAXFILESIZE': str(config['max_file_size'] * 1048576), # LP:1881227 |
331 | + 'MM_FILESETTINGS_AMAZONS3SSL': 'true', # defaults to true; belt and braces |
332 | + 'MM_FILESETTINGS_AMAZONS3ENDPOINT': config['s3_endpoint'], |
333 | + 'MM_FILESETTINGS_AMAZONS3BUCKET': config['s3_bucket'], |
334 | + 'MM_FILESETTINGS_AMAZONS3REGION': config['s3_region'], |
335 | + 'MM_FILESETTINGS_AMAZONS3ACCESSKEYID': config['s3_access_key_id'], |
336 | + 'MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY': config['s3_secret_access_key'], |
337 | + 'MM_FILESETTINGS_AMAZONS3SSE': 'true' if config['s3_server_side_encryption'] else 'false', |
338 | + 'MM_FILESETTINGS_AMAZONS3TRACE': 'true' if config['debug'] else 'false', |
339 | + } |
340 | + ) |
341 | |
342 | def _update_pod_spec_for_k8s_ingress(self, pod_spec): |
343 | """Add resources to pod_spec configuring site ingress, if needed.""" |
344 | @@ -288,6 +343,7 @@ class MattermostK8sCharm(CharmBase): |
345 | if not parsed.scheme.startswith('http'): |
346 | return |
347 | |
348 | + # XXX: This will need to be captured in the nginx-ingress-integrator relation. |
349 | annotations = {'nginx.ingress.kubernetes.io/proxy-body-size': '{}m'.format(self.model.config['max_file_size'])} |
350 | ingress = { |
351 | "name": "{}-ingress".format(self.app.name), |
352 | @@ -304,6 +360,7 @@ class MattermostK8sCharm(CharmBase): |
353 | ] |
354 | }, |
355 | } |
356 | + # XXX This will need to be captured in the nginx-ingress-integrator relation. |
357 | if parsed.scheme == 'https': |
358 | ingress['spec']['tls'] = [{'hosts': [parsed.hostname]}] |
359 | tls_secret_name = self.model.config['tls_secret_name'] |
360 | @@ -363,37 +420,34 @@ class MattermostK8sCharm(CharmBase): |
361 | } |
362 | ] |
363 | |
364 | - def _update_pod_spec_for_licence(self, pod_spec): |
365 | + def _update_env_config_for_licence(self, env_config): |
366 | """Update pod_spec to make the licence, if configured, available to Mattermost.""" |
367 | config = self.model.config |
368 | if not config['licence']: |
369 | return |
370 | |
371 | - secrets = pod_spec['kubernetesResources'].get('secrets', []) |
372 | - secrets = extend_list_merging_dicts_matched_by_key(secrets, self._make_licence_k8s_secrets(), key='name') |
373 | - pod_spec['kubernetesResources']['secrets'] = secrets |
374 | + # XXX: Per https://bugs.launchpad.net/juju/+bug/1854759 and |
375 | + # https://bugs.launchpad.net/juju/+bug/1878120 we don't currently |
376 | + # have a way of interacting with k8s secrets. Current guidance is |
377 | + # to use juju config directly for this, as we're doing here. |
378 | + LICENCE_FILE = "/mattermost/licence.txt" |
379 | + with open(LICENCE_FILE, 'w') as licence_file: |
380 | + license_file.write(config['licence']) |
381 | |
382 | - container = get_container(pod_spec, self.app.name) |
383 | - volume_config = container.get('volumeConfig', []) |
384 | - volume_config = extend_list_merging_dicts_matched_by_key( |
385 | - volume_config, self._make_licence_volume_configs(), key='name' |
386 | + env_config.update( |
387 | + {'MM_SERVICESETTINGS_LICENSEFILELOCATION': LICENCE_FILE}, |
388 | ) |
389 | - container['volumeConfig'] = volume_config |
390 | |
391 | - get_env_config(pod_spec, self.app.name).update( |
392 | - {'MM_SERVICESETTINGS_LICENSEFILELOCATION': '/secrets/licence.txt'}, |
393 | - ) |
394 | - |
395 | - def _update_pod_spec_for_canonical_defaults(self, pod_spec): |
396 | - """Update pod_spec with various Mattermost settings particular to Canonical's deployment. |
397 | + def _update_env_config_for_enterprise_defaults(self, env_config): |
398 | + """Update env_config with various Mattermost settings particular to an Enterprise deployment. |
399 | |
400 | These settings may be less generally useful, and so they are controlled here as a unit. |
401 | """ |
402 | config = self.model.config |
403 | - if not config['use_canonical_defaults']: |
404 | + if not config['use_enterprise_defaults']: |
405 | return |
406 | |
407 | - get_env_config(pod_spec, self.app.name).update( |
408 | + env_config.update( |
409 | { |
410 | # If this is off, users can't turn it on themselves. |
411 | 'MM_SERVICESETTINGS_CLOSEUNUSEDDIRECTMESSAGES': 'true', |
412 | @@ -410,8 +464,8 @@ class MattermostK8sCharm(CharmBase): |
413 | } |
414 | ) |
415 | |
416 | - def _update_pod_spec_for_clustering(self, pod_spec): |
417 | - """Update pod_spec with clustering settings, varying the cluster name on the application name. |
418 | + def _update_env_config_for_clustering(self, env_config): |
419 | + """Update env_config with clustering settings, varying the cluster name on the application name. |
420 | |
421 | This is done so that blue/green deployments in the same model won't talk to each other. |
422 | """ |
423 | @@ -419,7 +473,7 @@ class MattermostK8sCharm(CharmBase): |
424 | if not config['clustering']: |
425 | return |
426 | |
427 | - get_env_config(pod_spec, self.app.name).update( |
428 | + env_config.update( |
429 | { |
430 | "MM_CLUSTERSETTINGS_ENABLE": "true", |
431 | "MM_CLUSTERSETTINGS_CLUSTERNAME": '{}-{}'.format(self.app.name, os.environ['JUJU_MODEL_UUID']), |
432 | @@ -427,8 +481,8 @@ class MattermostK8sCharm(CharmBase): |
433 | } |
434 | ) |
435 | |
436 | - def _update_pod_spec_for_sso(self, pod_spec): |
437 | - """Update pod_spec with settings to use login.ubuntu.com via SAML for single sign-on. |
438 | + def _update_env_config_for_sso(self, env_config): |
439 | + """Update env_config with settings to use login.ubuntu.com via SAML for single sign-on. |
440 | |
441 | SAML_IDP_CRT must be generated and installed manually by a human (see README.md). |
442 | """ |
443 | @@ -438,7 +492,7 @@ class MattermostK8sCharm(CharmBase): |
444 | site_hostname = urlparse(config['site_url']).hostname |
445 | use_experimental_saml_library = 'true' if config['use_experimental_saml_library'] else 'false' |
446 | |
447 | - get_env_config(pod_spec, self.app.name).update( |
448 | + env_config.update( |
449 | { |
450 | 'MM_EMAILSETTINGS_ENABLESIGNINWITHEMAIL': 'false', |
451 | 'MM_EMAILSETTINGS_ENABLESIGNINWITHUSERNAME': 'false', |
452 | @@ -463,23 +517,24 @@ class MattermostK8sCharm(CharmBase): |
453 | } |
454 | ) |
455 | |
456 | - def _update_pod_spec_for_performance_monitoring(self, pod_spec): |
457 | + def _update_env_config_for_performance_monitoring(self, env_config): |
458 | """Update pod_spec with settings for the Prometheus exporter.""" |
459 | config = self.model.config |
460 | if not config['performance_monitoring_enabled']: |
461 | return |
462 | |
463 | - get_env_config(pod_spec, self.app.name).update( |
464 | + env_config.update( |
465 | { |
466 | 'MM_METRICSSETTINGS_ENABLE': 'true' if config['performance_monitoring_enabled'] else 'false', |
467 | 'MM_METRICSSETTINGS_LISTENADDRESS': ':{}'.format(METRICS_PORT), |
468 | } |
469 | ) |
470 | |
471 | + # XXX: See LP:1923820. |
472 | # Ordinarily pods are selected for scraping by the in-cluster |
473 | # Prometheus based on their annotations. Unfortunately Juju |
474 | - # doesn't support pod annotations yet (LP:1884177). When it |
475 | - # does, here are the annotations we'll need to add: |
476 | + # doesn't support pod annotations yet. |
477 | + # Here are the annotations we'll need to add: |
478 | |
479 | # [ fetch or create annotations dict ] |
480 | # annotations.update({ |
481 | @@ -490,14 +545,14 @@ class MattermostK8sCharm(CharmBase): |
482 | # }) |
483 | # [ store annotations in pod_spec ] |
484 | |
485 | - def _update_pod_spec_for_push(self, pod_spec): |
486 | + def _update_env_config_for_push(self, env_config): |
487 | """Update pod_spec with settings for Mattermost HPNS (hosted push notification service).""" |
488 | config = self.model.config |
489 | if not config['push_notification_server']: |
490 | return |
491 | contents = 'full' if config['push_notifications_include_message_snippet'] else 'id_loaded' |
492 | |
493 | - get_env_config(pod_spec, self.app.name).update( |
494 | + env_config.update( |
495 | { |
496 | 'MM_EMAILSETTINGS_SENDPUSHNOTIFICATIONS': 'true', |
497 | 'MM_EMAILSETTINGS_PUSHNOTIFICATIONCONTENTS': contents, |
498 | @@ -505,16 +560,29 @@ class MattermostK8sCharm(CharmBase): |
499 | } |
500 | ) |
501 | |
502 | - def _update_pod_spec_for_smtp(self, pod_spec): |
503 | + def _update_env_config_for_smtp(self, env_config): |
504 | """Update pod_spec with settings for an outgoing SMTP relay.""" |
505 | config = self.model.config |
506 | if not config['smtp_host']: |
507 | return |
508 | |
509 | - get_env_config(pod_spec, self.app.name).update( |
510 | + env_config.update( |
511 | {'MM_EMAILSETTINGS_SMTPPORT': 25, 'MM_EMAILSETTINGS_SMTPSERVER': config['smtp_host']} |
512 | ) |
513 | |
514 | + def _get_env_config(self): |
515 | + """Assemble our environment configuration.""" |
516 | + env_config = self._make_env_config() |
517 | + self._update_env_config_for_s3(env_config) |
518 | + self._update_env_config_for_enterprise_defaults(env_config) |
519 | + self._update_env_config_for_clustering(env_config) |
520 | + self._update_env_config_for_licence(env_config) |
521 | + self._update_env_config_for_performance_monitoring(env_config) |
522 | + self._update_env_config_for_push(env_config) |
523 | + self._update_env_config_for_sso(env_config) |
524 | + self._update_env_config_for_smtp(env_config) |
525 | + return env_config |
526 | + |
527 | def configure_pod(self, event): |
528 | """Assemble the pod spec and apply it, if possible.""" |
529 | if not self.state.db_uri: |
530 | @@ -533,7 +601,7 @@ class MattermostK8sCharm(CharmBase): |
531 | |
532 | self.unit.status = MaintenanceStatus('Assembling pod spec') |
533 | pod_spec = self._make_pod_spec() |
534 | - self._update_pod_spec_for_canonical_defaults(pod_spec) |
535 | + self._update_pod_spec_for_enterprise_defaults(pod_spec) |
536 | self._update_pod_spec_for_clustering(pod_spec) |
537 | self._update_pod_spec_for_k8s_ingress(pod_spec) |
538 | self._update_pod_spec_for_licence(pod_spec) |
539 | @@ -548,6 +616,7 @@ class MattermostK8sCharm(CharmBase): |
540 | |
541 | def _on_grant_admin_role_action(self, event): |
542 | """Handle the grant-admin-role action.""" |
543 | + # XXX: Currently fails due to https://bugs.launchpad.net/juju/+bug/1923822 |
544 | user = event.params["user"] |
545 | cmd = ["/mattermost/bin/mattermost", "roles", "system_admin", user] |
546 | granted = subprocess.run(cmd, capture_output=True) |
547 | @@ -562,4 +631,4 @@ class MattermostK8sCharm(CharmBase): |
548 | |
549 | |
550 | if __name__ == '__main__': |
551 | - main(MattermostK8sCharm, use_juju_for_storage=True) |
552 | + main(MattermostK8sCharm) |