Charmed-Kubernetes/kubernetes-worker/hooks/relations/aws-integration/provides.py

289 lines
8.6 KiB
Python

"""
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 #IntegrationRequests 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 #IntegrationRequests 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_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_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 [])