blob: f38adec015938bda7120dcd98d2b03429e8950a3 [file] [log] [blame]
# 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