blob: f38adec015938bda7120dcd98d2b03429e8950a3 [file] [log] [blame]
Patricia Reinosod5b463c2023-05-31 08:37:18 +00001# Copyright 2021 Canonical Ltd.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14#
15# For those usages not covered by the Apache License, Version 2.0 please
16# contact: legal@canonical.com
17#
18# To get in touch with the maintainers, please contact:
19# osm-charmers@lists.launchpad.net
20#
21#
22# This file populates the Actions tab on Charmhub.
23# See https://juju.is/docs/some-url-to-be-determined/ for a checklist and guidance.
24
25"""Keystone cluster library.
26
27This library allows the integration with Apache Guacd charm. Is is published as part of the
28[davigar15-apache-guacd]((https://charmhub.io/davigar15-apache-guacd) charm.
29
30The charm that requires guacd should include the following content in its metadata.yaml:
31
32```yaml
33# ...
34peers:
35 cluster:
36 interface: cluster
37# ...
38```
39
40A typical example of including this library might be:
41
42```python
43# ...
44from ops.framework import StoredState
45from charms.keystone.v0 import cluster
46
47class SomeApplication(CharmBase):
48 on = cluster.ClusterEvents()
49
50 def __init__(self, *args):
51 # ...
52 self.cluster = cluster.Cluster(self)
53 self.framework.observe(self.on.cluster_keys_changed, self._cluster_keys_changed)
54 # ...
55
56 def _cluster_keys_changed(self, _):
57 fernet_keys = self.cluster.fernet_keys
58 credential_keys = self.cluster.credential_keys
59 # ...
60```
61"""
62
63
64import json
65import logging
66from typing import Any, Dict, List
67
68from ops.charm import CharmEvents
69from ops.framework import EventBase, EventSource, Object
70from ops.model import Relation
71
72# Number of keys need might need to be adjusted in the future
73NUMBER_FERNET_KEYS = 2
74NUMBER_CREDENTIAL_KEYS = 2
75
76logger = logging.getLogger(__name__)
77
78
79class ClusterKeysChangedEvent(EventBase):
80 """Event to announce a change in the Guacd service."""
81
82
83class ClusterEvents(CharmEvents):
84 """Cluster Events."""
85
86 cluster_keys_changed = EventSource(ClusterKeysChangedEvent)
87
88
89class Cluster(Object):
90 """Peer relation."""
91
92 def __init__(self, charm):
93 super().__init__(charm, "cluster")
94 self.charm = charm
95
96 @property
97 def fernet_keys(self) -> List[str]:
98 """Fernet keys."""
99 relation: Relation = self.model.get_relation("cluster")
100 application_data = relation.data[self.model.app]
101 return json.loads(application_data.get("keys-fernet", "[]"))
102
103 @property
104 def credential_keys(self) -> List[str]:
105 """Credential keys."""
106 relation: Relation = self.model.get_relation("cluster")
107 application_data = relation.data[self.model.app]
108 return json.loads(application_data.get("keys-credential", "[]"))
109
110 def save_keys(self, keys: Dict[str, Any]) -> None:
111 """Generate fernet and credential keys.
112
113 This method will generate new keys and fire the cluster_keys_changed event.
114 """
115 logger.debug("Saving keys...")
116 relation: Relation = self.model.get_relation("cluster")
117 data = relation.data[self.model.app]
118 current_keys_str = data.get("key_repository", "{}")
119 current_keys = json.loads(current_keys_str)
120 if current_keys != keys:
121 data["key_repository"] = json.dumps(keys)
122 self.charm.on.cluster_keys_changed.emit()
123 logger.info("Keys saved!")
124
125 def get_keys(self) -> Dict[str, Any]:
126 """Get keys from the relation.
127
128 Returns:
129 Dict[str, Any]: Dictionary with the keys.
130 """
131 relation: Relation = self.model.get_relation("cluster")
132 data = relation.data[self.model.app]
133 current_keys_str = data.get("key_repository", "{}")
134 current_keys = json.loads(current_keys_str)
135 return current_keys