This repository has been archived by the owner on Mar 4, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathprovides.py
308 lines (263 loc) · 9.19 KB
/
provides.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
"""
This is the provides side of the interface layer, for use only by the AWS
integrator charm itself.
The flags that are set by the provides side of this interface are:
* **`endpoint.{endpoint_name}.requested`** This flag is set when there is
a new or updated request by a remote unit for AWS integration features.
The AWS integration charm should then iterate over each request, perform
whatever actions are necessary to satisfy those requests, and then mark
them as complete.
"""
import json
from hashlib import sha256
from charmhelpers.core import unitdata
from charms.reactive import Endpoint
from charms.reactive import when
from charms.reactive import toggle_flag, clear_flag
class AWSIntegrationProvides(Endpoint):
"""
Example usage:
```python
from charms.reactive import when, endpoint_from_flag
from charms import layer
@when('endpoint.aws.requested')
def handle_requests():
aws = endpoint_from_flag('endpoint.aws.requested')
for request in aws.requests:
if request.instance_tags:
tag_instance(
request.instance_id,
request.region,
request.instance_tags)
if request.requested_load_balancer_management:
layer.aws.enable_load_balancer_management(
request.application_name,
request.instance_id,
request.region,
)
# ...
request.mark_completed()
```
"""
@when("endpoint.{endpoint_name}.changed")
def check_requests(self):
requests = self.requests
toggle_flag(self.expand_name("requested"), len(requests) > 0)
clear_flag(self.expand_name("changed"))
@when("endpoint.{endpoint_name}.departed")
def cleanup(self):
for unit in self.all_departed_units:
request = IntegrationRequest(unit)
request.clear()
self.all_departed_units.clear()
clear_flag(self.expand_name("departed"))
@property
def requests(self):
"""
A list of the new or updated #IntegrationRequest objects that
have been made.
"""
return [request for request in self.all_requests if request.changed]
@property
def all_requests(self):
"""
A list of all the #IntegrationRequest objects that have been made,
even if unchanged.
"""
return [IntegrationRequest(unit) for unit in self.all_joined_units]
@property
def application_names(self):
"""
Set of names of all applications that are still joined.
"""
return {unit.application_name for unit in self.all_joined_units}
@property
def unit_instances(self):
"""
Mapping of unit names to instance IDs and regions for all joined units.
"""
return {
unit.unit_name: {
"instance-id": unit.received["instance-id"],
"region": unit.received["region"],
}
for unit in self.all_joined_units
}
class IntegrationRequest:
"""
A request for integration from a single remote unit.
"""
def __init__(self, unit):
self._unit = unit
self._hash = sha256(
json.dumps(dict(unit.received), sort_keys=True).encode("utf8")
).hexdigest()
@property
def hash(self):
"""
SHA hash of the data for this request.
"""
return self._hash
@property
def _hash_key(self):
endpoint = self._unit.relation.endpoint
return endpoint.expand_name("request.{}".format(self.instance_id))
@property
def changed(self):
"""
Whether this request has changed since the last time it was
marked completed.
"""
if not (self.instance_id and self._requested):
return False
saved_hash = unitdata.kv().get(self._hash_key)
result = saved_hash != self.hash
return result
def mark_completed(self):
"""
Mark this request as having been completed.
"""
completed = self._unit.relation.to_publish.get("completed", {})
completed[self.instance_id] = self.hash
unitdata.kv().set(self._hash_key, self.hash)
self._unit.relation.to_publish["completed"] = completed
def clear(self):
"""
Clear this request's cached data.
"""
unitdata.kv().unset(self._hash_key)
@property
def unit_name(self):
"""
The name of the unit making the request.
"""
return self._unit.unit_name
@property
def application_name(self):
"""
The name of the application making the request.
"""
return self._unit.application_name
@property
def _requested(self):
return self._unit.received["requested"]
@property
def instance_id(self):
"""
The instance ID reported for this request.
"""
return self._unit.received["instance-id"]
@property
def region(self):
"""
The region reported for this request.
"""
return self._unit.received["region"]
@property
def instance_tags(self):
"""
Mapping of tag names to values (or `None`) to apply to this instance.
"""
# uses dict() here to make a copy, just to be safe
return dict(self._unit.received.get("instance-tags", {}))
@property
def instance_security_group_tags(self):
"""
Mapping of tag names to values (or `None`) to apply to this instance's
machine-specific security group (firewall).
"""
# uses dict() here to make a copy, just to be safe
return dict(self._unit.received.get("instance-security-group-tags", {}))
@property
def instance_subnet_tags(self):
"""
Mapping of tag names to values (or `None`) to apply to this instance's
subnet.
"""
# uses dict() here to make a copy, just to be safe
return dict(self._unit.received.get("instance-subnet-tags", {}))
@property
def requested_instance_inspection(self):
"""
Flag indicating whether the ability to inspect instances was requested.
"""
return bool(self._unit.received["enable-instance-inspection"])
@property
def requested_instance_modification(self):
"""
Flag indicating whether the ability to modify instances was requested.
"""
return bool(self._unit.received["enable-instance-modification"])
@property
def requested_autoscaling_readonly(self):
"""
Flag indicating whether autoscaling read-attributes are requested.
"""
return bool(self._unit.received["enable-autoscaling-readonly"])
@property
def requested_acm_readonly(self):
"""
Flag indicating whether acm readonly was requested.
"""
return bool(self._unit.received["enable-acm-readonly"])
@property
def requested_acm_fullaccess(self):
"""
Flag indicating whether acm fullaccess was requested.
"""
return bool(self._unit.received["enable-acm-fullaccess"])
@property
def requested_network_management(self):
"""
Flag indicating whether the ability to manage networking (firewalls,
subnets, etc) was requested.
"""
return bool(self._unit.received["enable-network-management"])
@property
def requested_load_balancer_management(self):
"""
Flag indicating whether load balancer management was requested.
"""
return bool(self._unit.received["enable-load-balancer-management"])
@property
def requested_block_storage_management(self):
"""
Flag indicating whether block storage management was requested.
"""
return bool(self._unit.received["enable-block-storage-management"])
@property
def requested_dns_management(self):
"""
Flag indicating whether DNS management was requested.
"""
return bool(self._unit.received["enable-dns-management"])
@property
def requested_region_readonly(self):
"""
Flag indicating whether Region readonly management was requested.
"""
return bool(self._unit.received["enable-region-readonly"])
@property
def requested_object_storage_access(self):
"""
Flag indicating whether object storage access was requested.
"""
return bool(self._unit.received["enable-object-storage-access"])
@property
def object_storage_access_patterns(self):
"""
List of patterns to which to restrict object storage access.
"""
return list(self._unit.received["object-storage-access-patterns"] or [])
@property
def requested_object_storage_management(self):
"""
Flag indicating whether object storage management was requested.
"""
return bool(self._unit.received["enable-object-storage-management"])
@property
def object_storage_management_patterns(self):
"""
List of patterns to which to restrict object storage management.
"""
return list(self._unit.received["object-storage-management-patterns"] or [])