Add Keystone charm
[osm/devops.git] / installers / charm / osm-keystone / src / cluster.py
diff --git a/installers/charm/osm-keystone/src/cluster.py b/installers/charm/osm-keystone/src/cluster.py
new file mode 100644 (file)
index 0000000..f38adec
--- /dev/null
@@ -0,0 +1,135 @@
+# Copyright 2021 Canonical Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact: legal@canonical.com
+#
+# To get in touch with the maintainers, please contact:
+# osm-charmers@lists.launchpad.net
+#
+#
+# This file populates the Actions tab on Charmhub.
+# See https://juju.is/docs/some-url-to-be-determined/ for a checklist and guidance.
+
+"""Keystone cluster library.
+
+This library allows the integration with Apache Guacd charm. Is is published as part of the
+[davigar15-apache-guacd]((https://charmhub.io/davigar15-apache-guacd) charm.
+
+The charm that requires guacd should include the following content in its metadata.yaml:
+
+```yaml
+# ...
+peers:
+    cluster:
+        interface: cluster
+# ...
+```
+
+A typical example of including this library might be:
+
+```python
+# ...
+from ops.framework import StoredState
+from charms.keystone.v0 import cluster
+
+class SomeApplication(CharmBase):
+  on = cluster.ClusterEvents()
+
+  def __init__(self, *args):
+    # ...
+    self.cluster = cluster.Cluster(self)
+    self.framework.observe(self.on.cluster_keys_changed, self._cluster_keys_changed)
+    # ...
+
+  def _cluster_keys_changed(self, _):
+    fernet_keys = self.cluster.fernet_keys
+    credential_keys = self.cluster.credential_keys
+    # ...
+```
+"""
+
+
+import json
+import logging
+from typing import Any, Dict, List
+
+from ops.charm import CharmEvents
+from ops.framework import EventBase, EventSource, Object
+from ops.model import Relation
+
+# Number of keys need might need to be adjusted in the future
+NUMBER_FERNET_KEYS = 2
+NUMBER_CREDENTIAL_KEYS = 2
+
+logger = logging.getLogger(__name__)
+
+
+class ClusterKeysChangedEvent(EventBase):
+    """Event to announce a change in the Guacd service."""
+
+
+class ClusterEvents(CharmEvents):
+    """Cluster Events."""
+
+    cluster_keys_changed = EventSource(ClusterKeysChangedEvent)
+
+
+class Cluster(Object):
+    """Peer relation."""
+
+    def __init__(self, charm):
+        super().__init__(charm, "cluster")
+        self.charm = charm
+
+    @property
+    def fernet_keys(self) -> List[str]:
+        """Fernet keys."""
+        relation: Relation = self.model.get_relation("cluster")
+        application_data = relation.data[self.model.app]
+        return json.loads(application_data.get("keys-fernet", "[]"))
+
+    @property
+    def credential_keys(self) -> List[str]:
+        """Credential keys."""
+        relation: Relation = self.model.get_relation("cluster")
+        application_data = relation.data[self.model.app]
+        return json.loads(application_data.get("keys-credential", "[]"))
+
+    def save_keys(self, keys: Dict[str, Any]) -> None:
+        """Generate fernet and credential keys.
+
+        This method will generate new keys and fire the cluster_keys_changed event.
+        """
+        logger.debug("Saving keys...")
+        relation: Relation = self.model.get_relation("cluster")
+        data = relation.data[self.model.app]
+        current_keys_str = data.get("key_repository", "{}")
+        current_keys = json.loads(current_keys_str)
+        if current_keys != keys:
+            data["key_repository"] = json.dumps(keys)
+            self.charm.on.cluster_keys_changed.emit()
+        logger.info("Keys saved!")
+
+    def get_keys(self) -> Dict[str, Any]:
+        """Get keys from the relation.
+
+        Returns:
+            Dict[str, Any]: Dictionary with the keys.
+        """
+        relation: Relation = self.model.get_relation("cluster")
+        data = relation.data[self.model.app]
+        current_keys_str = data.get("key_repository", "{}")
+        current_keys = json.loads(current_keys_str)
+        return current_keys