Skip to content
Snippets Groups Projects
Commit 009a5d69 authored by garciadav's avatar garciadav Committed by Mark Beierl
Browse files

Add keystone charm and interface


- Bundles updated
- Installer updated

Change-Id: I0f8e9aafd51e9579159f9166864eb8634292f99c
Signed-off-by: default avatarDavid Garcia <david.garcia@canonical.com>
parent bdca472e
No related branches found
No related tags found
No related merge requests found
Showing
with 694 additions and 0 deletions
...@@ -23,3 +23,6 @@ build 'nbi-k8s' ...@@ -23,3 +23,6 @@ build 'nbi-k8s'
build 'pol-k8s' build 'pol-k8s'
build 'ro-k8s' build 'ro-k8s'
build 'ui-k8s' build 'ui-k8s'
build 'keystone'
build 'ng-ui'
build 'pla'
\ No newline at end of file
...@@ -91,6 +91,7 @@ applications: ...@@ -91,6 +91,7 @@ applications:
options: options:
log_level: "INFO" log_level: "INFO"
DATABASE_COMMONKEY: osm DATABASE_COMMONKEY: osm
auth-backend: keystone
annotations: annotations:
gui-x: 0 gui-x: 0
gui-y: -200 gui-y: -200
...@@ -214,6 +215,14 @@ applications: ...@@ -214,6 +215,14 @@ applications:
annotations: annotations:
gui-x: 250 gui-x: 250
gui-y: 550 gui-y: 550
keystone:
charm: '%(prefix)s/keystone%(suffix)s'
channel: '%(channel)s'
scale: 1
series: kubernetes
annotations:
gui-x: -250
gui-y: 550
relations: relations:
- - "kafka-k8s:zookeeper" - - "kafka-k8s:zookeeper"
...@@ -254,3 +263,7 @@ relations: ...@@ -254,3 +263,7 @@ relations:
- "mongodb-k8s:mongo" - "mongodb-k8s:mongo"
- - 'ng-ui:nbi' - - 'ng-ui:nbi'
- 'nbi-k8s:nbi' - 'nbi-k8s:nbi'
- - 'keystone:db'
- 'mariadb-k8s:mysql'
- - 'keystone:keystone'
- 'nbi-k8s:keystone'
...@@ -91,6 +91,7 @@ applications: ...@@ -91,6 +91,7 @@ applications:
options: options:
log_level: "INFO" log_level: "INFO"
DATABASE_COMMONKEY: osm DATABASE_COMMONKEY: osm
auth-backend: keystone
annotations: annotations:
gui-x: 0 gui-x: 0
gui-y: -200 gui-y: -200
...@@ -214,6 +215,14 @@ applications: ...@@ -214,6 +215,14 @@ applications:
annotations: annotations:
gui-x: 250 gui-x: 250
gui-y: 550 gui-y: 550
keystone:
charm: '%(prefix)s/keystone%(suffix)s'
channel: '%(channel)s'
scale: 1
series: kubernetes
annotations:
gui-x: -250
gui-y: 550
relations: relations:
- - "kafka-k8s:zookeeper" - - "kafka-k8s:zookeeper"
...@@ -254,3 +263,7 @@ relations: ...@@ -254,3 +263,7 @@ relations:
- "mongodb-k8s:mongo" - "mongodb-k8s:mongo"
- - 'ng-ui:nbi' - - 'ng-ui:nbi'
- 'nbi-k8s:nbi' - 'nbi-k8s:nbi'
- - 'keystone:db'
- 'mariadb-k8s:mysql'
- - 'keystone:keystone'
- 'nbi-k8s:keystone'
# Copyright 2020 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.
name: keystone
summary: Keystone Interface
version: 1
# Copyright 2020 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.
from charms.reactive import Endpoint
from charms.reactive import when
from charms.reactive import set_flag, clear_flag
class KeystoneProvides(Endpoint):
@when("endpoint.{endpoint_name}.joined")
def _joined(self):
set_flag(self.expand_name("{endpoint_name}.joined"))
@when("endpoint.{endpoint_name}.changed")
def _changed(self):
set_flag(self.expand_name("{endpoint_name}.ready"))
@when("endpoint.{endpoint_name}.departed")
def _departed(self):
set_flag(self.expand_name("{endpoint_name}.departed"))
clear_flag(self.expand_name("{endpoint_name}.joined"))
def publish_info(
self,
host,
port,
keystone_db_password,
region_id,
user_domain_name,
project_domain_name,
admin_username,
admin_password,
admin_project_name,
username,
password,
service,
):
for relation in self.relations:
relation.to_publish["host"] = host
relation.to_publish["port"] = port
relation.to_publish["keystone_db_password"] = keystone_db_password
relation.to_publish["region_id"] = region_id
relation.to_publish["user_domain_name"] = user_domain_name
relation.to_publish["project_domain_name"] = project_domain_name
relation.to_publish["admin_username"] = admin_username
relation.to_publish["admin_password"] = admin_password
relation.to_publish["admin_project_name"] = admin_project_name
relation.to_publish["username"] = username
relation.to_publish["password"] = password
relation.to_publish["service"] = service
def mark_complete(self):
clear_flag(self.expand_name("{endpoint_name}.joined"))
# Copyright 2020 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.
from charms.reactive import Endpoint
from charms.reactive import when
from charms.reactive import set_flag, clear_flag
class KeystoneRequires(Endpoint):
@when("endpoint.{endpoint_name}.joined")
def _joined(self):
set_flag(self.expand_name("{endpoint_name}.joined"))
@when("endpoint.{endpoint_name}.changed")
def _changed(self):
if len(self.keystones()) > 0:
set_flag(self.expand_name("{endpoint_name}.ready"))
else:
clear_flag(self.expand_name("{endpoint_name}.ready"))
@when("endpoint.{endpoint_name}.departed")
def _departed(self):
set_flag(self.expand_name("{endpoint_name}.departed"))
clear_flag(self.expand_name("{endpoint_name}.joined"))
clear_flag(self.expand_name("{endpoint_name}.ready"))
def keystones(self):
"""
Return Keystone Data:
[{
'host': <host>,
'port': <port>,
'keystone_db_password: <keystone_db_password>,
'region_id: <region_id>,
'admin_username: <admin_username>,
'admin_password: <admin_password>,
'admin_project_name: <admin_project_name>,
'username: <username>,
'password: <password>,
'service: <service>
}]
"""
keystones = []
for relation in self.relations:
for unit in relation.units:
data = {
"host": unit.received["host"],
"port": unit.received["port"],
"keystone_db_password": unit.received["keystone_db_password"],
"region_id": unit.received["region_id"],
"user_domain_name": unit.received["user_domain_name"],
"project_domain_name": unit.received["project_domain_name"],
"admin_username": unit.received["admin_username"],
"admin_password": unit.received["admin_password"],
"admin_project_name": unit.received["admin_project_name"],
"username": unit.received["username"],
"password": unit.received["password"],
"service": unit.received["service"],
}
if all(data.values()):
keystones.append(data)
return keystones
# Copyright 2020 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.
.vscode
build
keystone.charm
\ No newline at end of file
# Copyright 2020 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.
---
extends: default
yaml-files:
- "*.yaml"
- "*.yml"
- ".yamllint"
ignore: |
.tox
build/
mod/
lib/
<!-- Copyright 2020 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. -->
# Keystone operator Charm for Kubernetes
## Requirements
# Copyright 2020 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.
options:
image:
type: string
default: opensourcemano/keystone:latest
description: The docker image to install.
image_username:
type: string
description: |
The username for accessing the registry specified in image.
default: ""
image_password:
type: string
description: |
The password associated with image_username for accessing
the registry specified in image.
default: ""
max_file_size:
type: int
description: |
The maximum file size, in megabytes.
If there is a reverse proxy in front of Keystone, it may
need to be configured to handle the requested size.
default: 5
ingress_whitelist_source_range:
type: string
description: |
A comma-separated list of CIDRs to store in the
ingress.kubernetes.io/whitelist-source-range annotation.
This can be used to lock down access to
Keystone based on source IP address.
default: ""
tls_secret_name:
type: string
description: TLS Secret name
default: ""
site_url:
type: string
description: Ingress URL
default: ""
ldap_enabled:
type: boolean
description: Boolean to enable/disable LDAP authentication
default: false
region_id:
type: string
description: Region ID to be created when starting the service
default: RegionOne
keystone_db_password:
type: string
description: Keystone DB Password
default: admin
admin_username:
type: string
description: Admin username to be created when starting the service
default: admin
admin_password:
type: string
description: Admin password to be created when starting the service
default: admin
admin_project:
type: string
description: Admin project to be created when starting the service
default: admin
service_username:
type: string
description: Service Username to be created when starting the service
default: nbi
service_password:
type: string
description: Service Password to be created when starting the service
default: nbi
service_project:
type: string
description: Service Project to be created when starting the service
default: service
user_domain_name:
type: string
description: User domain name (Hardcoded in the container start.sh script)
default: default
project_domain_name:
type: string
description: |
Project domain name (Hardcoded in the container start.sh script)
default: default
# ENV LDAP_AUTHENTICATION_DOMAIN_NAME no default
# ENV LDAP_URL ldap://localhost
# ENV LDAP_BIND_USER no defauslt
# ENV LDAP_BIND_PASSWORD no default
# ENV LDAP_USER_TREE_DN no default
# ENV LDAP_USER_OBJECTCLASS inetOrgPerson
# ENV LDAP_USER_ID_ATTRIBUTE cn
# ENV LDAP_USER_NAME_ATTRIBUTE sn
# ENV LDAP_USER_PASS_ATTRIBUTE userPassword
# ENV LDAP_USER_FILTER no default
# ENV LDAP_USER_ENABLED_ATTRIBUTE enabled
# ENV LDAP_USER_ENABLED_MASK 0
# ENV LDAP_USER_ENABLED_DEFAULT true
# ENV LDAP_USER_ENABLED_INVERT false
# ENV LDAP_USE_STARTTLS false
# ENV LDAP_TLS_CACERT_BASE64 no default
# ENV LDAP_TLS_REQ_CERT demand
# Copyright 2020 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.
name: keystone
summary: A Keystone K8s charm
description: |
Transmission
series:
- kubernetes
min-juju-version: 2.8.0
requires:
db:
interface: mysql
limit: 1
provides:
keystone:
interface: keystone
deployment:
type: stateless
service: cluster
# Copyright 2020 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.
ops
#!/usr/bin/env python3
# Copyright 2020 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.
import logging
from urllib.parse import urlparse
from ops.charm import CharmBase
# from ops.framework import StoredState
from ops.main import main
from ops.model import (
ActiveStatus,
BlockedStatus,
# MaintenanceStatus,
WaitingStatus,
# ModelError,
)
from ops.framework import StoredState
logger = logging.getLogger(__name__)
REQUIRED_SETTINGS = []
DATABASE_NAME = "keystone" # This is hardcoded in the keystone container script
# We expect the keystone container to use the default port
KEYSTONE_PORT = 5000
class KeystoneCharm(CharmBase):
state = StoredState()
def __init__(self, *args):
super().__init__(*args)
# Register all of the events we want to observe
self.framework.observe(self.on.config_changed, self.configure_pod)
self.framework.observe(self.on.start, self.configure_pod)
self.framework.observe(self.on.upgrade_charm, self.configure_pod)
# Register relation events
self.state.set_default(
db_host=None, db_port=None, db_user=None, db_password=None
)
self.framework.observe(
self.on.db_relation_changed, self._on_db_relation_changed
)
self.framework.observe(
self.on.keystone_relation_joined, self._publish_keystone_info
)
def _publish_keystone_info(self, event):
config = self.model.config
if self.unit.is_leader():
rel_data = {
"host": f"http://{self.app.name}:{KEYSTONE_PORT}/v3",
"port": str(KEYSTONE_PORT),
"keystone_db_password": config["keystone_db_password"],
"region_id": config["region_id"],
"user_domain_name": config["user_domain_name"],
"project_domain_name": config["project_domain_name"],
"admin_username": config["admin_username"],
"admin_password": config["admin_password"],
"admin_project_name": config["admin_project"],
"username": config["service_username"],
"password": config["service_password"],
"service": config["service_project"],
}
for k, v in rel_data.items():
event.relation.data[self.model.unit][k] = v
def _on_db_relation_changed(self, event):
self.state.db_host = event.relation.data[event.unit].get("host")
self.state.db_port = event.relation.data[event.unit].get("port", 3306)
self.state.db_user = "root" # event.relation.data[event.unit].get("user")
self.state.db_password = event.relation.data[event.unit].get("root_password")
if self.state.db_host:
self.configure_pod(event)
def _check_settings(self):
problems = []
config = self.model.config
for setting in REQUIRED_SETTINGS:
if not config.get(setting):
problem = f"missing config {setting}"
problems.append(problem)
return ";".join(problems)
def _make_pod_image_details(self):
config = self.model.config
image_details = {
"imagePath": config["image"],
}
if config["image_username"]:
image_details.update(
{
"username": config["image_username"],
"password": config["image_password"],
}
)
return image_details
def _make_pod_ports(self):
return [
{"name": "keystone", "containerPort": KEYSTONE_PORT, "protocol": "TCP"},
]
def _make_pod_envconfig(self):
config = self.model.config
return {
"DB_HOST": self.state.db_host,
"DB_PORT": self.state.db_port,
"ROOT_DB_USER": self.state.db_user,
"ROOT_DB_PASSWORD": self.state.db_password,
"KEYSTONE_DB_PASSWORD": config["keystone_db_password"],
"REGION_ID": config["region_id"],
"KEYSTONE_HOST": self.app.name,
"ADMIN_USERNAME": config["admin_username"],
"ADMIN_PASSWORD": config["admin_password"],
"ADMIN_PROJECT": config["admin_project"],
"SERVICE_USERNAME": config["service_username"],
"SERVICE_PASSWORD": config["service_password"],
"SERVICE_PROJECT": config["service_project"],
}
def _make_pod_ingress_resources(self):
site_url = self.model.config["site_url"]
if not site_url:
return
parsed = urlparse(site_url)
if not parsed.scheme.startswith("http"):
return
max_file_size = self.model.config["max_file_size"]
ingress_whitelist_source_range = self.model.config[
"ingress_whitelist_source_range"
]
annotations = {
"nginx.ingress.kubernetes.io/proxy-body-size": "{}m".format(max_file_size)
}
if ingress_whitelist_source_range:
annotations[
"nginx.ingress.kubernetes.io/whitelist-source-range"
] = ingress_whitelist_source_range
ingress_spec_tls = None
if parsed.scheme == "https":
ingress_spec_tls = [{"hosts": [parsed.hostname]}]
tls_secret_name = self.model.config["tls_secret_name"]
if tls_secret_name:
ingress_spec_tls[0]["secretName"] = tls_secret_name
else:
annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false"
ingress = {
"name": "{}-ingress".format(self.app.name),
"annotations": annotations,
"spec": {
"rules": [
{
"host": parsed.hostname,
"http": {
"paths": [
{
"path": "/",
"backend": {
"serviceName": self.app.name,
"servicePort": KEYSTONE_PORT,
},
}
]
},
}
],
},
}
if ingress_spec_tls:
ingress["spec"]["tls"] = ingress_spec_tls
return [ingress]
def configure_pod(self, event):
"""Assemble the pod spec and apply it, if possible."""
if not self.state.db_host:
self.unit.status = WaitingStatus("Waiting for database relation")
event.defer()
return
if not self.unit.is_leader():
self.unit.status = ActiveStatus()
return
# Check problems in the settings
problems = self._check_settings()
if problems:
self.unit.status = BlockedStatus(problems)
return
self.unit.status = BlockedStatus("Assembling pod spec")
image_details = self._make_pod_image_details()
ports = self._make_pod_ports()
env_config = self._make_pod_envconfig()
ingress_resources = self._make_pod_ingress_resources()
pod_spec = {
"version": 3,
"containers": [
{
"name": self.framework.model.app.name,
"imageDetails": image_details,
"ports": ports,
"envConfig": env_config,
}
],
"kubernetesResources": {"ingressResources": ingress_resources or []},
}
self.model.pod.set_spec(pod_spec)
self.unit.status = ActiveStatus()
if __name__ == "__main__":
main(KeystoneCharm)
# Copyright 2020 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.
[tox]
envlist = pep8
skipsdist = True
[testenv]
setenv = VIRTUAL_ENV={envdir}
PYTHONHASHSEED=0
install_command =
pip install {opts} {packages}
[testenv:build]
basepython = python3
passenv=HTTP_PROXY HTTPS_PROXY NO_PROXY
whitelist_externals = charmcraft
rm
unzip
commands =
rm -rf release
charmcraft build
unzip keystone.charm -d release
[testenv:lint]
basepython = python3
deps =
black
yamllint
flake8
commands =
black --check --diff . --exclude "build/|.tox/|mod/|lib/"
yamllint .
flake8 . --max-line-length=100 --exclude "build/ .tox/ mod/ lib/"
[testenv:venv]
commands = {posargs}
...@@ -324,6 +324,9 @@ applications: ...@@ -324,6 +324,9 @@ applications:
ng-ui: ng-ui:
options: options:
image: opensourcemano/ng-ui:$TAG image: opensourcemano/ng-ui:$TAG
keystone:
options:
image: opensourcemano/keystone:$TAG
EOF EOF
mv /tmp/images-overlay.yaml $IMAGES_OVERLAY_FILE mv /tmp/images-overlay.yaml $IMAGES_OVERLAY_FILE
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment