Merge ~pjdc/charm-k8s-mattermost/+git/charm-k8s-mattermost:psql-relation into charm-k8s-mattermost:master

Proposed by Paul Collins
Status: Merged
Approved by: Paul Collins
Approved revision: 03cd18a78f5b7c66418f16343256f155f6476c6a
Merged at revision: 89623f2407b385a6b46e68b05774d035149bbb9b
Proposed branch: ~pjdc/charm-k8s-mattermost/+git/charm-k8s-mattermost:psql-relation
Merge into: charm-k8s-mattermost:master
Diff against target: 295 lines (+125/-47)
8 files modified
.gitignore (+1/-0)
.gitmodules (+3/-0)
README.md (+32/-16)
config.yaml (+0/-17)
lib/interface/pgsql (+1/-0)
metadata.yaml (+6/-1)
mod/interface-pgsql (+1/-0)
src/charm.py (+81/-13)
Reviewer Review Type Date Requested Status
Stuart Bishop (community) Approve
Canonical IS Reviewers Pending
Review via email: mp+384315@code.launchpad.net

Commit message

add db relation and wire it up

To post a comment you must log in.
Revision history for this message
Paul Collins (pjdc) wrote :
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
Stuart Bishop (stub) wrote :

A minor tweak to the docs (postgresql:db), and update the pgsql interface with code landed yesterday. Juju 2.8 is now mostly enforcing the limit: setting in the relation definition, so you can remove some ugly code.

review: Approve
Revision history for this message
Stuart Bishop (stub) wrote :

I also note that this charm does not handle the db relation being removed, but that is fine as the app will fail the same way no matter its db creds becoming invalid or the db creds no longer existing.

Revision history for this message
Paul Collins (pjdc) wrote :

Reply re offer/consume and egress-subnets.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 89623f2407b385a6b46e68b05774d035149bbb9b

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.gitignore b/.gitignore
2new file mode 100644
3index 0000000..b25c15b
4--- /dev/null
5+++ b/.gitignore
6@@ -0,0 +1 @@
7+*~
8diff --git a/.gitmodules b/.gitmodules
9index d3eb83f..49f5dd7 100644
10--- a/.gitmodules
11+++ b/.gitmodules
12@@ -4,3 +4,6 @@
13 [submodule "mod/resource-oci-image"]
14 path = mod/resource-oci-image
15 url = https://github.com/johnsca/resource-oci-image
16+[submodule "mod/interface-pgsql"]
17+ path = mod/interface-pgsql
18+ url = lp:~stub/interface-pgsql/+git/operator
19diff --git a/README.md b/README.md
20index 2024a9c..8a82fca 100644
21--- a/README.md
22+++ b/README.md
23@@ -5,28 +5,20 @@ configurable to use a postgresql backend.
24
25 ## Overview
26
27-This is a k8s charm and can only be deployed to to a Juju k8s cloud,
28-attached to a controller using `juju add-k8s`.
29+This is a k8s workload charm and can only be deployed to to a Juju k8s
30+cloud, attached to a controller using `juju add-k8s`.
31
32-Configuration for the Mattermost image is in standard Juju config. In
33-particular:
34+On a fresh deployment, you need to set Mattermost in Open Server mode,
35+which will let you create an admin account. Once this is done, you
36+can restrict access with:
37
38-* `pg_db_host`, `pg_db_port`, `pg_user`, `pg_password`: this charm should be
39- able in the future, to relate to postgresql, but as of now these parameters
40- allow setting up the connection.
41-
42-On a fresh deployment, you need to set Mattermost in Open Server mode, which will let you create an admin account.
43-Once this is done, you can restrict access with:
44-
45-```
46-juju config mattermost open_server=false
47-```
48+ juju config mattermost open_server=false
49
50 ## Details
51
52 See config option descriptions in config.yaml.
53
54-## Quickstart
55+## Getting Started
56
57 Notes for deploying a test setup locally using microk8s:
58
59@@ -49,6 +41,30 @@ Notes for deploying a test setup locally using microk8s:
60 juju bootstrap myk8s
61 juju add-model mattermost-test
62 juju deploy ./charm-k8s-mattermost --resource mattermost_image=localhost:32000/mattermost:latest mattermost
63- juju config mattermost pg_db_host=10.1.1.1 pg_password=secret
64 juju wait
65 juju status
66+
67+The charm will not function without a database, so you will need to
68+deploy `cs:postgresql` somewhere.
69+
70+If postgresql is deployed in the same model you plan to use for
71+mattermost, simply use `juju relate mattermost postgresql:db`. (This
72+deployment style is recommended for testing purposes only.)
73+
74+Cross-model relations are also supported. Create a suitable model on
75+a different cloud, for example, LXD or OpenStack.
76+
77+ juju switch database
78+ juju deploy cs:postgresql
79+ juju offer postgresql:db
80+
81+In most k8s deployments, traffic to external services from worker pods
82+will be SNATed by some part of the infrastructure. You will need to
83+know what the source addresses or address range is for the next step.
84+
85+ juju switch mattermost-test
86+ juju find-offers # note down offer URL; example used below:
87+ juju relate mattermost admin/database.postgresql --via 10.9.8.0/24
88+
89+(In the case of postgresql, `--via` is needed so that the charm can
90+configure `pga_hba.conf` to let the k8s pods connect to the database.)
91diff --git a/config.yaml b/config.yaml
92index dd116e8..603ba30 100644
93--- a/config.yaml
94+++ b/config.yaml
95@@ -7,20 +7,3 @@ options:
96 type: boolean
97 description: Allows users to sign up from the root page without an invite
98 default: false
99- pg_db_host:
100- type: string
101- description: The hostname or IP of the postgresql backend.
102- default: ''
103- pg_db_port:
104- type: int
105- description: The port number for the postgresql backend.
106- default: 5432
107- pg_user:
108- type: string
109- description: The postgresql user.
110- default: 'mattermost'
111- pg_password:
112- type: string
113- description: The postgresql password.
114- default: ''
115-
116diff --git a/lib/interface/pgsql b/lib/interface/pgsql
117new file mode 120000
118index 0000000..fac7ffe
119--- /dev/null
120+++ b/lib/interface/pgsql
121@@ -0,0 +1 @@
122+../../mod/interface-pgsql/pgsql
123\ No newline at end of file
124diff --git a/metadata.yaml b/metadata.yaml
125index 7f2c63e..e03364e 100644
126--- a/metadata.yaml
127+++ b/metadata.yaml
128@@ -1,14 +1,19 @@
129 name: mattermost
130 summary: Mattermost charm
131-maitainers:
132+maintainers:
133 - launchpad.net/~canonical-is-sre
134 description: |
135 A charm which deploys Mattermost on kubernetes.
136 Mattermost is a flexible, open source messaging platform that enables
137 secure team collaboration.
138 https://mattermost.com
139+min-juju-version: 2.8.0 # charm storage in state
140 series:
141 - kubernetes
142+requires:
143+ db:
144+ interface: pgsql
145+ limit: 1
146 resources:
147 mattermost_image:
148 type: oci-image
149diff --git a/mod/interface-pgsql b/mod/interface-pgsql
150new file mode 160000
151index 0000000..8daab41
152--- /dev/null
153+++ b/mod/interface-pgsql
154@@ -0,0 +1 @@
155+Subproject commit 8daab41e46aa144ac93c766a723238561e6c5e73
156diff --git a/src/charm.py b/src/charm.py
157index 864754f..0fcc304 100755
158--- a/src/charm.py
159+++ b/src/charm.py
160@@ -1,28 +1,51 @@
161 #!/usr/bin/env python3
162
163 import sys
164-sys.path.append('lib')
165-from ops.charm import CharmBase # NoQA: E402
166-from ops.framework import StoredState # NoQA: E402
167-from ops.main import main # NoQA: E402
168-from ops.model import ( # NoQA: E402
169+sys.path.append('lib') # noqa: E402
170+
171+from ops.charm import (
172+ CharmBase,
173+ CharmEvents,
174+)
175+from ops.framework import (
176+ EventBase,
177+ EventSource,
178+ StoredState,
179+)
180+from ops.main import main
181+from ops.model import (
182 ActiveStatus,
183 MaintenanceStatus,
184 WaitingStatus,
185 )
186
187+from interface import pgsql
188 from oci_image import OCIImageResource
189
190-import logging # NoQA: E402
191+import logging
192 logger = logging.getLogger()
193
194
195+DATABASE_NAME = 'mattermost'
196+
197+
198+class MattermostDBMasterAvailableEvent(EventBase):
199+ pass
200+
201+
202+class MattermostCharmEvents(CharmEvents):
203+ """Custom charm events."""
204+ db_master_available = EventSource(MattermostDBMasterAvailableEvent)
205+
206+
207 class MattermostK8sCharm(CharmBase):
208
209 state = StoredState()
210+ on = MattermostCharmEvents()
211+
212+ def __init__(self, *args):
213+ super().__init__(*args)
214
215- def __init__(self, framework, key):
216- super().__init__(framework, key)
217 # get our mattermost_image from juju
218 # ie: juju deploy . --resource mattermost_image=mattermost:latest )
219 self.mattermost_image = OCIImageResource(self, 'mattermost_image')
220@@ -31,7 +54,55 @@ class MattermostK8sCharm(CharmBase):
221 self.framework.observe(self.on.leader_elected, self.configure_pod)
222 self.framework.observe(self.on.upgrade_charm, self.configure_pod)
223
224+ # database
225+ self.state.set_default(db_conn_str=None, db_uri=None, db_ro_uris=[])
226+ self.db = pgsql.PostgreSQLClient(self, 'db')
227+ self.framework.observe(self.db.on.database_relation_joined, self._on_database_relation_joined)
228+ self.framework.observe(self.db.on.master_changed, self._on_master_changed)
229+ self.framework.observe(self.db.on.standby_changed, self._on_standby_changed)
230+ self.framework.observe(self.on.db_master_available, self.configure_pod)
231+
232+ def _on_database_relation_joined(self, event: pgsql.DatabaseRelationJoinedEvent):
233+ if self.model.unit.is_leader():
234+ # Provide requirements to the PostgreSQL server.
235+ event.database = DATABASE_NAME # Request database named mydbname
236+ # event.extensions = ['citext'] # Request the citext extension installed
237+ elif event.database != DATABASE_NAME:
238+ # Leader has not yet set requirements. Defer, incase this unit
239+ # becomes leader and needs to perform that operation.
240+ event.defer()
241+ return
242+
243+ def _on_master_changed(self, event: pgsql.MasterChangedEvent):
244+ if event.database != DATABASE_NAME:
245+ # Leader has not yet set requirements. Wait until next
246+ # event, or risk connecting to an incorrect database.
247+ return
248+
249+ self.state.db_conn_str = None if event.master is None else event.master.conn_str
250+ self.state.db_uri = None if event.master is None else event.master.uri
251+
252+ if event.master is None:
253+ return
254+
255+ self.on.db_master_available.emit()
256+
257+ def _on_standby_changed(self, event: pgsql.StandbyChangedEvent):
258+ if event.database != DATABASE_NAME:
259+ # Leader has not yet set requirements. Wait until next
260+ # event, or risk connecting to an incorrect database.
261+ return
262+
263+ self.state.db_ro_uris = [c.uri for c in event.standbys]
264+
265+ # TODO(pjdc): Emit event when we add support for read replicas.
266+
267 def configure_pod(self, event):
268+ if not self.state.db_uri:
269+ self.model.unit.status = WaitingStatus('Waiting for database relation')
270+ event.defer()
271+ return
272+
273 if not self.framework.model.unit.is_leader():
274 self.model.unit.status = WaitingStatus('Not a leader')
275 return
276@@ -49,17 +120,14 @@ class MattermostK8sCharm(CharmBase):
277 }],
278 'config': {
279 'MATTERMOST_HTTPD_LISTEN_PORT': int(config['mattermost_port']),
280- 'DB_HOST': config['pg_db_host'],
281- 'DB_PORT_NUMBER': int(config['pg_db_port']),
282- 'MM_USERNAME': config['pg_user'],
283- 'MM_PASSWORD': config['pg_password'],
284+ 'MM_SQLSETTINGS_DATASOURCE': self.state.db_uri,
285 'MM_ENABLEOPENSERVER': config['open_server'],
286 },
287 }]
288 })
289 self.state.is_started = True
290 self.model.unit.status = ActiveStatus()
291-
292+
293
294 if __name__ == '__main__':
295 main(MattermostK8sCharm)

Subscribers

People subscribed via source and target branches