Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
  • Loading branch information
lukemarsden committed Dec 15, 2020
1 parent 21de4f1 commit c8698ce
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 4 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,9 @@ dmypy.json

# Pyre type checker
.pyre/

*.swp
*.swo
id_rsa
kubeconfig
*.charm
10 changes: 9 additions & 1 deletion .testfaster.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ base:
# You can also pull docker images like this. Always use
# immutable tags for reproducibility!
- nginx:1.19.5
- jujusolutions/juju-db:4.0
- jujusolutions/jujud-operator:2.9-rc3

prewarm_script: |-
# This gets run after each individual VM starts up, so
Expand All @@ -35,13 +37,19 @@ base:
# pod to the apiserver. so give it an ip address that resolves to the host
# from inside the pods.
cd /root
snap install juju --classic --channel=latest/edge
snap install juju --classic --channel=2.9/edge
# Set kubeconfig to point to k8s apiserver on an address that's routable
# from inside the pods, so jujud can access it. In this case the docker
# bridge.
sed -i 's/localhost/172.17.0.1/' .kube/config
juju add-k8s --client testfaster
juju bootstrap testfaster
# default service account has registry credentials, but juju needs them too
# (testfaster created the secret regcred already.)
kubectl patch serviceaccount -n controller-testfaster default -p '{"imagePullSecrets": [{"name": "regcred"}]}'
juju add-model kf
# cs: means "charm store", which is the "old place".
# ch: means "charm hub", which is the "new place".
Expand Down
13 changes: 13 additions & 0 deletions bundle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
applications:
mlflow:
charm: "./mlflow.charm"
scale: 1
postgres:
charm: "cs:~postgresql-charmers/postgresql-k8s"
scale: 1
bundle: kubernetes
description: "MLflow"
relations:
- - mlflow:db
- postgres:db
13 changes: 11 additions & 2 deletions integration_test
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ testctl get
export KUBECONFIG=$(pwd)/kubeconfig
(cd mlflow && charmcraft build)
./testctl-rsync.sh mlflow/mlflow.charm /root
#./testctl-ssh.sh -- /snap/bin/juju deploy --resource oci-image=gcr.io/kubeflow-images-public/admission-webhook:vmaster-gaf96e4e3 ./mlflow.charm
./testctl-ssh.sh -- /snap/bin/juju deploy ./mlflow.charm
./testctl-rsync.sh bundle.yaml /root

# How to iterate quickly without getting a new VM every time:
#./testctl-ssh.sh -- /snap/bin/juju destroy model kf
#./testctl-ssh.sh -- /snap/bin/juju destroy controller
#./testctl-ssh.sh -- /snap/bin/juju unregister controller

./testctl-ssh.sh -- /snap/bin/juju deploy ./bundle.yaml

echo "Try: testctl ssh -- /snap/bin/juju debug-log --include mlflow"

#./testctl-ssh.sh -- /snap/bin/juju deploy --resource oci-image=gcr.io/kubeflow-images-public/admission-webhook:vmaster-gaf96e4e3 ./mlflow.charm
9 changes: 9 additions & 0 deletions mlflow/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[flake8]
max-line-length = 99
select: E,W,F,C,N
exclude:
venv
.git
build
dist
*.egg_info
3 changes: 3 additions & 0 deletions mlflow/.jujuignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/venv
*.py[cod]
*.charm
5 changes: 5 additions & 0 deletions mlflow/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ series: [kubernetes]
# type: oci-image
# description: Backing OCI image
# auto-fetch: true
requires:
db:
interface: pgsql
limit: 1 # Most charms only handle a single PostgreSQL Application.

3 changes: 2 additions & 1 deletion mlflow/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
ops
ops==0.8.0
git+git://github.com/johnsca/resource-oci-image.git#oci_image
ops-lib-pgsql
52 changes: 52 additions & 0 deletions mlflow/src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from ops.model import ActiveStatus, MaintenanceStatus
logger = logging.getLogger(__name__)

import ops.lib
pgsql = ops.lib.use("pgsql", 1, "postgresql-charmers@lists.launchpad.net")

class MlflowCharm(CharmBase):
_stored = StoredState()

Expand All @@ -19,6 +22,12 @@ def __init__(self, *args):
logger.info("================================")
super().__init__(*args)

self._stored.set_default(db_conn_str=None, db_uri=None, db_ro_uris=[])
self.db = pgsql.PostgreSQLClient(self, 'db') # 'db' relation in metadata.yaml
self.framework.observe(self.db.on.database_relation_joined, self._on_database_relation_joined)
self.framework.observe(self.db.on.master_changed, self._on_master_changed)
self.framework.observe(self.db.on.standby_changed, self._on_standby_changed)

self.framework.observe(self.on.install, self.set_pod_spec)
self.framework.observe(self.on.upgrade_charm, self.set_pod_spec)

Expand Down Expand Up @@ -47,5 +56,48 @@ def set_pod_spec(self, event):
)
self.model.unit.status = ActiveStatus()

def _on_database_relation_joined(self, event: pgsql.DatabaseRelationJoinedEvent):
if self.model.unit.is_leader():
# Provide requirements to the PostgreSQL server.
event.database = 'mydbname' # Request database named mydbname
event.extensions = ['citext'] # Request the citext extension installed
elif event.database != 'mydbname':
# Leader has not yet set requirements. Defer, incase this unit
# becomes leader and needs to perform that operation.
event.defer()
return

def _on_master_changed(self, event: pgsql.MasterChangedEvent):
if event.database != 'mydbname':
# Leader has not yet set requirements. Wait until next event,
# or risk connecting to an incorrect database.
return

# The connection to the primary database has been created,
# changed or removed. More specific events are available, but
# most charms will find it easier to just handle the Changed
# events. event.master is None if the master database is not
# available, or a pgsql.ConnectionString instance.
self._stored.db_conn_str = None if event.master is None else event.master.conn_str
self._stored.db_uri = None if event.master is None else event.master.uri

# You probably want to emit an event here or call a setup routine to
# do something useful with the libpq connection string or URI now they
# are available.

def _on_standby_changed(self, event: pgsql.StandbyChangedEvent):
if event.database != 'mydbname':
# Leader has not yet set requirements. Wait until next event,
# or risk connecting to an incorrect database.
return

# Charms needing access to the hot standby databases can get
# their connection details here. Applications can scale out
# horizontally if they can make use of the read only hot
# standby replica databases, rather than only use the single
# master. event.stanbys will be an empty list if no hot standby
# databases are available.
self._stored.db_ro_uris = [c.uri for c in event.standbys]

if __name__ == "__main__":
main(MlflowCharm)

0 comments on commit c8698ce

Please sign in to comment.