df70e376ff
* Add sunbeam project template to run pep8, py3 tests * Add zuul.d/zuul.yaml to run pep8, py3, cover tests * Update charmcraft and requirements for each charm * Add global tox.ini to invoke fmt, pep8, py3, cover, build * Add gitreview file * Fix py3 test failures in ciner-ceph-k8s, glance-k8s, openstack-exporter * Add jobs for charm builds using files option so that job is invoked if files within the component are modified. Add charm builds to both check and gate pipeline. * Make function tests as part of global. Split the function tests into core, ceph, caas, misc mainly to accomodate function tests to run on 8GB. Add function tests as part of check pipeline. * Add zuul job to publish charms in promote pipeline Add charmhub token as secret that can be used to publish charms. Note: Charmhub token is generated with ttl of 90 days. * Run tox formatting * Make .gitignore, .jujuignore, .stestr.conf global and remove the files from all charms. * Make libs and templates global. Split libs to internal and external so that internal libs can adhere to sunbeam formatting styles. * Add script to copy common files necessary libs, config templates, stestr conf, jujuignore during py3 tests and charm builds. * Tests for keystone-ldap-k8s are commented due to intermittent bug LP#2045206 Change-Id: I804ca64182c109d16bd820ac00f129aa6dcf4496
419 lines
13 KiB
Python
419 lines
13 KiB
Python
"""CloudCredentialsProvides and Requires module.
|
|
|
|
|
|
This library contains the Requires and Provides classes for handling
|
|
the cloud_credentials interface.
|
|
|
|
Import `CloudCredentialsRequires` in your charm, with the charm object and the
|
|
relation name:
|
|
- self
|
|
- "cloud_credentials"
|
|
|
|
Also provide additional parameters to the charm object:
|
|
- service
|
|
- internal_url
|
|
- public_url
|
|
- admin_url
|
|
- region
|
|
- username
|
|
- vhost
|
|
|
|
Two events are also available to respond to:
|
|
- connected
|
|
- ready
|
|
- goneaway
|
|
|
|
A basic example showing the usage of this relation follows:
|
|
|
|
```
|
|
from charms.keystone_k8s.v0.cloud_credentials import CloudCredentialsRequires
|
|
|
|
class CloudCredentialsClientCharm(CharmBase):
|
|
def __init__(self, *args):
|
|
super().__init__(*args)
|
|
# CloudCredentials Requires
|
|
self.cloud_credentials = CloudCredentialsRequires(
|
|
self, "cloud_credentials",
|
|
service = "my-service"
|
|
internal_url = "http://internal-url"
|
|
public_url = "http://public-url"
|
|
admin_url = "http://admin-url"
|
|
region = "region"
|
|
)
|
|
self.framework.observe(
|
|
self.cloud_credentials.on.connected, self._on_cloud_credentials_connected)
|
|
self.framework.observe(
|
|
self.cloud_credentials.on.ready, self._on_cloud_credentials_ready)
|
|
self.framework.observe(
|
|
self.cloud_credentials.on.goneaway, self._on_cloud_credentials_goneaway)
|
|
|
|
def _on_cloud_credentials_connected(self, event):
|
|
'''React to the CloudCredentials connected event.
|
|
|
|
This event happens when n CloudCredentials relation is added to the
|
|
model before credentials etc have been provided.
|
|
'''
|
|
# Do something before the relation is complete
|
|
pass
|
|
|
|
def _on_cloud_credentials_ready(self, event):
|
|
'''React to the CloudCredentials ready event.
|
|
|
|
The CloudCredentials interface will use the provided config for the
|
|
request to the identity server.
|
|
'''
|
|
# CloudCredentials Relation is ready. Do something with the completed relation.
|
|
pass
|
|
|
|
def _on_cloud_credentials_goneaway(self, event):
|
|
'''React to the CloudCredentials goneaway event.
|
|
|
|
This event happens when an CloudCredentials relation is removed.
|
|
'''
|
|
# CloudCredentials Relation has goneaway. shutdown services or suchlike
|
|
pass
|
|
```
|
|
"""
|
|
|
|
import logging
|
|
|
|
from ops.framework import (
|
|
StoredState,
|
|
EventBase,
|
|
ObjectEvents,
|
|
EventSource,
|
|
Object,
|
|
)
|
|
from ops.model import Relation
|
|
|
|
# The unique Charmhub library identifier, never change it
|
|
LIBID = "a5d96cc2686c47eea554ce2210c2d24e"
|
|
|
|
# Increment this major API version when introducing breaking changes
|
|
LIBAPI = 0
|
|
|
|
# Increment this PATCH version before using `charmcraft publish-lib` or reset
|
|
# to 0 if you are raising the major API version
|
|
LIBPATCH = 2
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class CloudCredentialsConnectedEvent(EventBase):
|
|
"""CloudCredentials connected Event."""
|
|
|
|
pass
|
|
|
|
|
|
class CloudCredentialsReadyEvent(EventBase):
|
|
"""CloudCredentials ready for use Event."""
|
|
|
|
pass
|
|
|
|
|
|
class CloudCredentialsGoneAwayEvent(EventBase):
|
|
"""CloudCredentials relation has gone-away Event"""
|
|
|
|
pass
|
|
|
|
|
|
class CloudCredentialsServerEvents(ObjectEvents):
|
|
"""Events class for `on`"""
|
|
|
|
connected = EventSource(CloudCredentialsConnectedEvent)
|
|
ready = EventSource(CloudCredentialsReadyEvent)
|
|
goneaway = EventSource(CloudCredentialsGoneAwayEvent)
|
|
|
|
|
|
class CloudCredentialsRequires(Object):
|
|
"""
|
|
CloudCredentialsRequires class
|
|
"""
|
|
|
|
on = CloudCredentialsServerEvents()
|
|
_stored = StoredState()
|
|
|
|
def __init__(self, charm, relation_name: str):
|
|
super().__init__(charm, relation_name)
|
|
self.charm = charm
|
|
self.relation_name = relation_name
|
|
self.framework.observe(
|
|
self.charm.on[relation_name].relation_joined,
|
|
self._on_cloud_credentials_relation_joined,
|
|
)
|
|
self.framework.observe(
|
|
self.charm.on[relation_name].relation_changed,
|
|
self._on_cloud_credentials_relation_changed,
|
|
)
|
|
self.framework.observe(
|
|
self.charm.on[relation_name].relation_departed,
|
|
self._on_cloud_credentials_relation_changed,
|
|
)
|
|
self.framework.observe(
|
|
self.charm.on[relation_name].relation_broken,
|
|
self._on_cloud_credentials_relation_broken,
|
|
)
|
|
|
|
def _on_cloud_credentials_relation_joined(self, event):
|
|
"""CloudCredentials relation joined."""
|
|
logging.debug("CloudCredentials on_joined")
|
|
self.on.connected.emit()
|
|
self.request_credentials()
|
|
|
|
def _on_cloud_credentials_relation_changed(self, event):
|
|
"""CloudCredentials relation changed."""
|
|
logging.debug("CloudCredentials on_changed")
|
|
try:
|
|
self.on.ready.emit()
|
|
except (AttributeError, KeyError):
|
|
logger.exception('Error when emitting event')
|
|
|
|
def _on_cloud_credentials_relation_broken(self, event):
|
|
"""CloudCredentials relation broken."""
|
|
logging.debug("CloudCredentials on_broken")
|
|
self.on.goneaway.emit()
|
|
|
|
@property
|
|
def _cloud_credentials_rel(self) -> Relation:
|
|
"""The CloudCredentials relation."""
|
|
return self.framework.model.get_relation(self.relation_name)
|
|
|
|
def get_remote_app_data(self, key: str) -> str:
|
|
"""Return the value for the given key from remote app data."""
|
|
data = self._cloud_credentials_rel.data[self._cloud_credentials_rel.app]
|
|
return data.get(key)
|
|
|
|
@property
|
|
def api_version(self) -> str:
|
|
"""Return the api_version."""
|
|
return self.get_remote_app_data('api-version')
|
|
|
|
@property
|
|
def auth_host(self) -> str:
|
|
"""Return the auth_host."""
|
|
return self.get_remote_app_data('auth-host')
|
|
|
|
@property
|
|
def auth_port(self) -> str:
|
|
"""Return the auth_port."""
|
|
return self.get_remote_app_data('auth-port')
|
|
|
|
@property
|
|
def auth_protocol(self) -> str:
|
|
"""Return the auth_protocol."""
|
|
return self.get_remote_app_data('auth-protocol')
|
|
|
|
@property
|
|
def internal_host(self) -> str:
|
|
"""Return the internal_host."""
|
|
return self.get_remote_app_data('internal-host')
|
|
|
|
@property
|
|
def internal_port(self) -> str:
|
|
"""Return the internal_port."""
|
|
return self.get_remote_app_data('internal-port')
|
|
|
|
@property
|
|
def internal_protocol(self) -> str:
|
|
"""Return the internal_protocol."""
|
|
return self.get_remote_app_data('internal-protocol')
|
|
|
|
@property
|
|
def username(self) -> str:
|
|
"""Return the username."""
|
|
return self.get_remote_app_data('username')
|
|
|
|
@property
|
|
def password(self) -> str:
|
|
"""Return the password."""
|
|
return self.get_remote_app_data('password')
|
|
|
|
@property
|
|
def project_name(self) -> str:
|
|
"""Return the project name."""
|
|
return self.get_remote_app_data('project-name')
|
|
|
|
@property
|
|
def project_id(self) -> str:
|
|
"""Return the project id."""
|
|
return self.get_remote_app_data('project-id')
|
|
|
|
@property
|
|
def user_domain_name(self) -> str:
|
|
"""Return the name of the user domain."""
|
|
return self.get_remote_app_data('user-domain-name')
|
|
|
|
@property
|
|
def user_domain_id(self) -> str:
|
|
"""Return the id of the user domain."""
|
|
return self.get_remote_app_data('user-domain-id')
|
|
|
|
@property
|
|
def project_domain_name(self) -> str:
|
|
"""Return the name of the project domain."""
|
|
return self.get_remote_app_data('project-domain-name')
|
|
|
|
@property
|
|
def project_domain_id(self) -> str:
|
|
"""Return the id of the project domain."""
|
|
return self.get_remote_app_data('project-domain-id')
|
|
|
|
@property
|
|
def region(self) -> str:
|
|
"""Return the region for the auth urls."""
|
|
return self.get_remote_app_data('region')
|
|
|
|
def request_credentials(self) -> None:
|
|
"""Request credentials from the CloudCredentials server."""
|
|
if self.model.unit.is_leader():
|
|
logging.debug(f'Requesting credentials for {self.charm.app.name}')
|
|
app_data = self._cloud_credentials_rel.data[self.charm.app]
|
|
app_data['username'] = self.charm.app.name
|
|
|
|
|
|
class HasCloudCredentialsClientsEvent(EventBase):
|
|
"""Has CloudCredentialsClients Event."""
|
|
|
|
pass
|
|
|
|
|
|
class ReadyCloudCredentialsClientsEvent(EventBase):
|
|
"""CloudCredentialsClients Ready Event."""
|
|
|
|
def __init__(self, handle, relation_id, relation_name, username):
|
|
super().__init__(handle)
|
|
self.relation_id = relation_id
|
|
self.relation_name = relation_name
|
|
self.username = username
|
|
|
|
def snapshot(self):
|
|
return {
|
|
"relation_id": self.relation_id,
|
|
"relation_name": self.relation_name,
|
|
"username": self.username,
|
|
}
|
|
|
|
def restore(self, snapshot):
|
|
super().restore(snapshot)
|
|
self.relation_id = snapshot["relation_id"]
|
|
self.relation_name = snapshot["relation_name"]
|
|
self.username = snapshot["username"]
|
|
|
|
|
|
class CloudCredentialsClientsGoneAwayEvent(EventBase):
|
|
"""Has CloudCredentialsClientsGoneAwayEvent Event."""
|
|
|
|
pass
|
|
|
|
|
|
class CloudCredentialsClientEvents(ObjectEvents):
|
|
"""Events class for `on`"""
|
|
|
|
has_cloud_credentials_clients = EventSource(
|
|
HasCloudCredentialsClientsEvent
|
|
)
|
|
ready_cloud_credentials_clients = EventSource(
|
|
ReadyCloudCredentialsClientsEvent
|
|
)
|
|
cloud_credentials_clients_gone = EventSource(
|
|
CloudCredentialsClientsGoneAwayEvent
|
|
)
|
|
|
|
|
|
class CloudCredentialsProvides(Object):
|
|
"""
|
|
CloudCredentialsProvides class
|
|
"""
|
|
|
|
on = CloudCredentialsClientEvents()
|
|
_stored = StoredState()
|
|
|
|
def __init__(self, charm, relation_name):
|
|
super().__init__(charm, relation_name)
|
|
self.charm = charm
|
|
self.relation_name = relation_name
|
|
self.framework.observe(
|
|
self.charm.on[relation_name].relation_joined,
|
|
self._on_cloud_credentials_relation_joined,
|
|
)
|
|
self.framework.observe(
|
|
self.charm.on[relation_name].relation_changed,
|
|
self._on_cloud_credentials_relation_changed,
|
|
)
|
|
self.framework.observe(
|
|
self.charm.on[relation_name].relation_broken,
|
|
self._on_cloud_credentials_relation_broken,
|
|
)
|
|
|
|
def _on_cloud_credentials_relation_joined(self, event):
|
|
"""Handle CloudCredentials joined."""
|
|
logging.debug("CloudCredentialsProvides on_joined")
|
|
self.on.has_cloud_credentials_clients.emit()
|
|
|
|
def _on_cloud_credentials_relation_changed(self, event):
|
|
"""Handle CloudCredentials changed."""
|
|
logging.debug("CloudCredentials on_changed")
|
|
REQUIRED_KEYS = ['username']
|
|
|
|
values = [
|
|
event.relation.data[event.relation.app].get(k)
|
|
for k in REQUIRED_KEYS
|
|
]
|
|
# Validate data on the relation
|
|
if all(values):
|
|
username = event.relation.data[event.relation.app]['username']
|
|
self.on.ready_cloud_credentials_clients.emit(
|
|
event.relation.id,
|
|
event.relation.name,
|
|
username,
|
|
)
|
|
|
|
def _on_cloud_credentials_relation_broken(self, event):
|
|
"""Handle CloudCredentials broken."""
|
|
logging.debug("CloudCredentialsProvides on_departed")
|
|
self.on.cloud_credentials_clients_gone.emit()
|
|
|
|
def set_cloud_credentials(self, relation_name: int,
|
|
relation_id: str,
|
|
api_version: str,
|
|
auth_host: str,
|
|
auth_port: str,
|
|
auth_protocol: str,
|
|
internal_host: str,
|
|
internal_port: str,
|
|
internal_protocol: str,
|
|
username: str,
|
|
password: str,
|
|
project_name: str,
|
|
project_id: str,
|
|
user_domain_name: str,
|
|
user_domain_id: str,
|
|
project_domain_name: str,
|
|
project_domain_id: str,
|
|
region: str):
|
|
logging.debug("Setting cloud_credentials connection information.")
|
|
_cloud_credentials_rel = None
|
|
for relation in self.framework.model.relations[relation_name]:
|
|
if relation.id == relation_id:
|
|
_cloud_credentials_rel = relation
|
|
if not _cloud_credentials_rel:
|
|
# Relation has disappeared so don't send the data
|
|
return
|
|
app_data = _cloud_credentials_rel.data[self.charm.app]
|
|
app_data["api-version"] = api_version
|
|
app_data["auth-host"] = auth_host
|
|
app_data["auth-port"] = str(auth_port)
|
|
app_data["auth-protocol"] = auth_protocol
|
|
app_data["internal-host"] = internal_host
|
|
app_data["internal-port"] = str(internal_port)
|
|
app_data["internal-protocol"] = internal_protocol
|
|
app_data["username"] = username
|
|
app_data["password"] = password
|
|
app_data["project-name"] = project_name
|
|
app_data["project-id"] = project_id
|
|
app_data["user-domain-name"] = user_domain_name
|
|
app_data["user-domain-id"] = user_domain_id
|
|
app_data["project-domain-name"] = project_domain_name
|
|
app_data["project-domain-id"] = project_domain_id
|
|
app_data["region"] = region
|