From: Mark Beierl Date: Wed, 22 Feb 2023 18:00:58 +0000 (-0500) Subject: Charm cleanup X-Git-Tag: release-v14.0-start~107 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=668b1469296df65fee65d62abbbbd087c69c2c04;p=osm%2Fdevops.git Charm cleanup Removal of obsolete charm code Change-Id: Ifc5e83457cf580d8b236a636328470c527c5c3a9 Signed-off-by: Mark Beierl --- diff --git a/devops-stages/stage-test.sh b/devops-stages/stage-test.sh index b455932d..b37ca5c6 100755 --- a/devops-stages/stage-test.sh +++ b/devops-stages/stage-test.sh @@ -24,7 +24,7 @@ CURRENT_DIR=`pwd` # Execute tests for charms CHARM_PATH="./installers/charm" -CHARM_NAMES="keystone lcm mon nbi ng-ui pla pol prometheus ro grafana mongodb-exporter mysqld-exporter kafka-exporter" +CHARM_NAMES="keystone prometheus grafana" for charm in $CHARM_NAMES; do cd $CHARM_PATH/$charm TOX_PARALLEL_NO_SPINNER=1 tox --parallel=auto diff --git a/installers/charm/build.sh b/installers/charm/build.sh deleted file mode 100755 index 459da132..00000000 --- a/installers/charm/build.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# 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. - -function build() { - cd $1 && tox -qe build && cd .. -} - -charms="ro nbi pla pol mon lcm ng-ui grafana prometheus mongodb-exporter kafka-exporter mysqld-exporter" -if [ -z `which charmcraft` ]; then - sudo snap install charmcraft --classic -fi - -for charm_directory in $charms; do - build $charm_directory -done -wait \ No newline at end of file diff --git a/installers/charm/interfaces/keystone/interface.yaml b/installers/charm/interfaces/keystone/interface.yaml deleted file mode 100644 index be1d09bd..00000000 --- a/installers/charm/interfaces/keystone/interface.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# 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 diff --git a/installers/charm/interfaces/keystone/provides.py b/installers/charm/interfaces/keystone/provides.py deleted file mode 100644 index bda5d2f4..00000000 --- a/installers/charm/interfaces/keystone/provides.py +++ /dev/null @@ -1,63 +0,0 @@ -# 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")) diff --git a/installers/charm/interfaces/keystone/requires.py b/installers/charm/interfaces/keystone/requires.py deleted file mode 100644 index c0d8d473..00000000 --- a/installers/charm/interfaces/keystone/requires.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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': , - 'port': , - 'keystone_db_password: , - 'region_id: , - 'admin_username: , - 'admin_password: , - 'admin_project_name: , - 'username: , - 'password: , - '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 diff --git a/installers/charm/interfaces/osm-nbi/README.md b/installers/charm/interfaces/osm-nbi/README.md deleted file mode 100644 index 8fb9523f..00000000 --- a/installers/charm/interfaces/osm-nbi/README.md +++ /dev/null @@ -1,63 +0,0 @@ - - -# Overview - -This interface layer handles communication between Mongodb and its clients. - -## Usage - -### Provides - -To implement this relation to offer an nbi: - -In your charm's metadata.yaml: - -```yaml -provides: - nbi: - interface: osm-nbi -``` - -reactive/mynbi.py: - -```python -@when('nbi.joined') -def send_config(nbi): - nbi.send_connection( - unit_get('private-address'), - get_nbi_port() - ) -``` - -### Requires - -If you would like to use an nbi from your charm: - -metadata.yaml: - -```yaml -requires: - nbi: - interface: osm-nbi -``` - -reactive/mycharm.py: - -```python -@when('nbi.ready') -def nbi_ready(): - nbi = endpoint_from_flag('nbi.ready') - if nbi: - for unit in nbi.nbis(): - add_nbi(unit['host'], unit['port']) -``` diff --git a/installers/charm/interfaces/osm-nbi/copyright b/installers/charm/interfaces/osm-nbi/copyright deleted file mode 100644 index dd9405e3..00000000 --- a/installers/charm/interfaces/osm-nbi/copyright +++ /dev/null @@ -1,16 +0,0 @@ -Format: http://dep.debian.net/deps/dep5/ - -Files: * -Copyright: Copyright 2020, Canonical Ltd., All Rights Reserved. -License: Apache License 2.0 - 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. \ No newline at end of file diff --git a/installers/charm/interfaces/osm-nbi/interface.yaml b/installers/charm/interfaces/osm-nbi/interface.yaml deleted file mode 100644 index ec8ee862..00000000 --- a/installers/charm/interfaces/osm-nbi/interface.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# 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: osm-nbi -summary: Interface for relating to a OSM Northbound Interface -maintainer: '"Adam Israel" ' diff --git a/installers/charm/interfaces/osm-nbi/provides.py b/installers/charm/interfaces/osm-nbi/provides.py deleted file mode 100644 index 7ff31994..00000000 --- a/installers/charm/interfaces/osm-nbi/provides.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python -# 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 RelationBase -from charms.reactive import hook -from charms.reactive import scopes - - -class OsmNBIProvides(RelationBase): - scope = scopes.GLOBAL - - @hook("{provides:osm-nbi}-relation-joined") - def joined(self): - self.set_state("{relation_name}.joined") - - @hook("{provides:osm-nbi}-relation-changed") - def changed(self): - self.set_state("{relation_name}.ready") - - @hook("{provides:osm-nbi}-relation-{broken,departed}") - def broken_departed(self): - self.remove_state("{relation_name}.ready") - self.remove_state("{relation_name}.joined") - - @hook("{provides:osm-nbi}-relation-broken") - def broken(self): - self.set_state("{relation_name}.removed") - - def send_connection(self, host, port=9999): - conv = self.conversation() - conv.set_remote("host", host) - conv.set_remote("port", port) diff --git a/installers/charm/interfaces/osm-nbi/requires.py b/installers/charm/interfaces/osm-nbi/requires.py deleted file mode 100644 index a5e8e29d..00000000 --- a/installers/charm/interfaces/osm-nbi/requires.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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 RelationBase -from charms.reactive import hook -from charms.reactive import scopes - - -class OsmNBIRequires(RelationBase): - scope = scopes.GLOBAL - - @hook("{requires:osm-nbi}-relation-joined") - def joined(self): - conv = self.conversation() - conv.set_state("{relation_name}.joined") - - @hook("{requires:osm-nbi}-relation-changed") - def changed(self): - conv = self.conversation() - if self.nbis(): - conv.set_state("{relation_name}.ready") - else: - conv.remove_state("{relation_name}.ready") - - @hook("{requires:osm-nbi}-relation-departed") - def departed(self): - conv = self.conversation() - conv.remove_state("{relation_name}.ready") - conv.remove_state("{relation_name}.joined") - - def nbis(self): - """Return the NBI's host and port. - - [{ - 'host': , - 'port': , - }] - """ - nbis = [] - for conv in self.conversations(): - port = conv.get_remote("port") - host = conv.get_remote("host") or conv.get_remote("private-address") - if host and port: - nbis.append({"host": host, "port": port}) - return nbis diff --git a/installers/charm/interfaces/osm-ro/README.md b/installers/charm/interfaces/osm-ro/README.md deleted file mode 100644 index eb6413a3..00000000 --- a/installers/charm/interfaces/osm-ro/README.md +++ /dev/null @@ -1,63 +0,0 @@ - - -# Overview - -This interface layer handles communication between OSM's RO and its clients. - -## Usage - -### Provides - -To implement this relation to offer an ro: - -In your charm's metadata.yaml: - -```yaml -provides: - ro: - interface: osm-ro -``` - -reactive/myro.py: - -```python -@when('ro.joined') -def send_config(ro): - ro.send_connection( - unit_get('private-address'), - get_ro_port() - ) -``` - -### Requires - -If you would like to use a rodb from your charm: - -metadata.yaml: - -```yaml -requires: - ro: - interface: osm-ro -``` - -reactive/mycharm.py: - -```python -@when('ro.ready') -def ro_ready(): - ro = endpoint_from_flag('ro.ready') - if ro: - for unit in ro.ros(): - add_ro(unit['host'], unit['port']) -``` diff --git a/installers/charm/interfaces/osm-ro/copyright b/installers/charm/interfaces/osm-ro/copyright deleted file mode 100644 index 9270d6ca..00000000 --- a/installers/charm/interfaces/osm-ro/copyright +++ /dev/null @@ -1,16 +0,0 @@ -Format: http://dep.debian.net/deps/dep5/ - -Files: * -Copyright: Copyright 2020, Canonical Ltd., All Rights Reserved. -License: Apache License 2.0 - 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. diff --git a/installers/charm/interfaces/osm-ro/interface.yaml b/installers/charm/interfaces/osm-ro/interface.yaml deleted file mode 100644 index 9a12872b..00000000 --- a/installers/charm/interfaces/osm-ro/interface.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# 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: osm-ro -summary: Interface for relating to a OSM Resource Orchestrator -maintainer: '"Adam Israel" ' diff --git a/installers/charm/interfaces/osm-ro/provides.py b/installers/charm/interfaces/osm-ro/provides.py deleted file mode 100644 index f5773194..00000000 --- a/installers/charm/interfaces/osm-ro/provides.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python -# 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 RelationBase -from charms.reactive import hook -from charms.reactive import scopes - - -class OsmROProvides(RelationBase): - scope = scopes.GLOBAL - - @hook("{provides:osm-ro}-relation-joined") - def joined(self): - self.set_state("{relation_name}.joined") - - @hook("{provides:osm-ro}-relation-changed") - def changed(self): - self.set_state("{relation_name}.ready") - - @hook("{provides:osm-ro}-relation-{broken,departed}") - def broken_departed(self): - self.remove_state("{relation_name}.ready") - self.remove_state("{relation_name}.joined") - - @hook("{provides:osm-ro}-relation-broken") - def broken(self): - self.set_state("{relation_name}.removed") - - def send_connection(self, host, port=9090): - conv = self.conversation() - conv.set_remote("host", host) - conv.set_remote("port", port) diff --git a/installers/charm/interfaces/osm-ro/requires.py b/installers/charm/interfaces/osm-ro/requires.py deleted file mode 100644 index fc8f0f4a..00000000 --- a/installers/charm/interfaces/osm-ro/requires.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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 RelationBase -from charms.reactive import hook -from charms.reactive import scopes - - -class OsmRORequires(RelationBase): - scope = scopes.GLOBAL - - @hook("{requires:osm-ro}-relation-joined") - def joined(self): - conv = self.conversation() - conv.set_state("{relation_name}.joined") - - @hook("{requires:osm-ro}-relation-changed") - def changed(self): - conv = self.conversation() - if self.ros(): - conv.set_state("{relation_name}.ready") - else: - conv.remove_state("{relation_name}.ready") - - @hook("{requires:osm-ro}-relation-departed") - def departed(self): - conv = self.conversation() - conv.remove_state("{relation_name}.ready") - conv.remove_state("{relation_name}.joined") - - def ros(self): - """Return the NBI's host and port. - - [{ - 'host': , - 'port': , - }] - """ - ros = [] - for conv in self.conversations(): - port = conv.get_remote("port") - host = conv.get_remote("host") or conv.get_remote("private-address") - if host and port: - ros.append({"host": host, "port": port}) - return ros diff --git a/installers/charm/layers/osm-common/README.md b/installers/charm/layers/osm-common/README.md deleted file mode 100644 index c55b97b0..00000000 --- a/installers/charm/layers/osm-common/README.md +++ /dev/null @@ -1,17 +0,0 @@ - - -# README - -WIP. Layer to share common functionality to write/deploy k8s charms for OSM demo diff --git a/installers/charm/layers/osm-common/layer.yaml b/installers/charm/layers/osm-common/layer.yaml deleted file mode 100644 index 6e8379ab..00000000 --- a/installers/charm/layers/osm-common/layer.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# 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. \ No newline at end of file diff --git a/installers/charm/layers/osm-common/lib/charms/osm/k8s.py b/installers/charm/layers/osm-common/lib/charms/osm/k8s.py deleted file mode 100644 index 9735517f..00000000 --- a/installers/charm/layers/osm-common/lib/charms/osm/k8s.py +++ /dev/null @@ -1,76 +0,0 @@ -# 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 charmhelpers.core.hookenv import ( - network_get, - relation_id, - log, -) - - -def get_service_ip(endpoint): - try: - info = network_get(endpoint, relation_id()) - if 'ingress-addresses' in info: - addr = info['ingress-addresses'][0] - if len(addr): - return addr - else: - log("No ingress-addresses: {}".format(info)) - except Exception as e: - log("Caught exception checking for service IP: {}".format(e)) - - return None - - -def is_pod_up(endpoint): - """Check to see if the pod of a relation is up. - - application-vimdb: 19:29:10 INFO unit.vimdb/0.juju-log network info - - In the example below: - - 10.1.1.105 is the address of the application pod. - - 10.152.183.199 is the service cluster ip - - { - 'bind-addresses': [{ - 'macaddress': '', - 'interfacename': '', - 'addresses': [{ - 'hostname': '', - 'address': '10.1.1.105', - 'cidr': '' - }] - }], - 'egress-subnets': [ - '10.152.183.199/32' - ], - 'ingress-addresses': [ - '10.152.183.199', - '10.1.1.105' - ] - } - """ - try: - info = network_get(endpoint, relation_id()) - - # Check to see if the pod has been assigned it's internal and - # external ips - for ingress in info['ingress-addresses']: - if len(ingress) == 0: - return False - except: - return False - - return True diff --git a/installers/charm/layers/osm-common/metadata.yaml b/installers/charm/layers/osm-common/metadata.yaml deleted file mode 100644 index 6e8379ab..00000000 --- a/installers/charm/layers/osm-common/metadata.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# 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. \ No newline at end of file diff --git a/installers/charm/layers/osm-common/reactive/osm_common.py b/installers/charm/layers/osm-common/reactive/osm_common.py deleted file mode 100644 index 6e8379ab..00000000 --- a/installers/charm/layers/osm-common/reactive/osm_common.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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. \ No newline at end of file diff --git a/installers/charm/lcm/.gitignore b/installers/charm/lcm/.gitignore deleted file mode 100644 index 2885df27..00000000 --- a/installers/charm/lcm/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.stestr -cover -release \ No newline at end of file diff --git a/installers/charm/lcm/.jujuignore b/installers/charm/lcm/.jujuignore deleted file mode 100644 index 3ae3e7dc..00000000 --- a/installers/charm/lcm/.jujuignore +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.gitignore -.stestr -cover -release -tests/ -requirements* -tox.ini diff --git a/installers/charm/lcm/.yamllint.yaml b/installers/charm/lcm/.yamllint.yaml deleted file mode 100644 index d71fb69f..00000000 --- a/installers/charm/lcm/.yamllint.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - ---- -extends: default - -yaml-files: - - "*.yaml" - - "*.yml" - - ".yamllint" -ignore: | - .tox - cover/ - build/ - venv - release/ diff --git a/installers/charm/lcm/README.md b/installers/charm/lcm/README.md deleted file mode 100644 index 1a6cd746..00000000 --- a/installers/charm/lcm/README.md +++ /dev/null @@ -1,23 +0,0 @@ - - -# LCM operator Charm for Kubernetes - -## Requirements diff --git a/installers/charm/lcm/charmcraft.yaml b/installers/charm/lcm/charmcraft.yaml deleted file mode 100644 index 0a285a9d..00000000 --- a/installers/charm/lcm/charmcraft.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -type: charm -bases: - - build-on: - - name: ubuntu - channel: "20.04" - architectures: ["amd64"] - run-on: - - name: ubuntu - channel: "20.04" - architectures: - - amd64 - - aarch64 - - arm64 -parts: - charm: - build-packages: [git] diff --git a/installers/charm/lcm/config.yaml b/installers/charm/lcm/config.yaml deleted file mode 100644 index 709a8ca6..00000000 --- a/installers/charm/lcm/config.yaml +++ /dev/null @@ -1,318 +0,0 @@ -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -options: - vca_host: - type: string - description: "The VCA host." - vca_port: - type: int - description: "The VCA port." - vca_user: - type: string - description: "The VCA user name." - vca_secret: - type: string - description: "The VCA user secret." - vca_pubkey: - type: string - description: "The VCA public key." - vca_cacert: - type: string - description: "The VCA cacert." - vca_apiproxy: - type: string - description: "The VCA api proxy (native charms)" - vca_cloud: - type: string - description: "The VCA lxd cloud name" - vca_k8s_cloud: - type: string - description: "The VCA K8s cloud name" - database_commonkey: - description: Database common key - type: string - default: osm - mongodb_uri: - type: string - description: MongoDB URI (external database) - log_level: - description: "Log Level" - type: string - default: "INFO" - vca_model_config_agent_metadata_url: - description: The URL of the private stream. - type: string - vca_model_config_agent_stream: - description: | - The stream to use for deploy/upgrades of agents. - See additional info below. - type: string - vca_model_config_apt_ftp_proxy: - description: The APT FTP proxy for the model. - type: string - vca_model_config_apt_http_proxy: - description: The APT HTTP proxy for the model. - type: string - vca_model_config_apt_https_proxy: - description: The APT HTTPS proxy for the model. - type: string - vca_model_config_apt_mirror: - description: The APT mirror for the model. - type: string - vca_model_config_apt_no_proxy: - description: The APT no proxy for the model. - type: string - vca_model_config_automatically_retry_hooks: - description: Set the policy on retying failed hooks. - type: boolean - vca_model_config_backup_dir: - description: Backup directory - type: string - vca_model_config_cloudinit_userdata: - description: Cloudinit userdata - type: string - vca_model_config_container_image_metadata_url: - description: | - Corresponds to 'image-metadata-url' (see below) for cloud-hosted - KVM guests or LXD containers. Not needed for the localhost cloud. - type: string - vca_model_config_container_image_stream: - description: | - Corresponds to 'image-stream' (see below) for cloud-hosted KVM - guests or LXD containers. Not needed for the localhost cloud. - type: string - vca_model_config_container_inherit_properties: - description: | - Set parameters to be inherited from a machine toits hosted - containers (KVM or LXD). - type: string - vca_model_config_container_networking_method: - description: | - The FAN networking mode to use. Default values can be provider-specific. - type: string - vca_model_config_default_series: - description: The default series of Ubuntu to use for deploying charms. - type: string - vca_model_config_default_space: - description: | - The space used as the default binding when deploying charms. - Will be "alpha" by default. - type: string - vca_model_config_development: - description: Set whether the model is in development mode. - type: boolean - vca_model_config_disable_network_management: - description: | - Set whether to give network control to the provider instead - of Juju controlling configuration. - type: boolean - vca_model_config_egress_subnets: - description: Egress subnets - type: string - vca_model_config_enable_os_refresh_update: - description: | - Set whether newly provisioned instances should run their - respective OS's update capability. - type: boolean - vca_model_config_enable_os_upgrade: - description: | - Set whether newly provisioned instances should run their - respective OS's upgrade capability. - type: boolean - vca_model_config_fan_config: - description: | - The FAN overlay and underlay networks in - CIDR notation (space-separated). - type: string - vca_model_config_firewall_mode: - description: The mode to use for network firewalling. - type: string - vca_model_config_ftp_proxy: - description: | - The FTP proxy value to configure on instances, - in the FTP_PROXY environment variable. - type: string - vca_model_config_http_proxy: - description: | - The HTTP proxy value to configure on instances, - in the HTTP_PROXY environment variable. - type: string - vca_model_config_https_proxy: - description: | - The HTTPS proxy value to configure on instances, - in the HTTPS_PROXY environment variable. - type: string - vca_model_config_ignore_machine_addresses: - description: | - When true, the machine worker will not look up - or discover any machine addresses. - type: boolean - vca_model_config_image_metadata_url: - description: | - The URL at which the metadata used to locate - OS image ids is located. - type: string - vca_model_config_image_stream: - description: | - The simplestreams stream used to identify which image - ids to search when starting an instance. - type: string - vca_model_config_juju_ftp_proxy: - description: The charm-centric FTP proxy value. - type: string - vca_model_config_juju_http_proxy: - description: The charm-centric HTTP proxy value. - type: string - vca_model_config_juju_https_proxy: - description: The charm-centric HTTPS proxy value. - type: string - vca_model_config_juju_no_proxy: - description: The charm-centric no-proxy value. - type: string - vca_model_config_logforward_enabled: - description: Set whether the log forward function is enabled. - type: boolean - vca_model_config_logging_config: - description: | - The configuration string to use when configuring Juju agent logging - type: string - vca_model_config_lxd_snap_channel: - description: LXD snap channel - type: string - vca_model_config_max_action_results_age: - description: The maximum aget for status action results entries - type: string - vca_model_config_max_action_results_size: - description: The maximum size for status action results entries - type: string - vca_model_config_max_status_history_age: - description: | - The maximum age for status history entries before they are pruned, - in a human-readable time format. - type: string - vca_model_config_max_status_history_size: - description: | - The maximum size for the status history collection, - in human-readable memory format. - type: string - vca_model_config_net_bond_reconfigure_delay: - description: Net bond reconfigure delay - type: int - vca_model_config_no_proxy: - description: List of domain addresses not to be proxied (comma-separated). - type: string - vca_model_config_provisioner_harvest_mode: - description: Set what to do with unknown machines. - type: string - vca_model_config_proxy_ssh: - description: | - Set whether SSH commands should be proxied through the API server. - type: boolean - vca_model_config_snap_http_proxy: - description: The snap-centric HTTP proxy value. - type: string - vca_model_config_snap_https_proxy: - description: The snap-centric HTTPS proxy value. - type: string - vca_model_config_snap_store_assertions: - description: | - The collection of snap store assertions. - Each entry should contain the snap store ID. - type: string - vca_model_config_snap_store_proxy: - description: The snap store ID. - type: string - vca_model_config_snap_store_proxy_url: - description: The snap store proxy url - type: string - vca_model_config_ssl_hostname_verification: - description: Set whether SSL hostname verification is enabled. - type: boolean - vca_model_config_test_mode: - description: | - Set whether the model is intended for testing. - If true, accessing the charm store does not affect - statistical data of the store. - type: boolean - vca_model_config_transmit_vendor_metrics: - description: | - Set whether the controller will send metrics collected from - this model for use in anonymized aggregate analytics. - type: boolean - vca_model_config_update_status_hook_interval: - description: | - The run frequency of the update-status hook. - The value has a random +/- 20% offset applied to avoid hooks - for all units firing at once. Value change only honoured - during controller and model creation - (bootstrap --config and add-model --config). - type: string - vca_stablerepourl: - description: Stable repository URL for Helm charts - type: string - default: https://charts.helm.sh/stable - vca_helm_ca_certs: - description: CA certificates to validate access to Helm repository - type: string - default: "" - image_pull_policy: - type: string - description: | - ImagePullPolicy configuration for the pod. - Possible values: always, ifnotpresent, never - default: always - debug_mode: - description: | - If true, debug mode is activated. It means that the service will not run, - and instead, the command for the container will be a `sleep infinity`. - Note: If enabled, security_context will be disabled. - type: boolean - default: false - debug_pubkey: - description: | - Public SSH key that will be injected to the application pod. - type: string - debug_lcm_local_path: - description: | - Local full path to the LCM project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - debug_n2vc_local_path: - description: | - Local full path to the N2VC project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - debug_common_local_path: - description: | - Local full path to the COMMON project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - security_context: - description: Enables the security context of the pods - type: boolean - default: false diff --git a/installers/charm/lcm/lib/charms/kafka_k8s/v0/kafka.py b/installers/charm/lcm/lib/charms/kafka_k8s/v0/kafka.py deleted file mode 100644 index 1baf9a88..00000000 --- a/installers/charm/lcm/lib/charms/kafka_k8s/v0/kafka.py +++ /dev/null @@ -1,207 +0,0 @@ -# Copyright 2022 Canonical Ltd. -# See LICENSE file for licensing details. -# -# 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. - -"""Kafka library. - -This [library](https://juju.is/docs/sdk/libraries) implements both sides of the -`kafka` [interface](https://juju.is/docs/sdk/relations). - -The *provider* side of this interface is implemented by the -[kafka-k8s Charmed Operator](https://charmhub.io/kafka-k8s). - -Any Charmed Operator that *requires* Kafka for providing its -service should implement the *requirer* side of this interface. - -In a nutshell using this library to implement a Charmed Operator *requiring* -Kafka would look like - -``` -$ charmcraft fetch-lib charms.kafka_k8s.v0.kafka -``` - -`metadata.yaml`: - -``` -requires: - kafka: - interface: kafka - limit: 1 -``` - -`src/charm.py`: - -``` -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.charm import CharmBase - - -class MyCharm(CharmBase): - - on = KafkaEvents() - - def __init__(self, *args): - super().__init__(*args) - self.kafka = KafkaRequires(self) - self.framework.observe( - self.on.kafka_available, - self._on_kafka_available, - ) - self.framework.observe( - self.on.kafka_broken, - self._on_kafka_broken, - ) - - def _on_kafka_available(self, event): - # Get Kafka host and port - host: str = self.kafka.host - port: int = self.kafka.port - # host => "kafka-k8s" - # port => 9092 - - def _on_kafka_broken(self, event): - # Stop service - # ... - self.unit.status = BlockedStatus("need kafka relation") -``` - -You can file bugs -[here](https://github.com/charmed-osm/kafka-k8s-operator/issues)! -""" - -from typing import Optional - -from ops.charm import CharmBase, CharmEvents -from ops.framework import EventBase, EventSource, Object - -# The unique Charmhub library identifier, never change it -from ops.model import Relation - -LIBID = "eacc8c85082347c9aae740e0220b8376" - -# 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 = 3 - - -KAFKA_HOST_APP_KEY = "host" -KAFKA_PORT_APP_KEY = "port" - - -class _KafkaAvailableEvent(EventBase): - """Event emitted when Kafka is available.""" - - -class _KafkaBrokenEvent(EventBase): - """Event emitted when Kafka relation is broken.""" - - -class KafkaEvents(CharmEvents): - """Kafka events. - - This class defines the events that Kafka can emit. - - Events: - kafka_available (_KafkaAvailableEvent) - """ - - kafka_available = EventSource(_KafkaAvailableEvent) - kafka_broken = EventSource(_KafkaBrokenEvent) - - -class KafkaRequires(Object): - """Requires-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self.charm = charm - self._endpoint_name = endpoint_name - - # Observe relation events - event_observe_mapping = { - charm.on[self._endpoint_name].relation_changed: self._on_relation_changed, - charm.on[self._endpoint_name].relation_broken: self._on_relation_broken, - } - for event, observer in event_observe_mapping.items(): - self.framework.observe(event, observer) - - def _on_relation_changed(self, event) -> None: - if event.relation.app and all( - key in event.relation.data[event.relation.app] - for key in (KAFKA_HOST_APP_KEY, KAFKA_PORT_APP_KEY) - ): - self.charm.on.kafka_available.emit() - - def _on_relation_broken(self, _) -> None: - self.charm.on.kafka_broken.emit() - - @property - def host(self) -> str: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - relation.data[relation.app].get(KAFKA_HOST_APP_KEY) - if relation and relation.app - else None - ) - - @property - def port(self) -> int: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - int(relation.data[relation.app].get(KAFKA_PORT_APP_KEY)) - if relation and relation.app - else None - ) - - -class KafkaProvides(Object): - """Provides-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self._endpoint_name = endpoint_name - - def set_host_info(self, host: str, port: int, relation: Optional[Relation] = None) -> None: - """Set Kafka host and port. - - This function writes in the application data of the relation, therefore, - only the unit leader can call it. - - Args: - host (str): Kafka hostname or IP address. - port (int): Kafka port. - relation (Optional[Relation]): Relation to update. - If not specified, all relations will be updated. - - Raises: - Exception: if a non-leader unit calls this function. - """ - if not self.model.unit.is_leader(): - raise Exception("only the leader set host information.") - - if relation: - self._update_relation_data(host, port, relation) - return - - for relation in self.model.relations[self._endpoint_name]: - self._update_relation_data(host, port, relation) - - def _update_relation_data(self, host: str, port: int, relation: Relation) -> None: - """Update data in relation if needed.""" - relation.data[self.model.app][KAFKA_HOST_APP_KEY] = host - relation.data[self.model.app][KAFKA_PORT_APP_KEY] = str(port) diff --git a/installers/charm/lcm/metadata.yaml b/installers/charm/lcm/metadata.yaml deleted file mode 100644 index e81cdd9b..00000000 --- a/installers/charm/lcm/metadata.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -name: osm-lcm -summary: OSM Lifecycle Management (LCM) -description: | - A CAAS charm to deploy OSM's Lifecycle Management (LCM). -series: - - kubernetes -tags: - - kubernetes - - osm - - lcm -min-juju-version: 2.8.0 -deployment: - type: stateless - service: cluster -resources: - image: - type: oci-image - description: OSM docker image for LCM - upstream-source: "opensourcemano/lcm:latest" -requires: - kafka: - interface: kafka - limit: 1 - mongodb: - interface: mongodb - limit: 1 - ro: - interface: http - limit: 1 diff --git a/installers/charm/lcm/requirements-test.txt b/installers/charm/lcm/requirements-test.txt deleted file mode 100644 index cf61dd4e..00000000 --- a/installers/charm/lcm/requirements-test.txt +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -mock==4.0.3 diff --git a/installers/charm/lcm/requirements.txt b/installers/charm/lcm/requirements.txt deleted file mode 100644 index 1a8928c7..00000000 --- a/installers/charm/lcm/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master \ No newline at end of file diff --git a/installers/charm/lcm/src/charm.py b/installers/charm/lcm/src/charm.py deleted file mode 100755 index 5319763f..00000000 --- a/installers/charm/lcm/src/charm.py +++ /dev/null @@ -1,573 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -# pylint: disable=E0213 - - -import logging -from typing import NoReturn, Optional - - -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.main import main -from opslib.osm.charm import CharmedOsmBase, RelationsMissing -from opslib.osm.interfaces.http import HttpClient -from opslib.osm.interfaces.mongo import MongoClient -from opslib.osm.pod import ContainerV3Builder, PodRestartPolicy, PodSpecV3Builder -from opslib.osm.validator import ModelValidator, validator - - -logger = logging.getLogger(__name__) - -PORT = 9999 - - -class ConfigModel(ModelValidator): - vca_host: Optional[str] - vca_port: Optional[int] - vca_user: Optional[str] - vca_secret: Optional[str] - vca_pubkey: Optional[str] - vca_cacert: Optional[str] - vca_cloud: Optional[str] - vca_k8s_cloud: Optional[str] - database_commonkey: str - mongodb_uri: Optional[str] - log_level: str - vca_apiproxy: Optional[str] - # Model-config options - vca_model_config_agent_metadata_url: Optional[str] - vca_model_config_agent_stream: Optional[str] - vca_model_config_apt_ftp_proxy: Optional[str] - vca_model_config_apt_http_proxy: Optional[str] - vca_model_config_apt_https_proxy: Optional[str] - vca_model_config_apt_mirror: Optional[str] - vca_model_config_apt_no_proxy: Optional[str] - vca_model_config_automatically_retry_hooks: Optional[bool] - vca_model_config_backup_dir: Optional[str] - vca_model_config_cloudinit_userdata: Optional[str] - vca_model_config_container_image_metadata_url: Optional[str] - vca_model_config_container_image_stream: Optional[str] - vca_model_config_container_inherit_properties: Optional[str] - vca_model_config_container_networking_method: Optional[str] - vca_model_config_default_series: Optional[str] - vca_model_config_default_space: Optional[str] - vca_model_config_development: Optional[bool] - vca_model_config_disable_network_management: Optional[bool] - vca_model_config_egress_subnets: Optional[str] - vca_model_config_enable_os_refresh_update: Optional[bool] - vca_model_config_enable_os_upgrade: Optional[bool] - vca_model_config_fan_config: Optional[str] - vca_model_config_firewall_mode: Optional[str] - vca_model_config_ftp_proxy: Optional[str] - vca_model_config_http_proxy: Optional[str] - vca_model_config_https_proxy: Optional[str] - vca_model_config_ignore_machine_addresses: Optional[bool] - vca_model_config_image_metadata_url: Optional[str] - vca_model_config_image_stream: Optional[str] - vca_model_config_juju_ftp_proxy: Optional[str] - vca_model_config_juju_http_proxy: Optional[str] - vca_model_config_juju_https_proxy: Optional[str] - vca_model_config_juju_no_proxy: Optional[str] - vca_model_config_logforward_enabled: Optional[bool] - vca_model_config_logging_config: Optional[str] - vca_model_config_lxd_snap_channel: Optional[str] - vca_model_config_max_action_results_age: Optional[str] - vca_model_config_max_action_results_size: Optional[str] - vca_model_config_max_status_history_age: Optional[str] - vca_model_config_max_status_history_size: Optional[str] - vca_model_config_net_bond_reconfigure_delay: Optional[str] - vca_model_config_no_proxy: Optional[str] - vca_model_config_provisioner_harvest_mode: Optional[str] - vca_model_config_proxy_ssh: Optional[bool] - vca_model_config_snap_http_proxy: Optional[str] - vca_model_config_snap_https_proxy: Optional[str] - vca_model_config_snap_store_assertions: Optional[str] - vca_model_config_snap_store_proxy: Optional[str] - vca_model_config_snap_store_proxy_url: Optional[str] - vca_model_config_ssl_hostname_verification: Optional[bool] - vca_model_config_test_mode: Optional[bool] - vca_model_config_transmit_vendor_metrics: Optional[bool] - vca_model_config_update_status_hook_interval: Optional[str] - vca_stablerepourl: Optional[str] - vca_helm_ca_certs: Optional[str] - image_pull_policy: str - debug_mode: bool - security_context: bool - - @validator("log_level") - def validate_log_level(cls, v): - if v not in {"INFO", "DEBUG"}: - raise ValueError("value must be INFO or DEBUG") - return v - - @validator("mongodb_uri") - def validate_mongodb_uri(cls, v): - if v and not v.startswith("mongodb://"): - raise ValueError("mongodb_uri is not properly formed") - return v - - @validator("image_pull_policy") - def validate_image_pull_policy(cls, v): - values = { - "always": "Always", - "ifnotpresent": "IfNotPresent", - "never": "Never", - } - v = v.lower() - if v not in values.keys(): - raise ValueError("value must be always, ifnotpresent or never") - return values[v] - - -class LcmCharm(CharmedOsmBase): - on = KafkaEvents() - - def __init__(self, *args) -> NoReturn: - super().__init__( - *args, - oci_image="image", - vscode_workspace=VSCODE_WORKSPACE, - ) - if self.config.get("debug_mode"): - self.enable_debug_mode( - pubkey=self.config.get("debug_pubkey"), - hostpaths={ - "LCM": { - "hostpath": self.config.get("debug_lcm_local_path"), - "container-path": "/usr/lib/python3/dist-packages/osm_lcm", - }, - "N2VC": { - "hostpath": self.config.get("debug_n2vc_local_path"), - "container-path": "/usr/lib/python3/dist-packages/n2vc", - }, - "osm_common": { - "hostpath": self.config.get("debug_common_local_path"), - "container-path": "/usr/lib/python3/dist-packages/osm_common", - }, - }, - ) - self.kafka = KafkaRequires(self) - self.framework.observe(self.on.kafka_available, self.configure_pod) - self.framework.observe(self.on.kafka_broken, self.configure_pod) - - self.mongodb_client = MongoClient(self, "mongodb") - self.framework.observe(self.on["mongodb"].relation_changed, self.configure_pod) - self.framework.observe(self.on["mongodb"].relation_broken, self.configure_pod) - - self.ro_client = HttpClient(self, "ro") - self.framework.observe(self.on["ro"].relation_changed, self.configure_pod) - self.framework.observe(self.on["ro"].relation_broken, self.configure_pod) - - def _check_missing_dependencies(self, config: ConfigModel): - missing_relations = [] - - if not self.kafka.host or not self.kafka.port: - missing_relations.append("kafka") - if not config.mongodb_uri and self.mongodb_client.is_missing_data_in_unit(): - missing_relations.append("mongodb") - if self.ro_client.is_missing_data_in_app(): - missing_relations.append("ro") - - if missing_relations: - raise RelationsMissing(missing_relations) - - def build_pod_spec(self, image_info): - # Validate config - config = ConfigModel(**dict(self.config)) - - if config.mongodb_uri and not self.mongodb_client.is_missing_data_in_unit(): - raise Exception("Mongodb data cannot be provided via config and relation") - - # Check relations - self._check_missing_dependencies(config) - - security_context_enabled = ( - config.security_context if not config.debug_mode else False - ) - - # Create Builder for the PodSpec - pod_spec_builder = PodSpecV3Builder( - enable_security_context=security_context_enabled - ) - - # Add secrets to the pod - mongodb_secret_name = f"{self.app.name}-mongodb-secret" - pod_spec_builder.add_secret( - mongodb_secret_name, - { - "uri": config.mongodb_uri or self.mongodb_client.connection_string, - "commonkey": config.database_commonkey, - "helm_ca_certs": config.vca_helm_ca_certs, - }, - ) - - # Build Container - container_builder = ContainerV3Builder( - self.app.name, - image_info, - config.image_pull_policy, - run_as_non_root=security_context_enabled, - ) - container_builder.add_port(name=self.app.name, port=PORT) - container_builder.add_envs( - { - # General configuration - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMLCM_GLOBAL_LOGLEVEL": config.log_level, - # RO configuration - "OSMLCM_RO_HOST": self.ro_client.host, - "OSMLCM_RO_PORT": self.ro_client.port, - "OSMLCM_RO_TENANT": "osm", - # Kafka configuration - "OSMLCM_MESSAGE_DRIVER": "kafka", - "OSMLCM_MESSAGE_HOST": self.kafka.host, - "OSMLCM_MESSAGE_PORT": self.kafka.port, - # Database configuration - "OSMLCM_DATABASE_DRIVER": "mongo", - # Storage configuration - "OSMLCM_STORAGE_DRIVER": "mongo", - "OSMLCM_STORAGE_PATH": "/app/storage", - "OSMLCM_STORAGE_COLLECTION": "files", - "OSMLCM_VCA_STABLEREPOURL": config.vca_stablerepourl, - } - ) - container_builder.add_secret_envs( - secret_name=mongodb_secret_name, - envs={ - "OSMLCM_DATABASE_URI": "uri", - "OSMLCM_DATABASE_COMMONKEY": "commonkey", - "OSMLCM_STORAGE_URI": "uri", - "OSMLCM_VCA_HELM_CA_CERTS": "helm_ca_certs", - }, - ) - if config.vca_host: - vca_secret_name = f"{self.app.name}-vca-secret" - pod_spec_builder.add_secret( - vca_secret_name, - { - "host": config.vca_host, - "port": str(config.vca_port), - "user": config.vca_user, - "pubkey": config.vca_pubkey, - "secret": config.vca_secret, - "cacert": config.vca_cacert, - "cloud": config.vca_cloud, - "k8s_cloud": config.vca_k8s_cloud, - }, - ) - container_builder.add_secret_envs( - secret_name=vca_secret_name, - envs={ - # VCA configuration - "OSMLCM_VCA_HOST": "host", - "OSMLCM_VCA_PORT": "port", - "OSMLCM_VCA_USER": "user", - "OSMLCM_VCA_PUBKEY": "pubkey", - "OSMLCM_VCA_SECRET": "secret", - "OSMLCM_VCA_CACERT": "cacert", - "OSMLCM_VCA_CLOUD": "cloud", - "OSMLCM_VCA_K8S_CLOUD": "k8s_cloud", - }, - ) - if config.vca_apiproxy: - container_builder.add_env("OSMLCM_VCA_APIPROXY", config.vca_apiproxy) - - model_config_envs = { - f"OSMLCM_{k.upper()}": v - for k, v in self.config.items() - if k.startswith("vca_model_config") - } - if model_config_envs: - container_builder.add_envs(model_config_envs) - container = container_builder.build() - - # Add container to pod spec - pod_spec_builder.add_container(container) - - # Add restart policy - restart_policy = PodRestartPolicy() - restart_policy.add_secrets() - pod_spec_builder.set_restart_policy(restart_policy) - - return pod_spec_builder.build() - - -VSCODE_WORKSPACE = { - "folders": [ - {"path": "/usr/lib/python3/dist-packages/osm_lcm"}, - {"path": "/usr/lib/python3/dist-packages/n2vc"}, - {"path": "/usr/lib/python3/dist-packages/osm_common"}, - ], - "settings": {}, - "launch": { - "version": "0.2.0", - "configurations": [ - { - "name": "LCM", - "type": "python", - "request": "launch", - "module": "osm_lcm.lcm", - "justMyCode": False, - } - ], - }, -} - - -if __name__ == "__main__": - main(LcmCharm) - - -# class ConfigurePodEvent(EventBase): -# """Configure Pod event""" - -# pass - - -# class LcmEvents(CharmEvents): -# """LCM Events""" - -# configure_pod = EventSource(ConfigurePodEvent) - - -# class LcmCharm(CharmBase): -# """LCM Charm.""" - -# state = StoredState() -# on = LcmEvents() - -# def __init__(self, *args) -> NoReturn: -# """LCM Charm constructor.""" -# super().__init__(*args) - -# # Internal state initialization -# self.state.set_default(pod_spec=None) - -# # Message bus data initialization -# self.state.set_default(message_host=None) -# self.state.set_default(message_port=None) - -# # Database data initialization -# self.state.set_default(database_uri=None) - -# # RO data initialization -# self.state.set_default(ro_host=None) -# self.state.set_default(ro_port=None) - -# self.port = LCM_PORT -# self.image = OCIImageResource(self, "image") - -# # Registering regular events -# self.framework.observe(self.on.start, self.configure_pod) -# self.framework.observe(self.on.config_changed, self.configure_pod) -# self.framework.observe(self.on.upgrade_charm, self.configure_pod) - -# # Registering custom internal events -# self.framework.observe(self.on.configure_pod, self.configure_pod) - -# # Registering required relation events -# self.framework.observe( -# self.on.kafka_relation_changed, self._on_kafka_relation_changed -# ) -# self.framework.observe( -# self.on.mongodb_relation_changed, self._on_mongodb_relation_changed -# ) -# self.framework.observe( -# self.on.ro_relation_changed, self._on_ro_relation_changed -# ) - -# # Registering required relation broken events -# self.framework.observe( -# self.on.kafka_relation_broken, self._on_kafka_relation_broken -# ) -# self.framework.observe( -# self.on.mongodb_relation_broken, self._on_mongodb_relation_broken -# ) -# self.framework.observe( -# self.on.ro_relation_broken, self._on_ro_relation_broken -# ) - -# def _on_kafka_relation_changed(self, event: EventBase) -> NoReturn: -# """Reads information about the kafka relation. - -# Args: -# event (EventBase): Kafka relation event. -# """ -# message_host = event.relation.data[event.unit].get("host") -# message_port = event.relation.data[event.unit].get("port") - -# if ( -# message_host -# and message_port -# and ( -# self.state.message_host != message_host -# or self.state.message_port != message_port -# ) -# ): -# self.state.message_host = message_host -# self.state.message_port = message_port -# self.on.configure_pod.emit() - -# def _on_kafka_relation_broken(self, event: EventBase) -> NoReturn: -# """Clears data from kafka relation. - -# Args: -# event (EventBase): Kafka relation event. -# """ -# self.state.message_host = None -# self.state.message_port = None -# self.on.configure_pod.emit() - -# def _on_mongodb_relation_changed(self, event: EventBase) -> NoReturn: -# """Reads information about the DB relation. - -# Args: -# event (EventBase): DB relation event. -# """ -# database_uri = event.relation.data[event.unit].get("connection_string") - -# if database_uri and self.state.database_uri != database_uri: -# self.state.database_uri = database_uri -# self.on.configure_pod.emit() - -# def _on_mongodb_relation_broken(self, event: EventBase) -> NoReturn: -# """Clears data from mongodb relation. - -# Args: -# event (EventBase): DB relation event. -# """ -# self.state.database_uri = None -# self.on.configure_pod.emit() - -# def _on_ro_relation_changed(self, event: EventBase) -> NoReturn: -# """Reads information about the RO relation. - -# Args: -# event (EventBase): Keystone relation event. -# """ -# ro_host = event.relation.data[event.unit].get("host") -# ro_port = event.relation.data[event.unit].get("port") - -# if ( -# ro_host -# and ro_port -# and (self.state.ro_host != ro_host or self.state.ro_port != ro_port) -# ): -# self.state.ro_host = ro_host -# self.state.ro_port = ro_port -# self.on.configure_pod.emit() - -# def _on_ro_relation_broken(self, event: EventBase) -> NoReturn: -# """Clears data from ro relation. - -# Args: -# event (EventBase): Keystone relation event. -# """ -# self.state.ro_host = None -# self.state.ro_port = None -# self.on.configure_pod.emit() - -# def _missing_relations(self) -> str: -# """Checks if there missing relations. - -# Returns: -# str: string with missing relations -# """ -# data_status = { -# "kafka": self.state.message_host, -# "mongodb": self.state.database_uri, -# "ro": self.state.ro_host, -# } - -# missing_relations = [k for k, v in data_status.items() if not v] - -# return ", ".join(missing_relations) - -# @property -# def relation_state(self) -> Dict[str, Any]: -# """Collects relation state configuration for pod spec assembly. - -# Returns: -# Dict[str, Any]: relation state information. -# """ -# relation_state = { -# "message_host": self.state.message_host, -# "message_port": self.state.message_port, -# "database_uri": self.state.database_uri, -# "ro_host": self.state.ro_host, -# "ro_port": self.state.ro_port, -# } - -# return relation_state - -# def configure_pod(self, event: EventBase) -> NoReturn: -# """Assemble the pod spec and apply it, if possible. - -# Args: -# event (EventBase): Hook or Relation event that started the -# function. -# """ -# if missing := self._missing_relations(): -# self.unit.status = BlockedStatus( -# "Waiting for {0} relation{1}".format( -# missing, "s" if "," in missing else "" -# ) -# ) -# return - -# if not self.unit.is_leader(): -# self.unit.status = ActiveStatus("ready") -# return - -# self.unit.status = MaintenanceStatus("Assembling pod spec") - -# # Fetch image information -# try: -# self.unit.status = MaintenanceStatus("Fetching image information") -# image_info = self.image.fetch() -# except OCIImageResourceError: -# self.unit.status = BlockedStatus("Error fetching image information") -# return - -# try: -# pod_spec = make_pod_spec( -# image_info, -# self.model.config, -# self.relation_state, -# self.model.app.name, -# self.port, -# ) -# except ValueError as exc: -# logger.exception("Config/Relation data validation error") -# self.unit.status = BlockedStatus(str(exc)) -# return - -# if self.state.pod_spec != pod_spec: -# self.model.pod.set_spec(pod_spec) -# self.state.pod_spec = pod_spec - -# self.unit.status = ActiveStatus("ready") - - -# if __name__ == "__main__": -# main(LcmCharm) diff --git a/installers/charm/lcm/src/pod_spec.py b/installers/charm/lcm/src/pod_spec.py deleted file mode 100644 index 8709f4fa..00000000 --- a/installers/charm/lcm/src/pod_spec.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import logging -from typing import Any, Dict, List, NoReturn - -logger = logging.getLogger(__name__) - - -def _validate_data( - config_data: Dict[str, Any], relation_data: Dict[str, Any] -) -> NoReturn: - """Validate input data. - - Args: - config_data (Dict[str, Any]): configuration data. - relation_data (Dict[str, Any]): relation data. - """ - config_validators = { - "database_commonkey": lambda value, _: ( - isinstance(value, str) and len(value) > 1 - ), - "log_level": lambda value, _: ( - isinstance(value, str) and value in ("INFO", "DEBUG") - ), - "vca_host": lambda value, _: isinstance(value, str) and len(value) > 1, - "vca_port": lambda value, _: isinstance(value, int) and value > 0, - "vca_user": lambda value, _: isinstance(value, str) and len(value) > 1, - "vca_pubkey": lambda value, _: isinstance(value, str) and len(value) > 1, - "vca_password": lambda value, _: isinstance(value, str) and len(value) > 1, - "vca_cacert": lambda value, _: isinstance(value, str), - "vca_cloud": lambda value, _: isinstance(value, str) and len(value) > 1, - "vca_k8s_cloud": lambda value, _: isinstance(value, str) and len(value) > 1, - "vca_apiproxy": lambda value, _: (isinstance(value, str) and len(value) > 1) - if value - else True, - } - relation_validators = { - "ro_host": lambda value, _: isinstance(value, str) and len(value) > 1, - "ro_port": lambda value, _: isinstance(value, int) and value > 0, - "message_host": lambda value, _: isinstance(value, str) and len(value) > 1, - "message_port": lambda value, _: isinstance(value, int) and value > 0, - "database_uri": lambda value, _: isinstance(value, str) and len(value) > 1, - } - problems = [] - - for key, validator in config_validators.items(): - valid = validator(config_data.get(key), config_data) - - if not valid: - problems.append(key) - - for key, validator in relation_validators.items(): - valid = validator(relation_data.get(key), relation_data) - - if not valid: - problems.append(key) - - if len(problems) > 0: - raise ValueError("Errors found in: {}".format(", ".join(problems))) - - -def _make_pod_ports(port: int) -> List[Dict[str, Any]]: - """Generate pod ports details. - - Args: - port (int): port to expose. - - Returns: - List[Dict[str, Any]]: pod port details. - """ - return [{"name": "lcm", "containerPort": port, "protocol": "TCP"}] - - -def _make_pod_envconfig( - config: Dict[str, Any], relation_state: Dict[str, Any] -) -> Dict[str, Any]: - """Generate pod environment configuration. - - Args: - config (Dict[str, Any]): configuration information. - relation_state (Dict[str, Any]): relation state information. - - Returns: - Dict[str, Any]: pod environment configuration. - """ - envconfig = { - # General configuration - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMLCM_GLOBAL_LOGLEVEL": config["log_level"], - # RO configuration - "OSMLCM_RO_HOST": relation_state["ro_host"], - "OSMLCM_RO_PORT": relation_state["ro_port"], - "OSMLCM_RO_TENANT": "osm", - # Kafka configuration - "OSMLCM_MESSAGE_DRIVER": "kafka", - "OSMLCM_MESSAGE_HOST": relation_state["message_host"], - "OSMLCM_MESSAGE_PORT": relation_state["message_port"], - # Database configuration - "OSMLCM_DATABASE_DRIVER": "mongo", - "OSMLCM_DATABASE_URI": relation_state["database_uri"], - "OSMLCM_DATABASE_COMMONKEY": config["database_commonkey"], - # Storage configuration - "OSMLCM_STORAGE_DRIVER": "mongo", - "OSMLCM_STORAGE_PATH": "/app/storage", - "OSMLCM_STORAGE_COLLECTION": "files", - "OSMLCM_STORAGE_URI": relation_state["database_uri"], - # VCA configuration - "OSMLCM_VCA_HOST": config["vca_host"], - "OSMLCM_VCA_PORT": config["vca_port"], - "OSMLCM_VCA_USER": config["vca_user"], - "OSMLCM_VCA_PUBKEY": config["vca_pubkey"], - "OSMLCM_VCA_SECRET": config["vca_password"], - "OSMLCM_VCA_CACERT": config["vca_cacert"], - "OSMLCM_VCA_CLOUD": config["vca_cloud"], - "OSMLCM_VCA_K8S_CLOUD": config["vca_k8s_cloud"], - } - - if "vca_apiproxy" in config and config["vca_apiproxy"]: - envconfig["OSMLCM_VCA_APIPROXY"] = config["vca_apiproxy"] - - return envconfig - - -def _make_startup_probe() -> Dict[str, Any]: - """Generate startup probe. - - Returns: - Dict[str, Any]: startup probe. - """ - return { - "exec": {"command": ["/usr/bin/pgrep python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - -def _make_readiness_probe(port: int) -> Dict[str, Any]: - """Generate readiness probe. - - Args: - port (int): [description] - - Returns: - Dict[str, Any]: readiness probe. - """ - return { - "httpGet": { - "path": "/osm/", - "port": port, - }, - "initialDelaySeconds": 45, - "timeoutSeconds": 5, - } - - -def _make_liveness_probe(port: int) -> Dict[str, Any]: - """Generate liveness probe. - - Args: - port (int): [description] - - Returns: - Dict[str, Any]: liveness probe. - """ - return { - "httpGet": { - "path": "/osm/", - "port": port, - }, - "initialDelaySeconds": 45, - "timeoutSeconds": 5, - } - - -def make_pod_spec( - image_info: Dict[str, str], - config: Dict[str, Any], - relation_state: Dict[str, Any], - app_name: str = "lcm", - port: int = 9999, -) -> Dict[str, Any]: - """Generate the pod spec information. - - Args: - image_info (Dict[str, str]): Object provided by - OCIImageResource("image").fetch(). - config (Dict[str, Any]): Configuration information. - relation_state (Dict[str, Any]): Relation state information. - app_name (str, optional): Application name. Defaults to "lcm". - port (int, optional): Port for the container. Defaults to 9999. - - Returns: - Dict[str, Any]: Pod spec dictionary for the charm. - """ - if not image_info: - return None - - _validate_data(config, relation_state) - - ports = _make_pod_ports(port) - env_config = _make_pod_envconfig(config, relation_state) - - return { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": ports, - "envConfig": env_config, - } - ], - "kubernetesResources": { - "ingressResources": [], - }, - } diff --git a/installers/charm/lcm/tests/__init__.py b/installers/charm/lcm/tests/__init__.py deleted file mode 100644 index 446d5cee..00000000 --- a/installers/charm/lcm/tests/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -"""Init mocking for unit tests.""" - -import sys - - -import mock - - -class OCIImageResourceErrorMock(Exception): - pass - - -sys.path.append("src") - -oci_image = mock.MagicMock() -oci_image.OCIImageResourceError = OCIImageResourceErrorMock -sys.modules["oci_image"] = oci_image -sys.modules["oci_image"].OCIImageResource().fetch.return_value = {} diff --git a/installers/charm/lcm/tests/test_charm.py b/installers/charm/lcm/tests/test_charm.py deleted file mode 100644 index aa11a747..00000000 --- a/installers/charm/lcm/tests/test_charm.py +++ /dev/null @@ -1,462 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import sys -from typing import NoReturn -import unittest - -from charm import LcmCharm -import mock -from ops.model import ActiveStatus, BlockedStatus -from ops.testing import Harness - - -class TestCharm(unittest.TestCase): - """LCM Charm unit tests.""" - - def setUp(self) -> NoReturn: - """Test setup""" - self.image_info = sys.modules["oci_image"].OCIImageResource().fetch() - self.harness = Harness(LcmCharm) - self.harness.set_leader(is_leader=True) - self.harness.begin() - self.config = { - "vca_host": "192.168.0.13", - "vca_port": 17070, - "vca_user": "admin", - "vca_secret": "admin", - "vca_pubkey": "key", - "vca_cacert": "cacert", - "vca_cloud": "cloud", - "vca_k8s_cloud": "k8scloud", - "database_commonkey": "commonkey", - "mongodb_uri": "", - "log_level": "INFO", - } - self.harness.update_config(self.config) - - def test_config_changed_no_relations( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue( - all( - relation in self.harness.charm.unit.status.message - for relation in ["mongodb", "kafka", "ro"] - ) - ) - - def test_config_changed_non_leader( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - self.harness.set_leader(is_leader=False) - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus) - - def test_with_relations_and_mongodb_config( - self, - ) -> NoReturn: - "Test with relations and mongodb config" - self.initialize_kafka_relation() - self.initialize_mongo_config() - self.initialize_ro_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_with_relations( - self, - ) -> NoReturn: - "Test with relations (internal)" - self.initialize_kafka_relation() - self.initialize_mongo_relation() - self.initialize_ro_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_exception_mongodb_relation_and_config( - self, - ) -> NoReturn: - "Test with all relations and config for mongodb. Must fail" - self.initialize_mongo_relation() - self.initialize_mongo_config() - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - - # def test_build_pod_spec( - # self, - # ) -> NoReturn: - # expected_config = { - # "OSMLCM_GLOBAL_LOGLEVEL": self.config["log_level"], - # "OSMLCM_DATABASE_COMMONKEY": self.config["database_commonkey"], - # } - # expected_config.update( - # { - # f"OSMLCM_{k.upper()}": v - # for k, v in self.config.items() - # if k.startswith("vca_") - # } - # ) - # self.harness.charm._check_missing_dependencies = mock.Mock() - # pod_spec = self.harness.charm.build_pod_spec( - # {"imageDetails": {"imagePath": "lcm-image"}} - # ) - # actual_config = pod_spec["containers"][0]["envConfig"] - - # self.assertDictContainsSubset( - # expected_config, - # actual_config, - # ) - # for config_key in actual_config: - # self.assertNotIn("VCA_MODEL_CONFIG", config_key) - - def test_build_pod_spec_with_model_config( - self, - ) -> NoReturn: - self.harness.update_config( - { - "vca_model_config_agent_metadata_url": "string", - "vca_model_config_agent_stream": "string", - "vca_model_config_apt_ftp_proxy": "string", - "vca_model_config_apt_http_proxy": "string", - "vca_model_config_apt_https_proxy": "string", - "vca_model_config_apt_mirror": "string", - "vca_model_config_apt_no_proxy": "string", - "vca_model_config_automatically_retry_hooks": False, - "vca_model_config_backup_dir": "string", - "vca_model_config_cloudinit_userdata": "string", - "vca_model_config_container_image_metadata_url": "string", - "vca_model_config_container_image_stream": "string", - "vca_model_config_container_inherit_properties": "string", - "vca_model_config_container_networking_method": "string", - "vca_model_config_default_series": "string", - "vca_model_config_default_space": "string", - "vca_model_config_development": False, - "vca_model_config_disable_network_management": False, - "vca_model_config_egress_subnets": "string", - "vca_model_config_enable_os_refresh_update": False, - "vca_model_config_enable_os_upgrade": False, - "vca_model_config_fan_config": "string", - "vca_model_config_firewall_mode": "string", - "vca_model_config_ftp_proxy": "string", - "vca_model_config_http_proxy": "string", - "vca_model_config_https_proxy": "string", - "vca_model_config_ignore_machine_addresses": False, - "vca_model_config_image_metadata_url": "string", - "vca_model_config_image_stream": "string", - "vca_model_config_juju_ftp_proxy": "string", - "vca_model_config_juju_http_proxy": "string", - "vca_model_config_juju_https_proxy": "string", - "vca_model_config_juju_no_proxy": "string", - "vca_model_config_logforward_enabled": False, - "vca_model_config_logging_config": "string", - "vca_model_config_lxd_snap_channel": "string", - "vca_model_config_max_action_results_age": "string", - "vca_model_config_max_action_results_size": "string", - "vca_model_config_max_status_history_age": "string", - "vca_model_config_max_status_history_size": "string", - "vca_model_config_net_bond_reconfigure_delay": "string", - "vca_model_config_no_proxy": "string", - "vca_model_config_provisioner_harvest_mode": "string", - "vca_model_config_proxy_ssh": False, - "vca_model_config_snap_http_proxy": "string", - "vca_model_config_snap_https_proxy": "string", - "vca_model_config_snap_store_assertions": "string", - "vca_model_config_snap_store_proxy": "string", - "vca_model_config_snap_store_proxy_url": "string", - "vca_model_config_ssl_hostname_verification": False, - "vca_model_config_test_mode": False, - "vca_model_config_transmit_vendor_metrics": False, - "vca_model_config_update_status_hook_interval": "string", - } - ) - expected_config = { - f"OSMLCM_{k.upper()}": v - for k, v in self.config.items() - if k.startswith("vca_model_config_") - } - - self.harness.charm._check_missing_dependencies = mock.Mock() - pod_spec = self.harness.charm.build_pod_spec( - {"imageDetails": {"imagePath": "lcm-image"}} - ) - actual_config = pod_spec["containers"][0]["envConfig"] - - self.assertDictContainsSubset( - expected_config, - actual_config, - ) - - def initialize_kafka_relation(self): - kafka_relation_id = self.harness.add_relation("kafka", "kafka") - self.harness.add_relation_unit(kafka_relation_id, "kafka/0") - self.harness.update_relation_data( - kafka_relation_id, "kafka", {"host": "kafka", "port": 9092} - ) - - def initialize_mongo_config(self): - self.harness.update_config({"mongodb_uri": "mongodb://mongo:27017"}) - - def initialize_mongo_relation(self): - mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") - self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") - self.harness.update_relation_data( - mongodb_relation_id, - "mongodb/0", - {"connection_string": "mongodb://mongo:27017"}, - ) - - def initialize_ro_relation(self): - http_relation_id = self.harness.add_relation("ro", "ro") - self.harness.add_relation_unit(http_relation_id, "ro") - self.harness.update_relation_data( - http_relation_id, - "ro", - {"host": "ro", "port": 9090}, - ) - - -if __name__ == "__main__": - unittest.main() - - -# class TestCharm(unittest.TestCase): -# """LCM Charm unit tests.""" - -# def setUp(self) -> NoReturn: -# """Test setup""" -# self.harness = Harness(LcmCharm) -# self.harness.set_leader(is_leader=True) -# self.harness.begin() - -# def test_on_start_without_relations(self) -> NoReturn: -# """Test installation without any relation.""" -# self.harness.charm.on.start.emit() - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertIn("ro", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - -# def test_on_start_with_relations(self) -> NoReturn: -# """Test deployment without keystone.""" -# expected_result = { -# "version": 3, -# "containers": [ -# { -# "name": "lcm", -# "imageDetails": self.harness.charm.image.fetch(), -# "imagePullPolicy": "Always", -# "ports": [ -# { -# "name": "lcm", -# "containerPort": 9999, -# "protocol": "TCP", -# } -# ], -# "envConfig": { -# "ALLOW_ANONYMOUS_LOGIN": "yes", -# "OSMLCM_GLOBAL_LOGLEVEL": "INFO", -# "OSMLCM_RO_HOST": "ro", -# "OSMLCM_RO_PORT": 9090, -# "OSMLCM_RO_TENANT": "osm", -# "OSMLCM_MESSAGE_DRIVER": "kafka", -# "OSMLCM_MESSAGE_HOST": "kafka", -# "OSMLCM_MESSAGE_PORT": 9092, -# "OSMLCM_DATABASE_DRIVER": "mongo", -# "OSMLCM_DATABASE_URI": "mongodb://mongo:27017", -# "OSMLCM_DATABASE_COMMONKEY": "osm", -# "OSMLCM_STORAGE_DRIVER": "mongo", -# "OSMLCM_STORAGE_PATH": "/app/storage", -# "OSMLCM_STORAGE_COLLECTION": "files", -# "OSMLCM_STORAGE_URI": "mongodb://mongo:27017", -# "OSMLCM_VCA_HOST": "admin", -# "OSMLCM_VCA_PORT": 17070, -# "OSMLCM_VCA_USER": "admin", -# "OSMLCM_VCA_PUBKEY": "secret", -# "OSMLCM_VCA_SECRET": "secret", -# "OSMLCM_VCA_CACERT": "", -# "OSMLCM_VCA_CLOUD": "localhost", -# "OSMLCM_VCA_K8S_CLOUD": "k8scloud", -# }, -# } -# ], -# "kubernetesResources": {"ingressResources": []}, -# } - -# self.harness.charm.on.start.emit() - -# # Check if kafka datastore is initialized -# self.assertIsNone(self.harness.charm.state.message_host) -# self.assertIsNone(self.harness.charm.state.message_port) - -# # Check if mongodb datastore is initialized -# self.assertIsNone(self.harness.charm.state.database_uri) - -# # Check if RO datastore is initialized -# self.assertIsNone(self.harness.charm.state.ro_host) -# self.assertIsNone(self.harness.charm.state.ro_port) - -# # Initializing the kafka relation -# kafka_relation_id = self.harness.add_relation("kafka", "kafka") -# self.harness.add_relation_unit(kafka_relation_id, "kafka/0") -# self.harness.update_relation_data( -# kafka_relation_id, "kafka/0", {"host": "kafka", "port": 9092} -# ) - -# # Initializing the mongo relation -# mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") -# self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") -# self.harness.update_relation_data( -# mongodb_relation_id, -# "mongodb/0", -# {"connection_string": "mongodb://mongo:27017"}, -# ) - -# # Initializing the RO relation -# ro_relation_id = self.harness.add_relation("ro", "ro") -# self.harness.add_relation_unit(ro_relation_id, "ro/0") -# self.harness.update_relation_data( -# ro_relation_id, "ro/0", {"host": "ro", "port": 9090} -# ) - -# # Checking if kafka data is stored -# self.assertEqual(self.harness.charm.state.message_host, "kafka") -# self.assertEqual(self.harness.charm.state.message_port, 9092) - -# # Checking if mongodb data is stored -# self.assertEqual(self.harness.charm.state.database_uri, "mongodb://mongo:27017") - -# # Checking if RO data is stored -# self.assertEqual(self.harness.charm.state.ro_host, "ro") -# self.assertEqual(self.harness.charm.state.ro_port, 9090) - -# # Verifying status -# self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# pod_spec, _ = self.harness.get_pod_spec() - -# self.assertDictEqual(expected_result, pod_spec) - -# def test_on_kafka_relation_unit_changed(self) -> NoReturn: -# """Test to see if kafka relation is updated.""" -# self.harness.charm.on.start.emit() - -# self.assertIsNone(self.harness.charm.state.message_host) -# self.assertIsNone(self.harness.charm.state.message_port) - -# relation_id = self.harness.add_relation("kafka", "kafka") -# self.harness.add_relation_unit(relation_id, "kafka/0") -# self.harness.update_relation_data( -# relation_id, "kafka/0", {"host": "kafka", "port": 9092} -# ) - -# self.assertEqual(self.harness.charm.state.message_host, "kafka") -# self.assertEqual(self.harness.charm.state.message_port, 9092) - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertNotIn("kafka", self.harness.charm.unit.status.message) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertIn("ro", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - -# def test_on_mongodb_unit_relation_changed(self) -> NoReturn: -# """Test to see if mongodb relation is updated.""" -# self.harness.charm.on.start.emit() - -# self.assertIsNone(self.harness.charm.state.database_uri) - -# relation_id = self.harness.add_relation("mongodb", "mongodb") -# self.harness.add_relation_unit(relation_id, "mongodb/0") -# self.harness.update_relation_data( -# relation_id, "mongodb/0", {"connection_string": "mongodb://mongo:27017"} -# ) - -# self.assertEqual(self.harness.charm.state.database_uri, "mongodb://mongo:27017") - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertNotIn("mongodb", self.harness.charm.unit.status.message) -# self.assertIn("ro", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - -# def test_on_ro_unit_relation_changed(self) -> NoReturn: -# """Test to see if RO relation is updated.""" -# self.harness.charm.on.start.emit() - -# self.assertIsNone(self.harness.charm.state.ro_host) -# self.assertIsNone(self.harness.charm.state.ro_port) - -# relation_id = self.harness.add_relation("ro", "ro") -# self.harness.add_relation_unit(relation_id, "ro/0") -# self.harness.update_relation_data( -# relation_id, "ro/0", {"host": "ro", "port": 9090} -# ) - -# self.assertEqual(self.harness.charm.state.ro_host, "ro") -# self.assertEqual(self.harness.charm.state.ro_port, 9090) - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertNotIn("ro", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - - -# if __name__ == "__main__": -# unittest.main() diff --git a/installers/charm/lcm/tests/test_pod_spec.py b/installers/charm/lcm/tests/test_pod_spec.py deleted file mode 100644 index c74fb102..00000000 --- a/installers/charm/lcm/tests/test_pod_spec.py +++ /dev/null @@ -1,426 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -from typing import NoReturn -import unittest - -import pod_spec - - -class TestPodSpec(unittest.TestCase): - """Pod spec unit tests.""" - - def test_make_pod_ports(self) -> NoReturn: - """Testing make pod ports.""" - port = 9999 - - expected_result = [ - { - "name": "lcm", - "containerPort": port, - "protocol": "TCP", - } - ] - - pod_ports = pod_spec._make_pod_ports(9999) - - self.assertListEqual(expected_result, pod_ports) - - def test_make_pod_envconfig_without_vca_apiproxy(self) -> NoReturn: - """Teting make pod envconfig without vca_apiproxy configuration.""" - config = { - "database_commonkey": "commonkey", - "log_level": "INFO", - "vca_host": "vca", - "vca_port": 1212, - "vca_user": "vca_user", - "vca_pubkey": "vca_pubkey", - "vca_password": "vca_password", - "vca_cacert": "vca_cacert", - "vca_cloud": "vca_cloud", - "vca_k8s_cloud": "vca_k8s_cloud", - } - relation_state = { - "message_host": "kafka", - "message_port": 2181, - "database_uri": "mongodb://mongo", - "ro_host": "ro", - "ro_port": 9090, - } - - expected_result = { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMLCM_GLOBAL_LOGLEVEL": config["log_level"], - "OSMLCM_RO_HOST": relation_state["ro_host"], - "OSMLCM_RO_PORT": relation_state["ro_port"], - "OSMLCM_RO_TENANT": "osm", - "OSMLCM_MESSAGE_DRIVER": "kafka", - "OSMLCM_MESSAGE_HOST": relation_state["message_host"], - "OSMLCM_MESSAGE_PORT": relation_state["message_port"], - "OSMLCM_DATABASE_DRIVER": "mongo", - "OSMLCM_DATABASE_URI": relation_state["database_uri"], - "OSMLCM_DATABASE_COMMONKEY": config["database_commonkey"], - "OSMLCM_STORAGE_DRIVER": "mongo", - "OSMLCM_STORAGE_PATH": "/app/storage", - "OSMLCM_STORAGE_COLLECTION": "files", - "OSMLCM_STORAGE_URI": relation_state["database_uri"], - "OSMLCM_VCA_HOST": config["vca_host"], - "OSMLCM_VCA_PORT": config["vca_port"], - "OSMLCM_VCA_USER": config["vca_user"], - "OSMLCM_VCA_PUBKEY": config["vca_pubkey"], - "OSMLCM_VCA_SECRET": config["vca_password"], - "OSMLCM_VCA_CACERT": config["vca_cacert"], - "OSMLCM_VCA_CLOUD": config["vca_cloud"], - "OSMLCM_VCA_K8S_CLOUD": config["vca_k8s_cloud"], - } - - pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state) - - self.assertDictEqual(expected_result, pod_envconfig) - - def test_make_pod_envconfig_with_vca_apiproxy(self) -> NoReturn: - """Teting make pod envconfig with vca_apiproxy configuration.""" - config = { - "database_commonkey": "commonkey", - "log_level": "INFO", - "vca_host": "vca", - "vca_port": 1212, - "vca_user": "vca_user", - "vca_pubkey": "vca_pubkey", - "vca_password": "vca_password", - "vca_cacert": "vca_cacert", - "vca_cloud": "vca_cloud", - "vca_k8s_cloud": "vca_k8s_cloud", - "vca_apiproxy": "vca_apiproxy", - } - relation_state = { - "message_host": "kafka", - "message_port": 2181, - "database_uri": "mongodb://mongo", - "ro_host": "ro", - "ro_port": 9090, - } - - expected_result = { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMLCM_GLOBAL_LOGLEVEL": config["log_level"], - "OSMLCM_RO_HOST": relation_state["ro_host"], - "OSMLCM_RO_PORT": relation_state["ro_port"], - "OSMLCM_RO_TENANT": "osm", - "OSMLCM_MESSAGE_DRIVER": "kafka", - "OSMLCM_MESSAGE_HOST": relation_state["message_host"], - "OSMLCM_MESSAGE_PORT": relation_state["message_port"], - "OSMLCM_DATABASE_DRIVER": "mongo", - "OSMLCM_DATABASE_URI": relation_state["database_uri"], - "OSMLCM_DATABASE_COMMONKEY": config["database_commonkey"], - "OSMLCM_STORAGE_DRIVER": "mongo", - "OSMLCM_STORAGE_PATH": "/app/storage", - "OSMLCM_STORAGE_COLLECTION": "files", - "OSMLCM_STORAGE_URI": relation_state["database_uri"], - "OSMLCM_VCA_HOST": config["vca_host"], - "OSMLCM_VCA_PORT": config["vca_port"], - "OSMLCM_VCA_USER": config["vca_user"], - "OSMLCM_VCA_PUBKEY": config["vca_pubkey"], - "OSMLCM_VCA_SECRET": config["vca_password"], - "OSMLCM_VCA_CACERT": config["vca_cacert"], - "OSMLCM_VCA_CLOUD": config["vca_cloud"], - "OSMLCM_VCA_K8S_CLOUD": config["vca_k8s_cloud"], - "OSMLCM_VCA_APIPROXY": config["vca_apiproxy"], - } - - pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state) - - self.assertDictEqual(expected_result, pod_envconfig) - - def test_make_startup_probe(self) -> NoReturn: - """Testing make startup probe.""" - expected_result = { - "exec": {"command": ["/usr/bin/pgrep python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - startup_probe = pod_spec._make_startup_probe() - - self.assertDictEqual(expected_result, startup_probe) - - def test_make_readiness_probe(self) -> NoReturn: - """Testing make readiness probe.""" - port = 9999 - - expected_result = { - "httpGet": { - "path": "/osm/", - "port": port, - }, - "initialDelaySeconds": 45, - "timeoutSeconds": 5, - } - - readiness_probe = pod_spec._make_readiness_probe(port) - - self.assertDictEqual(expected_result, readiness_probe) - - def test_make_liveness_probe(self) -> NoReturn: - """Testing make liveness probe.""" - port = 9999 - - expected_result = { - "httpGet": { - "path": "/osm/", - "port": port, - }, - "initialDelaySeconds": 45, - "timeoutSeconds": 5, - } - - liveness_probe = pod_spec._make_liveness_probe(port) - - self.assertDictEqual(expected_result, liveness_probe) - - def test_make_pod_spec(self) -> NoReturn: - """Testing make pod spec.""" - image_info = {"upstream-source": "opensourcemano/lcm:8"} - config = { - "database_commonkey": "commonkey", - "log_level": "INFO", - "vca_host": "vca", - "vca_port": 1212, - "vca_user": "vca_user", - "vca_pubkey": "vca_pubkey", - "vca_password": "vca_password", - "vca_cacert": "vca_cacert", - "vca_cloud": "vca_cloud", - "vca_k8s_cloud": "vca_k8s_cloud", - "vca_apiproxy": "vca_apiproxy", - } - relation_state = { - "message_host": "kafka", - "message_port": 2181, - "database_uri": "mongodb://mongo", - "ro_host": "ro", - "ro_port": 9090, - } - app_name = "lcm" - port = 9999 - - expected_result = { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": [ - { - "name": app_name, - "containerPort": port, - "protocol": "TCP", - } - ], - "envConfig": { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMLCM_GLOBAL_LOGLEVEL": config["log_level"], - "OSMLCM_RO_HOST": relation_state["ro_host"], - "OSMLCM_RO_PORT": relation_state["ro_port"], - "OSMLCM_RO_TENANT": "osm", - "OSMLCM_MESSAGE_DRIVER": "kafka", - "OSMLCM_MESSAGE_HOST": relation_state["message_host"], - "OSMLCM_MESSAGE_PORT": relation_state["message_port"], - "OSMLCM_DATABASE_DRIVER": "mongo", - "OSMLCM_DATABASE_URI": relation_state["database_uri"], - "OSMLCM_DATABASE_COMMONKEY": config["database_commonkey"], - "OSMLCM_STORAGE_DRIVER": "mongo", - "OSMLCM_STORAGE_PATH": "/app/storage", - "OSMLCM_STORAGE_COLLECTION": "files", - "OSMLCM_STORAGE_URI": relation_state["database_uri"], - "OSMLCM_VCA_HOST": config["vca_host"], - "OSMLCM_VCA_PORT": config["vca_port"], - "OSMLCM_VCA_USER": config["vca_user"], - "OSMLCM_VCA_PUBKEY": config["vca_pubkey"], - "OSMLCM_VCA_SECRET": config["vca_password"], - "OSMLCM_VCA_CACERT": config["vca_cacert"], - "OSMLCM_VCA_CLOUD": config["vca_cloud"], - "OSMLCM_VCA_K8S_CLOUD": config["vca_k8s_cloud"], - "OSMLCM_VCA_APIPROXY": config["vca_apiproxy"], - }, - } - ], - "kubernetesResources": {"ingressResources": []}, - } - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertDictEqual(expected_result, spec) - - def test_make_pod_spec_with_vca_apiproxy(self) -> NoReturn: - """Testing make pod spec with vca_apiproxy.""" - image_info = {"upstream-source": "opensourcemano/lcm:8"} - config = { - "database_commonkey": "commonkey", - "log_level": "INFO", - "vca_host": "vca", - "vca_port": 1212, - "vca_user": "vca_user", - "vca_pubkey": "vca_pubkey", - "vca_password": "vca_password", - "vca_cacert": "vca_cacert", - "vca_cloud": "vca_cloud", - "vca_k8s_cloud": "vca_k8s_cloud", - } - relation_state = { - "message_host": "kafka", - "message_port": 2181, - "database_uri": "mongodb://mongo", - "ro_host": "ro", - "ro_port": 9090, - } - app_name = "lcm" - port = 9999 - - expected_result = { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": [ - { - "name": app_name, - "containerPort": port, - "protocol": "TCP", - } - ], - "envConfig": { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMLCM_GLOBAL_LOGLEVEL": config["log_level"], - "OSMLCM_RO_HOST": relation_state["ro_host"], - "OSMLCM_RO_PORT": relation_state["ro_port"], - "OSMLCM_RO_TENANT": "osm", - "OSMLCM_MESSAGE_DRIVER": "kafka", - "OSMLCM_MESSAGE_HOST": relation_state["message_host"], - "OSMLCM_MESSAGE_PORT": relation_state["message_port"], - "OSMLCM_DATABASE_DRIVER": "mongo", - "OSMLCM_DATABASE_URI": relation_state["database_uri"], - "OSMLCM_DATABASE_COMMONKEY": config["database_commonkey"], - "OSMLCM_STORAGE_DRIVER": "mongo", - "OSMLCM_STORAGE_PATH": "/app/storage", - "OSMLCM_STORAGE_COLLECTION": "files", - "OSMLCM_STORAGE_URI": relation_state["database_uri"], - "OSMLCM_VCA_HOST": config["vca_host"], - "OSMLCM_VCA_PORT": config["vca_port"], - "OSMLCM_VCA_USER": config["vca_user"], - "OSMLCM_VCA_PUBKEY": config["vca_pubkey"], - "OSMLCM_VCA_SECRET": config["vca_password"], - "OSMLCM_VCA_CACERT": config["vca_cacert"], - "OSMLCM_VCA_CLOUD": config["vca_cloud"], - "OSMLCM_VCA_K8S_CLOUD": config["vca_k8s_cloud"], - }, - } - ], - "kubernetesResources": {"ingressResources": []}, - } - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertDictEqual(expected_result, spec) - - def test_make_pod_spec_without_image_info(self) -> NoReturn: - """Testing make pod spec without image_info.""" - image_info = None - config = { - "database_commonkey": "commonkey", - "log_level": "INFO", - "vca_host": "vca", - "vca_port": 1212, - "vca_user": "vca_user", - "vca_pubkey": "vca_pubkey", - "vca_password": "vca_password", - "vca_cacert": "vca_cacert", - "vca_cloud": "vca_cloud", - "vca_k8s_cloud": "vca_k8s_cloud", - "vca_apiproxy": "vca_apiproxy", - } - relation_state = { - "message_host": "kafka", - "message_port": 2181, - "database_uri": "mongodb://mongo", - "ro_host": "ro", - "ro_port": 9090, - } - app_name = "lcm" - port = 9999 - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertIsNone(spec) - - def test_make_pod_spec_without_config(self) -> NoReturn: - """Testing make pod spec without config.""" - image_info = {"upstream-source": "opensourcemano/lcm:8"} - config = {} - relation_state = { - "message_host": "kafka", - "message_port": 2181, - "database_uri": "mongodb://mongo", - "ro_host": "ro", - "ro_port": 9090, - } - app_name = "lcm" - port = 9999 - - with self.assertRaises(ValueError): - pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port) - - def test_make_pod_spec_without_relation_state(self) -> NoReturn: - """Testing make pod spec without relation_state.""" - image_info = {"upstream-source": "opensourcemano/lcm:8"} - config = { - "database_commonkey": "commonkey", - "log_level": "INFO", - "vca_host": "vca", - "vca_port": 1212, - "vca_user": "vca_user", - "vca_pubkey": "vca_pubkey", - "vca_password": "vca_password", - "vca_cacert": "vca_cacert", - "vca_cloud": "vca_cloud", - "vca_k8s_cloud": "vca_k8s_cloud", - "vca_apiproxy": "vca_apiproxy", - } - relation_state = {} - app_name = "lcm" - port = 9999 - - with self.assertRaises(ValueError): - pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port) - - -if __name__ == "__main__": - unittest.main() diff --git a/installers/charm/lcm/tox.ini b/installers/charm/lcm/tox.ini deleted file mode 100644 index f3c91440..00000000 --- a/installers/charm/lcm/tox.ini +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## -####################################################################################### - -[tox] -envlist = black, cover, flake8, pylint, yamllint, safety -skipsdist = true - -[tox:jenkins] -toxworkdir = /tmp/.tox - -[testenv] -basepython = python3.8 -setenv = - VIRTUAL_ENV={envdir} - PYTHONPATH = {toxinidir}:{toxinidir}/lib:{toxinidir}/src - PYTHONDONTWRITEBYTECODE = 1 -deps = -r{toxinidir}/requirements.txt - - -####################################################################################### -[testenv:black] -deps = black -commands = - black --check --diff src/ tests/ - - -####################################################################################### -[testenv:cover] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - coverage - nose2 -commands = - sh -c 'rm -f nosetests.xml' - coverage erase - nose2 -C --coverage src - coverage report --omit='*tests*' - coverage html -d ./cover --omit='*tests*' - coverage xml -o coverage.xml --omit=*tests* -whitelist_externals = sh - - -####################################################################################### -[testenv:flake8] -deps = flake8 - flake8-import-order -commands = - flake8 src/ tests/ - - -####################################################################################### -[testenv:pylint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - pylint==2.10.2 -commands = - pylint -E src/ tests/ - - -####################################################################################### -[testenv:safety] -setenv = - LC_ALL=C.UTF-8 - LANG=C.UTF-8 -deps = {[testenv]deps} - safety -commands = - - safety check --full-report - - -####################################################################################### -[testenv:yamllint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - yamllint -commands = yamllint . - -####################################################################################### -[testenv:build] -passenv=HTTP_PROXY HTTPS_PROXY NO_PROXY -whitelist_externals = - charmcraft - sh -commands = - charmcraft pack - sh -c 'ubuntu_version=20.04; \ - architectures="amd64-aarch64-arm64"; \ - charm_name=`cat metadata.yaml | grep -E "^name: " | cut -f 2 -d " "`; \ - mv $charm_name"_ubuntu-"$ubuntu_version-$architectures.charm $charm_name.charm' - -####################################################################################### -[flake8] -ignore = - W291, - W293, - W503, - E123, - E125, - E226, - E241, -exclude = - .git, - __pycache__, - .tox, -max-line-length = 120 -show-source = True -builtins = _ -max-complexity = 10 -import-order-style = google diff --git a/installers/charm/lint.sh b/installers/charm/lint.sh deleted file mode 100755 index 3c42dd1b..00000000 --- a/installers/charm/lint.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -# 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. - -set -eux - -function lint() { - cd $1 - tox -e lint - cd .. -} - -lint 'lcm-k8s' -lint 'mon-k8s' -lint 'nbi-k8s' -lint 'pol-k8s' -lint 'ro-k8s' -lint 'ui-k8s' -lint 'keystone' -lint 'ng-ui' -lint 'pla' \ No newline at end of file diff --git a/installers/charm/mon/.gitignore b/installers/charm/mon/.gitignore deleted file mode 100644 index 2885df27..00000000 --- a/installers/charm/mon/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.stestr -cover -release \ No newline at end of file diff --git a/installers/charm/mon/.jujuignore b/installers/charm/mon/.jujuignore deleted file mode 100644 index 3ae3e7dc..00000000 --- a/installers/charm/mon/.jujuignore +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.gitignore -.stestr -cover -release -tests/ -requirements* -tox.ini diff --git a/installers/charm/mon/.yamllint.yaml b/installers/charm/mon/.yamllint.yaml deleted file mode 100644 index d71fb69f..00000000 --- a/installers/charm/mon/.yamllint.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - ---- -extends: default - -yaml-files: - - "*.yaml" - - "*.yml" - - ".yamllint" -ignore: | - .tox - cover/ - build/ - venv - release/ diff --git a/installers/charm/mon/README.md b/installers/charm/mon/README.md deleted file mode 100644 index 216a7846..00000000 --- a/installers/charm/mon/README.md +++ /dev/null @@ -1,23 +0,0 @@ - - -# MON operator Charm for Kubernetes - -## Requirements diff --git a/installers/charm/mon/charmcraft.yaml b/installers/charm/mon/charmcraft.yaml deleted file mode 100644 index 0a285a9d..00000000 --- a/installers/charm/mon/charmcraft.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -type: charm -bases: - - build-on: - - name: ubuntu - channel: "20.04" - architectures: ["amd64"] - run-on: - - name: ubuntu - channel: "20.04" - architectures: - - amd64 - - aarch64 - - arm64 -parts: - charm: - build-packages: [git] diff --git a/installers/charm/mon/config.yaml b/installers/charm/mon/config.yaml deleted file mode 100644 index 04f52c0e..00000000 --- a/installers/charm/mon/config.yaml +++ /dev/null @@ -1,135 +0,0 @@ -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -options: - openstack_default_granularity: - description: Openstack default granularity - type: int - default: 300 - global_request_timeout: - description: Global request timeout - type: int - default: 10 - log_level: - description: Log level - type: string - default: INFO - database_commonkey: - description: Database common key - type: string - default: osm - mongodb_uri: - type: string - description: MongoDB URI (external database) - collector_interval: - description: Collector interval - type: int - default: 30 - evaluator_interval: - description: Evaluator interval - type: int - default: 30 - vca_host: - type: string - description: "The VCA host." - default: "admin" - vca_user: - type: string - description: "The VCA user name." - default: "admin" - vca_secret: - type: string - description: "The VCA user password." - default: "secret" - vca_cacert: - type: string - description: "The VCA cacert." - default: "" - grafana_url: - description: Grafana URL - type: string - default: http://grafana:3000 - grafana_user: - description: Grafana user - type: string - default: admin - grafana_password: - description: Grafana password - type: string - default: admin - keystone_enabled: - description: MON will use Keystone backend - type: boolean - default: false - certificates: - type: string - description: | - comma-separated list of : certificates. - Where: - name: name of the file for the certificate - content: base64 content of the certificate - The path for the files is /certs. - image_pull_policy: - type: string - description: | - ImagePullPolicy configuration for the pod. - Possible values: always, ifnotpresent, never - default: always - debug_mode: - description: | - If true, debug mode is activated. It means that the service will not run, - and instead, the command for the container will be a `sleep infinity`. - Note: If enabled, security_context will be disabled. - type: boolean - default: false - debug_pubkey: - description: | - Public SSH key that will be injected to the application pod. - type: string - debug_mon_local_path: - description: | - Local full path to the MON project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - debug_n2vc_local_path: - description: | - Local full path to the N2VC project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - debug_common_local_path: - description: | - Local full path to the COMMON project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - security_context: - description: Enables the security context of the pods - type: boolean - default: false - vm_infra_metrics: - description: Enables querying the VIMs asking for the status of the VMs - type: boolean - default: true diff --git a/installers/charm/mon/lib/charms/kafka_k8s/v0/kafka.py b/installers/charm/mon/lib/charms/kafka_k8s/v0/kafka.py deleted file mode 100644 index 1baf9a88..00000000 --- a/installers/charm/mon/lib/charms/kafka_k8s/v0/kafka.py +++ /dev/null @@ -1,207 +0,0 @@ -# Copyright 2022 Canonical Ltd. -# See LICENSE file for licensing details. -# -# 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. - -"""Kafka library. - -This [library](https://juju.is/docs/sdk/libraries) implements both sides of the -`kafka` [interface](https://juju.is/docs/sdk/relations). - -The *provider* side of this interface is implemented by the -[kafka-k8s Charmed Operator](https://charmhub.io/kafka-k8s). - -Any Charmed Operator that *requires* Kafka for providing its -service should implement the *requirer* side of this interface. - -In a nutshell using this library to implement a Charmed Operator *requiring* -Kafka would look like - -``` -$ charmcraft fetch-lib charms.kafka_k8s.v0.kafka -``` - -`metadata.yaml`: - -``` -requires: - kafka: - interface: kafka - limit: 1 -``` - -`src/charm.py`: - -``` -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.charm import CharmBase - - -class MyCharm(CharmBase): - - on = KafkaEvents() - - def __init__(self, *args): - super().__init__(*args) - self.kafka = KafkaRequires(self) - self.framework.observe( - self.on.kafka_available, - self._on_kafka_available, - ) - self.framework.observe( - self.on.kafka_broken, - self._on_kafka_broken, - ) - - def _on_kafka_available(self, event): - # Get Kafka host and port - host: str = self.kafka.host - port: int = self.kafka.port - # host => "kafka-k8s" - # port => 9092 - - def _on_kafka_broken(self, event): - # Stop service - # ... - self.unit.status = BlockedStatus("need kafka relation") -``` - -You can file bugs -[here](https://github.com/charmed-osm/kafka-k8s-operator/issues)! -""" - -from typing import Optional - -from ops.charm import CharmBase, CharmEvents -from ops.framework import EventBase, EventSource, Object - -# The unique Charmhub library identifier, never change it -from ops.model import Relation - -LIBID = "eacc8c85082347c9aae740e0220b8376" - -# 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 = 3 - - -KAFKA_HOST_APP_KEY = "host" -KAFKA_PORT_APP_KEY = "port" - - -class _KafkaAvailableEvent(EventBase): - """Event emitted when Kafka is available.""" - - -class _KafkaBrokenEvent(EventBase): - """Event emitted when Kafka relation is broken.""" - - -class KafkaEvents(CharmEvents): - """Kafka events. - - This class defines the events that Kafka can emit. - - Events: - kafka_available (_KafkaAvailableEvent) - """ - - kafka_available = EventSource(_KafkaAvailableEvent) - kafka_broken = EventSource(_KafkaBrokenEvent) - - -class KafkaRequires(Object): - """Requires-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self.charm = charm - self._endpoint_name = endpoint_name - - # Observe relation events - event_observe_mapping = { - charm.on[self._endpoint_name].relation_changed: self._on_relation_changed, - charm.on[self._endpoint_name].relation_broken: self._on_relation_broken, - } - for event, observer in event_observe_mapping.items(): - self.framework.observe(event, observer) - - def _on_relation_changed(self, event) -> None: - if event.relation.app and all( - key in event.relation.data[event.relation.app] - for key in (KAFKA_HOST_APP_KEY, KAFKA_PORT_APP_KEY) - ): - self.charm.on.kafka_available.emit() - - def _on_relation_broken(self, _) -> None: - self.charm.on.kafka_broken.emit() - - @property - def host(self) -> str: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - relation.data[relation.app].get(KAFKA_HOST_APP_KEY) - if relation and relation.app - else None - ) - - @property - def port(self) -> int: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - int(relation.data[relation.app].get(KAFKA_PORT_APP_KEY)) - if relation and relation.app - else None - ) - - -class KafkaProvides(Object): - """Provides-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self._endpoint_name = endpoint_name - - def set_host_info(self, host: str, port: int, relation: Optional[Relation] = None) -> None: - """Set Kafka host and port. - - This function writes in the application data of the relation, therefore, - only the unit leader can call it. - - Args: - host (str): Kafka hostname or IP address. - port (int): Kafka port. - relation (Optional[Relation]): Relation to update. - If not specified, all relations will be updated. - - Raises: - Exception: if a non-leader unit calls this function. - """ - if not self.model.unit.is_leader(): - raise Exception("only the leader set host information.") - - if relation: - self._update_relation_data(host, port, relation) - return - - for relation in self.model.relations[self._endpoint_name]: - self._update_relation_data(host, port, relation) - - def _update_relation_data(self, host: str, port: int, relation: Relation) -> None: - """Update data in relation if needed.""" - relation.data[self.model.app][KAFKA_HOST_APP_KEY] = host - relation.data[self.model.app][KAFKA_PORT_APP_KEY] = str(port) diff --git a/installers/charm/mon/metadata.yaml b/installers/charm/mon/metadata.yaml deleted file mode 100644 index f3c3990d..00000000 --- a/installers/charm/mon/metadata.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -name: osm-mon -summary: OSM Monitoring (MON) -description: | - A CAAS charm to deploy OSM's Monitoring (MON). -series: - - kubernetes -tags: - - kubernetes - - osm - - mon -min-juju-version: 2.8.0 -deployment: - type: stateless - service: cluster -resources: - image: - type: oci-image - description: OSM docker image for MON - upstream-source: "opensourcemano/mon:latest" -requires: - kafka: - interface: kafka - mongodb: - interface: mongodb - prometheus: - interface: prometheus - keystone: - interface: keystone diff --git a/installers/charm/mon/requirements-test.txt b/installers/charm/mon/requirements-test.txt deleted file mode 100644 index cf61dd4e..00000000 --- a/installers/charm/mon/requirements-test.txt +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -mock==4.0.3 diff --git a/installers/charm/mon/requirements.txt b/installers/charm/mon/requirements.txt deleted file mode 100644 index 1a8928c7..00000000 --- a/installers/charm/mon/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master \ No newline at end of file diff --git a/installers/charm/mon/src/charm.py b/installers/charm/mon/src/charm.py deleted file mode 100755 index 9ad49ada..00000000 --- a/installers/charm/mon/src/charm.py +++ /dev/null @@ -1,395 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -# pylint: disable=E0213 - - -import base64 -import logging -from typing import NoReturn, Optional - - -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.main import main -from opslib.osm.charm import CharmedOsmBase, RelationsMissing -from opslib.osm.interfaces.keystone import KeystoneClient -from opslib.osm.interfaces.mongo import MongoClient -from opslib.osm.interfaces.prometheus import PrometheusClient -from opslib.osm.pod import ( - ContainerV3Builder, - FilesV3Builder, - PodRestartPolicy, - PodSpecV3Builder, -) -from opslib.osm.validator import ModelValidator, validator - - -logger = logging.getLogger(__name__) - -PORT = 8000 - - -def _check_certificate_data(name: str, content: str): - if not name or not content: - raise ValueError("certificate name and content must be a non-empty string") - - -def _extract_certificates(certs_config: str): - certificates = {} - if certs_config: - cert_list = certs_config.split(",") - for cert in cert_list: - name, content = cert.split(":") - _check_certificate_data(name, content) - certificates[name] = content - return certificates - - -def decode(content: str): - return base64.b64decode(content.encode("utf-8")).decode("utf-8") - - -class ConfigModel(ModelValidator): - keystone_enabled: bool - vca_host: str - vca_user: str - vca_secret: str - vca_cacert: str - database_commonkey: str - mongodb_uri: Optional[str] - log_level: str - openstack_default_granularity: int - global_request_timeout: int - collector_interval: int - vm_infra_metrics: bool - evaluator_interval: int - grafana_url: str - grafana_user: str - grafana_password: str - certificates: Optional[str] - image_pull_policy: str - debug_mode: bool - security_context: bool - - @validator("log_level") - def validate_log_level(cls, v): - if v not in {"INFO", "DEBUG"}: - raise ValueError("value must be INFO or DEBUG") - return v - - @validator("certificates") - def validate_certificates(cls, v): - # Raises an exception if it cannot extract the certificates - _extract_certificates(v) - return v - - @validator("mongodb_uri") - def validate_mongodb_uri(cls, v): - if v and not v.startswith("mongodb://"): - raise ValueError("mongodb_uri is not properly formed") - return v - - @validator("image_pull_policy") - def validate_image_pull_policy(cls, v): - values = { - "always": "Always", - "ifnotpresent": "IfNotPresent", - "never": "Never", - } - v = v.lower() - if v not in values.keys(): - raise ValueError("value must be always, ifnotpresent or never") - return values[v] - - @property - def certificates_dict(cls): - return _extract_certificates(cls.certificates) if cls.certificates else {} - - -class MonCharm(CharmedOsmBase): - on = KafkaEvents() - - def __init__(self, *args) -> NoReturn: - super().__init__( - *args, - oci_image="image", - vscode_workspace=VSCODE_WORKSPACE, - ) - if self.config.get("debug_mode"): - self.enable_debug_mode( - pubkey=self.config.get("debug_pubkey"), - hostpaths={ - "MON": { - "hostpath": self.config.get("debug_mon_local_path"), - "container-path": "/usr/lib/python3/dist-packages/osm_mon", - }, - "N2VC": { - "hostpath": self.config.get("debug_n2vc_local_path"), - "container-path": "/usr/lib/python3/dist-packages/n2vc", - }, - "osm_common": { - "hostpath": self.config.get("debug_common_local_path"), - "container-path": "/usr/lib/python3/dist-packages/osm_common", - }, - }, - ) - self.kafka = KafkaRequires(self) - self.framework.observe(self.on.kafka_available, self.configure_pod) - self.framework.observe(self.on.kafka_broken, self.configure_pod) - - self.mongodb_client = MongoClient(self, "mongodb") - self.framework.observe(self.on["mongodb"].relation_changed, self.configure_pod) - self.framework.observe(self.on["mongodb"].relation_broken, self.configure_pod) - - self.prometheus_client = PrometheusClient(self, "prometheus") - self.framework.observe( - self.on["prometheus"].relation_changed, self.configure_pod - ) - self.framework.observe( - self.on["prometheus"].relation_broken, self.configure_pod - ) - - self.keystone_client = KeystoneClient(self, "keystone") - self.framework.observe(self.on["keystone"].relation_changed, self.configure_pod) - self.framework.observe(self.on["keystone"].relation_broken, self.configure_pod) - - def _check_missing_dependencies(self, config: ConfigModel): - missing_relations = [] - - if not self.kafka.host or not self.kafka.port: - missing_relations.append("kafka") - if not config.mongodb_uri and self.mongodb_client.is_missing_data_in_unit(): - missing_relations.append("mongodb") - if self.prometheus_client.is_missing_data_in_app(): - missing_relations.append("prometheus") - if config.keystone_enabled: - if self.keystone_client.is_missing_data_in_app(): - missing_relations.append("keystone") - - if missing_relations: - raise RelationsMissing(missing_relations) - - def _build_cert_files( - self, - config: ConfigModel, - ): - cert_files_builder = FilesV3Builder() - for name, content in config.certificates_dict.items(): - cert_files_builder.add_file(name, decode(content), mode=0o600) - return cert_files_builder.build() - - def build_pod_spec(self, image_info): - # Validate config - config = ConfigModel(**dict(self.config)) - - if config.mongodb_uri and not self.mongodb_client.is_missing_data_in_unit(): - raise Exception("Mongodb data cannot be provided via config and relation") - - # Check relations - self._check_missing_dependencies(config) - - security_context_enabled = ( - config.security_context if not config.debug_mode else False - ) - - # Create Builder for the PodSpec - pod_spec_builder = PodSpecV3Builder( - enable_security_context=security_context_enabled - ) - - # Add secrets to the pod - mongodb_secret_name = f"{self.app.name}-mongodb-secret" - pod_spec_builder.add_secret( - mongodb_secret_name, - { - "uri": config.mongodb_uri or self.mongodb_client.connection_string, - "commonkey": config.database_commonkey, - }, - ) - grafana_secret_name = f"{self.app.name}-grafana-secret" - pod_spec_builder.add_secret( - grafana_secret_name, - { - "url": config.grafana_url, - "user": config.grafana_user, - "password": config.grafana_password, - }, - ) - - vca_secret_name = f"{self.app.name}-vca-secret" - pod_spec_builder.add_secret( - vca_secret_name, - { - "host": config.vca_host, - "user": config.vca_user, - "secret": config.vca_secret, - "cacert": config.vca_cacert, - }, - ) - - # Build Container - container_builder = ContainerV3Builder( - self.app.name, - image_info, - config.image_pull_policy, - run_as_non_root=security_context_enabled, - ) - certs_files = self._build_cert_files(config) - - if certs_files: - container_builder.add_volume_config("certs", "/certs", certs_files) - - container_builder.add_port(name=self.app.name, port=PORT) - container_builder.add_envs( - { - # General configuration - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMMON_OPENSTACK_DEFAULT_GRANULARITY": config.openstack_default_granularity, - "OSMMON_GLOBAL_REQUEST_TIMEOUT": config.global_request_timeout, - "OSMMON_GLOBAL_LOGLEVEL": config.log_level, - "OSMMON_COLLECTOR_INTERVAL": config.collector_interval, - "OSMMON_COLLECTOR_VM_INFRA_METRICS": config.vm_infra_metrics, - "OSMMON_EVALUATOR_INTERVAL": config.evaluator_interval, - # Kafka configuration - "OSMMON_MESSAGE_DRIVER": "kafka", - "OSMMON_MESSAGE_HOST": self.kafka.host, - "OSMMON_MESSAGE_PORT": self.kafka.port, - # Database configuration - "OSMMON_DATABASE_DRIVER": "mongo", - # Prometheus configuration - "OSMMON_PROMETHEUS_URL": f"http://{self.prometheus_client.hostname}:{self.prometheus_client.port}", - } - ) - prometheus_user = self.prometheus_client.user - prometheus_password = self.prometheus_client.password - if prometheus_user and prometheus_password: - container_builder.add_envs( - { - "OSMMON_PROMETHEUS_USER": prometheus_user, - "OSMMON_PROMETHEUS_PASSWORD": prometheus_password, - } - ) - container_builder.add_secret_envs( - secret_name=mongodb_secret_name, - envs={ - "OSMMON_DATABASE_URI": "uri", - "OSMMON_DATABASE_COMMONKEY": "commonkey", - }, - ) - container_builder.add_secret_envs( - secret_name=vca_secret_name, - envs={ - "OSMMON_VCA_HOST": "host", - "OSMMON_VCA_USER": "user", - "OSMMON_VCA_SECRET": "secret", - "OSMMON_VCA_CACERT": "cacert", - }, - ) - container_builder.add_secret_envs( - secret_name=grafana_secret_name, - envs={ - "OSMMON_GRAFANA_URL": "url", - "OSMMON_GRAFANA_USER": "user", - "OSMMON_GRAFANA_PASSWORD": "password", - }, - ) - if config.keystone_enabled: - keystone_secret_name = f"{self.app.name}-keystone-secret" - pod_spec_builder.add_secret( - keystone_secret_name, - { - "url": self.keystone_client.host, - "user_domain": self.keystone_client.user_domain_name, - "project_domain": self.keystone_client.project_domain_name, - "service_username": self.keystone_client.username, - "service_password": self.keystone_client.password, - "service_project": self.keystone_client.service, - }, - ) - container_builder.add_env("OSMMON_KEYSTONE_ENABLED", True) - container_builder.add_secret_envs( - secret_name=keystone_secret_name, - envs={ - "OSMMON_KEYSTONE_URL": "url", - "OSMMON_KEYSTONE_DOMAIN_NAME": "user_domain", - "OSMMON_KEYSTONE_PROJECT_DOMAIN_NAME": "project_domain", - "OSMMON_KEYSTONE_SERVICE_USER": "service_username", - "OSMMON_KEYSTONE_SERVICE_PASSWORD": "service_password", - "OSMMON_KEYSTONE_SERVICE_PROJECT": "service_project", - }, - ) - container = container_builder.build() - - # Add restart policy - restart_policy = PodRestartPolicy() - restart_policy.add_secrets() - pod_spec_builder.set_restart_policy(restart_policy) - - # Add container to pod spec - pod_spec_builder.add_container(container) - - return pod_spec_builder.build() - - -VSCODE_WORKSPACE = { - "folders": [ - {"path": "/usr/lib/python3/dist-packages/osm_mon"}, - {"path": "/usr/lib/python3/dist-packages/osm_common"}, - {"path": "/usr/lib/python3/dist-packages/n2vc"}, - ], - "settings": {}, - "launch": { - "version": "0.2.0", - "configurations": [ - { - "name": "MON Server", - "type": "python", - "request": "launch", - "module": "osm_mon.cmd.mon_server", - "justMyCode": False, - }, - { - "name": "MON evaluator", - "type": "python", - "request": "launch", - "module": "osm_mon.cmd.mon_evaluator", - "justMyCode": False, - }, - { - "name": "MON collector", - "type": "python", - "request": "launch", - "module": "osm_mon.cmd.mon_collector", - "justMyCode": False, - }, - { - "name": "MON dashboarder", - "type": "python", - "request": "launch", - "module": "osm_mon.cmd.mon_dashboarder", - "justMyCode": False, - }, - ], - }, -} -if __name__ == "__main__": - main(MonCharm) diff --git a/installers/charm/mon/src/pod_spec.py b/installers/charm/mon/src/pod_spec.py deleted file mode 100644 index dcadfc04..00000000 --- a/installers/charm/mon/src/pod_spec.py +++ /dev/null @@ -1,231 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import logging -from typing import Any, Dict, List, NoReturn - -logger = logging.getLogger(__name__) - - -def _validate_data( - config_data: Dict[str, Any], relation_data: Dict[str, Any] -) -> NoReturn: - """Validate input data. - - Args: - config_data (Dict[str, Any]): configuration data. - relation_data (Dict[str, Any]): relation data. - """ - config_validators = { - "openstack_default_granularity": lambda value, _: ( - isinstance(value, int) and value > 0 - ), - "global_request_timeout": lambda value, _: isinstance(value, int) and value > 0, - "log_level": lambda value, _: ( - isinstance(value, str) and value in ("INFO", "DEBUG") - ), - "collector_interval": lambda value, _: isinstance(value, int) and value > 0, - "evaluator_interval": lambda value, _: isinstance(value, int) and value > 0, - "database_commonkey": lambda value, _: ( - isinstance(value, str) and len(value) > 0 - ), - "vca_host": lambda value, _: isinstance(value, str) and len(value) > 0, - "vca_user": lambda value, _: isinstance(value, str) and len(value) > 0, - "vca_password": lambda value, _: isinstance(value, str) and len(value) > 0, - "vca_cacert": lambda value, _: isinstance(value, str), - } - relation_validators = { - "message_host": lambda value, _: isinstance(value, str) and len(value) > 0, - "message_port": lambda value, _: isinstance(value, int) and value > 0, - "database_uri": lambda value, _: ( - isinstance(value, str) and value.startswith("mongodb://") - ), - "prometheus_host": lambda value, _: isinstance(value, str) and len(value) > 0, - "prometheus_port": lambda value, _: isinstance(value, int) and value > 0, - } - problems = [] - - for key, validator in config_validators.items(): - valid = validator(config_data.get(key), config_data) - - if not valid: - problems.append(key) - - for key, validator in relation_validators.items(): - valid = validator(relation_data.get(key), relation_data) - - if not valid: - problems.append(key) - - if len(problems) > 0: - raise ValueError("Errors found in: {}".format(", ".join(problems))) - - -def _make_pod_ports(port: int) -> List[Dict[str, Any]]: - """Generate pod ports details. - - Args: - port (int): port to expose. - - Returns: - List[Dict[str, Any]]: pod port details. - """ - return [{"name": "mon", "containerPort": port, "protocol": "TCP"}] - - -def _make_pod_envconfig( - config: Dict[str, Any], relation_state: Dict[str, Any] -) -> Dict[str, Any]: - """Generate pod environment configuration. - - Args: - config (Dict[str, Any]): configuration information. - relation_state (Dict[str, Any]): relation state information. - - Returns: - Dict[str, Any]: pod environment configuration. - """ - envconfig = { - # General configuration - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMMON_OPENSTACK_DEFAULT_GRANULARITY": config["openstack_default_granularity"], - "OSMMON_GLOBAL_REQUEST_TIMEOUT": config["global_request_timeout"], - "OSMMON_GLOBAL_LOGLEVEL": config["log_level"], - "OSMMON_COLLECTOR_INTERVAL": config["collector_interval"], - "OSMMON_EVALUATOR_INTERVAL": config["evaluator_interval"], - # Kafka configuration - "OSMMON_MESSAGE_DRIVER": "kafka", - "OSMMON_MESSAGE_HOST": relation_state["message_host"], - "OSMMON_MESSAGE_PORT": relation_state["message_port"], - # Database configuration - "OSMMON_DATABASE_DRIVER": "mongo", - "OSMMON_DATABASE_URI": relation_state["database_uri"], - "OSMMON_DATABASE_COMMONKEY": config["database_commonkey"], - # Prometheus configuration - "OSMMON_PROMETHEUS_URL": f"http://{relation_state['prometheus_host']}:{relation_state['prometheus_port']}", - # VCA configuration - "OSMMON_VCA_HOST": config["vca_host"], - "OSMMON_VCA_USER": config["vca_user"], - "OSMMON_VCA_SECRET": config["vca_password"], - "OSMMON_VCA_CACERT": config["vca_cacert"], - } - - return envconfig - - -def _make_startup_probe() -> Dict[str, Any]: - """Generate startup probe. - - Returns: - Dict[str, Any]: startup probe. - """ - return { - "exec": {"command": ["/usr/bin/pgrep python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - -def _make_readiness_probe(port: int) -> Dict[str, Any]: - """Generate readiness probe. - - Args: - port (int): [description] - - Returns: - Dict[str, Any]: readiness probe. - """ - return { - "tcpSocket": { - "port": port, - }, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - -def _make_liveness_probe(port: int) -> Dict[str, Any]: - """Generate liveness probe. - - Args: - port (int): [description] - - Returns: - Dict[str, Any]: liveness probe. - """ - return { - "tcpSocket": { - "port": port, - }, - "initialDelaySeconds": 45, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - -def make_pod_spec( - image_info: Dict[str, str], - config: Dict[str, Any], - relation_state: Dict[str, Any], - app_name: str = "mon", - port: int = 8000, -) -> Dict[str, Any]: - """Generate the pod spec information. - - Args: - image_info (Dict[str, str]): Object provided by - OCIImageResource("image").fetch(). - config (Dict[str, Any]): Configuration information. - relation_state (Dict[str, Any]): Relation state information. - app_name (str, optional): Application name. Defaults to "mon". - port (int, optional): Port for the container. Defaults to 8000. - - Returns: - Dict[str, Any]: Pod spec dictionary for the charm. - """ - if not image_info: - return None - - _validate_data(config, relation_state) - - ports = _make_pod_ports(port) - env_config = _make_pod_envconfig(config, relation_state) - - return { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": ports, - "envConfig": env_config, - } - ], - "kubernetesResources": { - "ingressResources": [], - }, - } diff --git a/installers/charm/mon/tests/__init__.py b/installers/charm/mon/tests/__init__.py deleted file mode 100644 index 446d5cee..00000000 --- a/installers/charm/mon/tests/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -"""Init mocking for unit tests.""" - -import sys - - -import mock - - -class OCIImageResourceErrorMock(Exception): - pass - - -sys.path.append("src") - -oci_image = mock.MagicMock() -oci_image.OCIImageResourceError = OCIImageResourceErrorMock -sys.modules["oci_image"] = oci_image -sys.modules["oci_image"].OCIImageResource().fetch.return_value = {} diff --git a/installers/charm/mon/tests/test_charm.py b/installers/charm/mon/tests/test_charm.py deleted file mode 100644 index e9748d30..00000000 --- a/installers/charm/mon/tests/test_charm.py +++ /dev/null @@ -1,411 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import base64 -import sys -from typing import NoReturn -import unittest - -from charm import MonCharm -from ops.model import ActiveStatus, BlockedStatus -from ops.testing import Harness - - -def encode(content: str): - return base64.b64encode(content.encode("ascii")).decode("utf-8") - - -certificate_pem = encode( - """ ------BEGIN CERTIFICATE----- -MIIDazCCAlOgAwIBAgIUf1b0s3UKtrxHXH2rge7UaQyfJAMwDQYJKoZIhvcNAQEL -BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAzMjIxNzEyMjdaFw0zMTAz -MjAxNzEyMjdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw -HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQCgCfCBgYAN6ON0yHDXuW407rFtJVRf0u46Jrp0Dk7J -kkSZ1e7Kq14r7yFHazEBWv78oOdwBocvWrd8leLuf3bYGcHR65hRy6A/fbYm5Aje -cKpwlFwaqfR4BLelwJl79jZ2rJX738cCBVrIk1nAVdOxGrXV4MTWUaKR2c+uKKvc -OKRT+5VqCeP4N5FWeATZ/KqGu8uV9E9WhFgwIZyStemLyLaDbn5PmAQ6S9oeR5jJ -o2gEEp/lDKvsqOWs76KFumSKa9hQs5Dw2lj0mb1UoyYK1gYc4ubzVChJadv44AU8 -MYtIjlFn1X1P+RjaKZNUIAGXkoLwYn6SizF6y6LiuFS9AgMBAAGjUzBRMB0GA1Ud -DgQWBBRl+/23CB+FXczeAZRQyYcfOdy9YDAfBgNVHSMEGDAWgBRl+/23CB+FXcze -AZRQyYcfOdy9YDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAd -dkeDym6lRN8kWFtfu3IyiLF8G8sn91qNbH3Yr4TuTBhgcjYyW6PgisSbrNgA9ysE -GoaF7ohb8GeVfCsQdK23+NpAlj/+DZ3OnGcxwXj1RUAz4yr9kanV1yuEtr1q2xJI -UaECWr8HZlwGBAKNTGx2EXT2/2aFzgULpDcxzTKD+MRpKpMUrWhf9ULvVrclvHWe -POLYhobUFuBHuo6rt5Rcq16j67zCX9EVTlAE3o2OECIWByK22sXdeOidYMpTkl4q -8FrOqjNsx5d+SBPJBv/pqtBm4bA47Vx1P8tbWOQ4bXS0UmXgwpeBOU/O/ot30+KS -JnKEy+dYyvVBKg77sRHw ------END CERTIFICATE----- -""" -) - - -class TestCharm(unittest.TestCase): - """Prometheus Charm unit tests.""" - - def setUp(self) -> NoReturn: - """Test setup""" - self.image_info = sys.modules["oci_image"].OCIImageResource().fetch() - self.harness = Harness(MonCharm) - self.harness.set_leader(is_leader=True) - self.harness.begin() - self.config = { - "vca_host": "192.168.0.13", - "vca_user": "admin", - "vca_secret": "admin", - "vca_cacert": "cacert", - "database_commonkey": "commonkey", - "mongodb_uri": "", - "log_level": "INFO", - "openstack_default_granularity": 10, - "global_request_timeout": 10, - "collector_interval": 30, - "evaluator_interval": 30, - "keystone_enabled": True, - "certificates": f"cert1:{certificate_pem}", - } - self.harness.update_config(self.config) - - def test_config_changed_no_relations( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue( - all( - relation in self.harness.charm.unit.status.message - for relation in ["mongodb", "kafka", "prometheus", "keystone"] - ) - ) - - def test_config_changed_non_leader( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - self.harness.set_leader(is_leader=False) - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus) - - def test_with_relations_and_mongodb_config( - self, - ) -> NoReturn: - "Test with relations (internal)" - self.initialize_kafka_relation() - self.initialize_mongo_config() - self.initialize_prometheus_relation() - self.initialize_keystone_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_with_relations( - self, - ) -> NoReturn: - "Test with relations (internal)" - self.initialize_kafka_relation() - self.initialize_mongo_relation() - self.initialize_prometheus_relation() - self.initialize_keystone_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_exception_mongodb_relation_and_config( - self, - ) -> NoReturn: - "Test with relations and config for mongodb. Must fail" - self.initialize_mongo_relation() - self.initialize_mongo_config() - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def initialize_kafka_relation(self): - kafka_relation_id = self.harness.add_relation("kafka", "kafka") - self.harness.add_relation_unit(kafka_relation_id, "kafka/0") - self.harness.update_relation_data( - kafka_relation_id, "kafka", {"host": "kafka", "port": 9092} - ) - - def initialize_mongo_config(self): - self.harness.update_config({"mongodb_uri": "mongodb://mongo:27017"}) - - def initialize_mongo_relation(self): - mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") - self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") - self.harness.update_relation_data( - mongodb_relation_id, - "mongodb/0", - {"connection_string": "mongodb://mongo:27017"}, - ) - - def initialize_prometheus_relation(self): - prometheus_relation_id = self.harness.add_relation("prometheus", "prometheus") - self.harness.add_relation_unit(prometheus_relation_id, "prometheus/0") - self.harness.update_relation_data( - prometheus_relation_id, - "prometheus", - {"hostname": "prometheus", "port": 9090}, - ) - - def initialize_keystone_relation(self): - keystone_relation_id = self.harness.add_relation("keystone", "keystone") - self.harness.add_relation_unit(keystone_relation_id, "keystone/0") - self.harness.update_relation_data( - keystone_relation_id, - "keystone", - { - "host": "host", - "port": 5000, - "user_domain_name": "ud", - "project_domain_name": "pd", - "username": "u", - "password": "p", - "service": "s", - "keystone_db_password": "something", - "region_id": "something", - "admin_username": "something", - "admin_password": "something", - "admin_project_name": "something", - }, - ) - - -if __name__ == "__main__": - unittest.main() - - -# class TestCharm(unittest.TestCase): -# """MON Charm unit tests.""" - -# def setUp(self) -> NoReturn: -# """Test setup""" -# self.harness = Harness(MonCharm) -# self.harness.set_leader(is_leader=True) -# self.harness.begin() - -# def test_on_start_without_relations(self) -> NoReturn: -# """Test installation without any relation.""" -# self.harness.charm.on.start.emit() - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertIn("prometheus", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - -# def test_on_start_with_relations(self) -> NoReturn: -# """Test deployment without keystone.""" -# expected_result = { -# "version": 3, -# "containers": [ -# { -# "name": "mon", -# "imageDetails": self.harness.charm.image.fetch(), -# "imagePullPolicy": "Always", -# "ports": [ -# { -# "name": "mon", -# "containerPort": 8000, -# "protocol": "TCP", -# } -# ], -# "envConfig": { -# "ALLOW_ANONYMOUS_LOGIN": "yes", -# "OSMMON_OPENSTACK_DEFAULT_GRANULARITY": 300, -# "OSMMON_GLOBAL_REQUEST_TIMEOUT": 10, -# "OSMMON_GLOBAL_LOGLEVEL": "INFO", -# "OSMMON_COLLECTOR_INTERVAL": 30, -# "OSMMON_EVALUATOR_INTERVAL": 30, -# "OSMMON_MESSAGE_DRIVER": "kafka", -# "OSMMON_MESSAGE_HOST": "kafka", -# "OSMMON_MESSAGE_PORT": 9092, -# "OSMMON_DATABASE_DRIVER": "mongo", -# "OSMMON_DATABASE_URI": "mongodb://mongo:27017", -# "OSMMON_DATABASE_COMMONKEY": "osm", -# "OSMMON_PROMETHEUS_URL": "http://prometheus:9090", -# "OSMMON_VCA_HOST": "admin", -# "OSMMON_VCA_USER": "admin", -# "OSMMON_VCA_SECRET": "secret", -# "OSMMON_VCA_CACERT": "", -# }, -# } -# ], -# "kubernetesResources": {"ingressResources": []}, -# } - -# self.harness.charm.on.start.emit() - -# # Check if kafka datastore is initialized -# self.assertIsNone(self.harness.charm.state.message_host) -# self.assertIsNone(self.harness.charm.state.message_port) - -# # Check if mongodb datastore is initialized -# self.assertIsNone(self.harness.charm.state.database_uri) - -# # Check if prometheus datastore is initialized -# self.assertIsNone(self.harness.charm.state.prometheus_host) -# self.assertIsNone(self.harness.charm.state.prometheus_port) - -# # Initializing the kafka relation -# kafka_relation_id = self.harness.add_relation("kafka", "kafka") -# self.harness.add_relation_unit(kafka_relation_id, "kafka/0") -# self.harness.update_relation_data( -# kafka_relation_id, "kafka/0", {"host": "kafka", "port": 9092} -# ) - -# # Initializing the mongo relation -# mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") -# self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") -# self.harness.update_relation_data( -# mongodb_relation_id, -# "mongodb/0", -# {"connection_string": "mongodb://mongo:27017"}, -# ) - -# # Initializing the prometheus relation -# prometheus_relation_id = self.harness.add_relation("prometheus", "prometheus") -# self.harness.add_relation_unit(prometheus_relation_id, "prometheus/0") -# self.harness.update_relation_data( -# prometheus_relation_id, -# "prometheus", -# {"hostname": "prometheus", "port": 9090}, -# ) - -# # Checking if kafka data is stored -# self.assertEqual(self.harness.charm.state.message_host, "kafka") -# self.assertEqual(self.harness.charm.state.message_port, 9092) - -# # Checking if mongodb data is stored -# self.assertEqual(self.harness.charm.state.database_uri, "mongodb://mongo:27017") - -# # Checking if prometheus data is stored -# self.assertEqual(self.harness.charm.state.prometheus_host, "prometheus") -# self.assertEqual(self.harness.charm.state.prometheus_port, 9090) - -# # Verifying status -# self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# pod_spec, _ = self.harness.get_pod_spec() - -# self.assertDictEqual(expected_result, pod_spec) - -# def test_on_kafka_unit_relation_changed(self) -> NoReturn: -# """Test to see if kafka relation is updated.""" -# self.harness.charm.on.start.emit() - -# self.assertIsNone(self.harness.charm.state.message_host) -# self.assertIsNone(self.harness.charm.state.message_port) - -# relation_id = self.harness.add_relation("kafka", "kafka") -# self.harness.add_relation_unit(relation_id, "kafka/0") -# self.harness.update_relation_data( -# relation_id, "kafka/0", {"host": "kafka", "port": 9092} -# ) - -# self.assertEqual(self.harness.charm.state.message_host, "kafka") -# self.assertEqual(self.harness.charm.state.message_port, 9092) - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertNotIn("kafka", self.harness.charm.unit.status.message) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertIn("prometheus", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - -# def test_on_mongodb_unit_relation_changed(self) -> NoReturn: -# """Test to see if mongodb relation is updated.""" -# self.harness.charm.on.start.emit() - -# self.assertIsNone(self.harness.charm.state.database_uri) - -# relation_id = self.harness.add_relation("mongodb", "mongodb") -# self.harness.add_relation_unit(relation_id, "mongodb/0") -# self.harness.update_relation_data( -# relation_id, "mongodb/0", {"connection_string": "mongodb://mongo:27017"} -# ) - -# self.assertEqual(self.harness.charm.state.database_uri, "mongodb://mongo:27017") - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertNotIn("mongodb", self.harness.charm.unit.status.message) -# self.assertIn("prometheus", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - -# def test_on_prometheus_unit_relation_changed(self) -> NoReturn: -# """Test to see if prometheus relation is updated.""" -# self.harness.charm.on.start.emit() - -# self.assertIsNone(self.harness.charm.state.prometheus_host) -# self.assertIsNone(self.harness.charm.state.prometheus_port) - -# relation_id = self.harness.add_relation("prometheus", "prometheus") -# self.harness.add_relation_unit(relation_id, "prometheus/0") -# self.harness.update_relation_data( -# relation_id, "prometheus", {"hostname": "prometheus", "port": 9090} -# ) - -# self.assertEqual(self.harness.charm.state.prometheus_host, "prometheus") -# self.assertEqual(self.harness.charm.state.prometheus_port, 9090) - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertNotIn("prometheus", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - - -# if __name__ == "__main__": -# unittest.main() diff --git a/installers/charm/mon/tests/test_pod_spec.py b/installers/charm/mon/tests/test_pod_spec.py deleted file mode 100644 index 86a3d169..00000000 --- a/installers/charm/mon/tests/test_pod_spec.py +++ /dev/null @@ -1,295 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -from typing import NoReturn -import unittest - -import pod_spec - - -class TestPodSpec(unittest.TestCase): - """Pod spec unit tests.""" - - def test_make_pod_ports(self) -> NoReturn: - """Testing make pod ports.""" - port = 8000 - - expected_result = [ - { - "name": "mon", - "containerPort": port, - "protocol": "TCP", - } - ] - - pod_ports = pod_spec._make_pod_ports(port) - - self.assertListEqual(expected_result, pod_ports) - - def test_make_pod_envconfig(self) -> NoReturn: - """Testing make pod envconfig.""" - config = { - "openstack_default_granularity": 300, - "global_request_timeout": 10, - "log_level": "INFO", - "database_commonkey": "osm", - "collector_interval": 30, - "evaluator_interval": 30, - "vca_host": "admin", - "vca_user": "admin", - "vca_password": "secret", - "vca_cacert": "", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - } - - expected_result = { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMMON_OPENSTACK_DEFAULT_GRANULARITY": config[ - "openstack_default_granularity" - ], - "OSMMON_GLOBAL_REQUEST_TIMEOUT": config["global_request_timeout"], - "OSMMON_GLOBAL_LOGLEVEL": config["log_level"], - "OSMMON_COLLECTOR_INTERVAL": config["collector_interval"], - "OSMMON_EVALUATOR_INTERVAL": config["evaluator_interval"], - "OSMMON_MESSAGE_DRIVER": "kafka", - "OSMMON_MESSAGE_HOST": relation_state["message_host"], - "OSMMON_MESSAGE_PORT": relation_state["message_port"], - "OSMMON_DATABASE_DRIVER": "mongo", - "OSMMON_DATABASE_URI": relation_state["database_uri"], - "OSMMON_DATABASE_COMMONKEY": config["database_commonkey"], - "OSMMON_PROMETHEUS_URL": f"http://{relation_state['prometheus_host']}:{relation_state['prometheus_port']}", - "OSMMON_VCA_HOST": config["vca_host"], - "OSMMON_VCA_USER": config["vca_user"], - "OSMMON_VCA_SECRET": config["vca_password"], - "OSMMON_VCA_CACERT": config["vca_cacert"], - } - - pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state) - - self.assertDictEqual(expected_result, pod_envconfig) - - def test_make_startup_probe(self) -> NoReturn: - """Testing make startup probe.""" - expected_result = { - "exec": {"command": ["/usr/bin/pgrep python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - startup_probe = pod_spec._make_startup_probe() - - self.assertDictEqual(expected_result, startup_probe) - - def test_make_readiness_probe(self) -> NoReturn: - """Testing make readiness probe.""" - port = 8000 - - expected_result = { - "tcpSocket": { - "port": port, - }, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - readiness_probe = pod_spec._make_readiness_probe(port) - - self.assertDictEqual(expected_result, readiness_probe) - - def test_make_liveness_probe(self) -> NoReturn: - """Testing make liveness probe.""" - port = 8000 - - expected_result = { - "tcpSocket": { - "port": port, - }, - "initialDelaySeconds": 45, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - liveness_probe = pod_spec._make_liveness_probe(port) - - self.assertDictEqual(expected_result, liveness_probe) - - def test_make_pod_spec(self) -> NoReturn: - """Testing make pod spec.""" - image_info = {"upstream-source": "opensourcemano/mon:8"} - config = { - "site_url": "", - "openstack_default_granularity": 300, - "global_request_timeout": 10, - "log_level": "INFO", - "database_commonkey": "osm", - "collector_interval": 30, - "evaluator_interval": 30, - "vca_host": "admin", - "vca_user": "admin", - "vca_password": "secret", - "vca_cacert": "", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - } - app_name = "mon" - port = 8000 - - expected_result = { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": [ - { - "name": app_name, - "containerPort": port, - "protocol": "TCP", - } - ], - "envConfig": { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMMON_OPENSTACK_DEFAULT_GRANULARITY": config[ - "openstack_default_granularity" - ], - "OSMMON_GLOBAL_REQUEST_TIMEOUT": config[ - "global_request_timeout" - ], - "OSMMON_GLOBAL_LOGLEVEL": config["log_level"], - "OSMMON_COLLECTOR_INTERVAL": config["collector_interval"], - "OSMMON_EVALUATOR_INTERVAL": config["evaluator_interval"], - "OSMMON_MESSAGE_DRIVER": "kafka", - "OSMMON_MESSAGE_HOST": relation_state["message_host"], - "OSMMON_MESSAGE_PORT": relation_state["message_port"], - "OSMMON_DATABASE_DRIVER": "mongo", - "OSMMON_DATABASE_URI": relation_state["database_uri"], - "OSMMON_DATABASE_COMMONKEY": config["database_commonkey"], - "OSMMON_PROMETHEUS_URL": ( - f"http://{relation_state['prometheus_host']}:{relation_state['prometheus_port']}" - ), - "OSMMON_VCA_HOST": config["vca_host"], - "OSMMON_VCA_USER": config["vca_user"], - "OSMMON_VCA_SECRET": config["vca_password"], - "OSMMON_VCA_CACERT": config["vca_cacert"], - }, - } - ], - "kubernetesResources": {"ingressResources": []}, - } - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertDictEqual(expected_result, spec) - - def test_make_pod_spec_without_image_info(self) -> NoReturn: - """Testing make pod spec without image_info.""" - image_info = None - config = { - "site_url": "", - "openstack_default_granularity": 300, - "global_request_timeout": 10, - "log_level": "INFO", - "database_commonkey": "osm", - "collector_interval": 30, - "evaluator_interval": 30, - "vca_host": "admin", - "vca_user": "admin", - "vca_password": "secret", - "vca_cacert": "", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - } - app_name = "mon" - port = 8000 - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertIsNone(spec) - - def test_make_pod_spec_without_config(self) -> NoReturn: - """Testing make pod spec without config.""" - image_info = {"upstream-source": "opensourcemano/mon:8"} - config = {} - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - } - app_name = "mon" - port = 8000 - - with self.assertRaises(ValueError): - pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port) - - def test_make_pod_spec_without_relation_state(self) -> NoReturn: - """Testing make pod spec without relation_state.""" - image_info = {"upstream-source": "opensourcemano/mon:8"} - config = { - "site_url": "", - "openstack_default_granularity": 300, - "global_request_timeout": 10, - "log_level": "INFO", - "database_commonkey": "osm", - "collector_interval": 30, - "evaluator_interval": 30, - "vca_host": "admin", - "vca_user": "admin", - "vca_password": "secret", - "vca_cacert": "", - } - relation_state = {} - app_name = "mon" - port = 8000 - - with self.assertRaises(ValueError): - pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port) - - -if __name__ == "__main__": - unittest.main() diff --git a/installers/charm/mon/tox.ini b/installers/charm/mon/tox.ini deleted file mode 100644 index f3c91440..00000000 --- a/installers/charm/mon/tox.ini +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## -####################################################################################### - -[tox] -envlist = black, cover, flake8, pylint, yamllint, safety -skipsdist = true - -[tox:jenkins] -toxworkdir = /tmp/.tox - -[testenv] -basepython = python3.8 -setenv = - VIRTUAL_ENV={envdir} - PYTHONPATH = {toxinidir}:{toxinidir}/lib:{toxinidir}/src - PYTHONDONTWRITEBYTECODE = 1 -deps = -r{toxinidir}/requirements.txt - - -####################################################################################### -[testenv:black] -deps = black -commands = - black --check --diff src/ tests/ - - -####################################################################################### -[testenv:cover] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - coverage - nose2 -commands = - sh -c 'rm -f nosetests.xml' - coverage erase - nose2 -C --coverage src - coverage report --omit='*tests*' - coverage html -d ./cover --omit='*tests*' - coverage xml -o coverage.xml --omit=*tests* -whitelist_externals = sh - - -####################################################################################### -[testenv:flake8] -deps = flake8 - flake8-import-order -commands = - flake8 src/ tests/ - - -####################################################################################### -[testenv:pylint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - pylint==2.10.2 -commands = - pylint -E src/ tests/ - - -####################################################################################### -[testenv:safety] -setenv = - LC_ALL=C.UTF-8 - LANG=C.UTF-8 -deps = {[testenv]deps} - safety -commands = - - safety check --full-report - - -####################################################################################### -[testenv:yamllint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - yamllint -commands = yamllint . - -####################################################################################### -[testenv:build] -passenv=HTTP_PROXY HTTPS_PROXY NO_PROXY -whitelist_externals = - charmcraft - sh -commands = - charmcraft pack - sh -c 'ubuntu_version=20.04; \ - architectures="amd64-aarch64-arm64"; \ - charm_name=`cat metadata.yaml | grep -E "^name: " | cut -f 2 -d " "`; \ - mv $charm_name"_ubuntu-"$ubuntu_version-$architectures.charm $charm_name.charm' - -####################################################################################### -[flake8] -ignore = - W291, - W293, - W503, - E123, - E125, - E226, - E241, -exclude = - .git, - __pycache__, - .tox, -max-line-length = 120 -show-source = True -builtins = _ -max-complexity = 10 -import-order-style = google diff --git a/installers/charm/nbi/.gitignore b/installers/charm/nbi/.gitignore deleted file mode 100644 index 2885df27..00000000 --- a/installers/charm/nbi/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.stestr -cover -release \ No newline at end of file diff --git a/installers/charm/nbi/.jujuignore b/installers/charm/nbi/.jujuignore deleted file mode 100644 index 3ae3e7dc..00000000 --- a/installers/charm/nbi/.jujuignore +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.gitignore -.stestr -cover -release -tests/ -requirements* -tox.ini diff --git a/installers/charm/nbi/.yamllint.yaml b/installers/charm/nbi/.yamllint.yaml deleted file mode 100644 index d71fb69f..00000000 --- a/installers/charm/nbi/.yamllint.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - ---- -extends: default - -yaml-files: - - "*.yaml" - - "*.yml" - - ".yamllint" -ignore: | - .tox - cover/ - build/ - venv - release/ diff --git a/installers/charm/nbi/README.md b/installers/charm/nbi/README.md deleted file mode 100644 index de0a4bf8..00000000 --- a/installers/charm/nbi/README.md +++ /dev/null @@ -1,23 +0,0 @@ - - -# NBI operator Charm for Kubernetes - -## Requirements \ No newline at end of file diff --git a/installers/charm/nbi/charmcraft.yaml b/installers/charm/nbi/charmcraft.yaml deleted file mode 100644 index 0a285a9d..00000000 --- a/installers/charm/nbi/charmcraft.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -type: charm -bases: - - build-on: - - name: ubuntu - channel: "20.04" - architectures: ["amd64"] - run-on: - - name: ubuntu - channel: "20.04" - architectures: - - amd64 - - aarch64 - - arm64 -parts: - charm: - build-packages: [git] diff --git a/installers/charm/nbi/config.yaml b/installers/charm/nbi/config.yaml deleted file mode 100644 index f10304fe..00000000 --- a/installers/charm/nbi/config.yaml +++ /dev/null @@ -1,109 +0,0 @@ -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -options: - 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. - Note: if set to 0, there is no limit. - default: 0 - ingress_class: - type: string - description: | - Ingress class name. This is useful for selecting the ingress to be used - in case there are multiple ingresses in the underlying k8s clusters. - 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: "" - cluster_issuer: - type: string - description: Name of the cluster issuer for TLS certificates - default: "" - log_level: - description: "Log Level" - type: string - default: "INFO" - database_commonkey: - description: Database COMMON KEY - type: string - default: osm - auth_backend: - type: string - description: Authentication backend ('internal' or 'keystone') - default: internal - enable_test: - type: boolean - description: Enable test endpoints of NBI. - default: false - mongodb_uri: - type: string - description: MongoDB URI (external database) - image_pull_policy: - type: string - description: | - ImagePullPolicy configuration for the pod. - Possible values: always, ifnotpresent, never - default: always - debug_mode: - description: | - If true, debug mode is activated. It means that the service will not run, - and instead, the command for the container will be a `sleep infinity`. - Note: If enabled, security_context will be disabled. - type: boolean - default: false - debug_pubkey: - description: | - Public SSH key that will be injected to the application pod. - type: string - debug_nbi_local_path: - description: | - Local full path to the NBI project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - debug_common_local_path: - description: | - Local full path to the COMMON project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - security_context: - description: Enables the security context of the pods - type: boolean - default: false diff --git a/installers/charm/nbi/lib/charms/kafka_k8s/v0/kafka.py b/installers/charm/nbi/lib/charms/kafka_k8s/v0/kafka.py deleted file mode 100644 index 1baf9a88..00000000 --- a/installers/charm/nbi/lib/charms/kafka_k8s/v0/kafka.py +++ /dev/null @@ -1,207 +0,0 @@ -# Copyright 2022 Canonical Ltd. -# See LICENSE file for licensing details. -# -# 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. - -"""Kafka library. - -This [library](https://juju.is/docs/sdk/libraries) implements both sides of the -`kafka` [interface](https://juju.is/docs/sdk/relations). - -The *provider* side of this interface is implemented by the -[kafka-k8s Charmed Operator](https://charmhub.io/kafka-k8s). - -Any Charmed Operator that *requires* Kafka for providing its -service should implement the *requirer* side of this interface. - -In a nutshell using this library to implement a Charmed Operator *requiring* -Kafka would look like - -``` -$ charmcraft fetch-lib charms.kafka_k8s.v0.kafka -``` - -`metadata.yaml`: - -``` -requires: - kafka: - interface: kafka - limit: 1 -``` - -`src/charm.py`: - -``` -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.charm import CharmBase - - -class MyCharm(CharmBase): - - on = KafkaEvents() - - def __init__(self, *args): - super().__init__(*args) - self.kafka = KafkaRequires(self) - self.framework.observe( - self.on.kafka_available, - self._on_kafka_available, - ) - self.framework.observe( - self.on.kafka_broken, - self._on_kafka_broken, - ) - - def _on_kafka_available(self, event): - # Get Kafka host and port - host: str = self.kafka.host - port: int = self.kafka.port - # host => "kafka-k8s" - # port => 9092 - - def _on_kafka_broken(self, event): - # Stop service - # ... - self.unit.status = BlockedStatus("need kafka relation") -``` - -You can file bugs -[here](https://github.com/charmed-osm/kafka-k8s-operator/issues)! -""" - -from typing import Optional - -from ops.charm import CharmBase, CharmEvents -from ops.framework import EventBase, EventSource, Object - -# The unique Charmhub library identifier, never change it -from ops.model import Relation - -LIBID = "eacc8c85082347c9aae740e0220b8376" - -# 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 = 3 - - -KAFKA_HOST_APP_KEY = "host" -KAFKA_PORT_APP_KEY = "port" - - -class _KafkaAvailableEvent(EventBase): - """Event emitted when Kafka is available.""" - - -class _KafkaBrokenEvent(EventBase): - """Event emitted when Kafka relation is broken.""" - - -class KafkaEvents(CharmEvents): - """Kafka events. - - This class defines the events that Kafka can emit. - - Events: - kafka_available (_KafkaAvailableEvent) - """ - - kafka_available = EventSource(_KafkaAvailableEvent) - kafka_broken = EventSource(_KafkaBrokenEvent) - - -class KafkaRequires(Object): - """Requires-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self.charm = charm - self._endpoint_name = endpoint_name - - # Observe relation events - event_observe_mapping = { - charm.on[self._endpoint_name].relation_changed: self._on_relation_changed, - charm.on[self._endpoint_name].relation_broken: self._on_relation_broken, - } - for event, observer in event_observe_mapping.items(): - self.framework.observe(event, observer) - - def _on_relation_changed(self, event) -> None: - if event.relation.app and all( - key in event.relation.data[event.relation.app] - for key in (KAFKA_HOST_APP_KEY, KAFKA_PORT_APP_KEY) - ): - self.charm.on.kafka_available.emit() - - def _on_relation_broken(self, _) -> None: - self.charm.on.kafka_broken.emit() - - @property - def host(self) -> str: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - relation.data[relation.app].get(KAFKA_HOST_APP_KEY) - if relation and relation.app - else None - ) - - @property - def port(self) -> int: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - int(relation.data[relation.app].get(KAFKA_PORT_APP_KEY)) - if relation and relation.app - else None - ) - - -class KafkaProvides(Object): - """Provides-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self._endpoint_name = endpoint_name - - def set_host_info(self, host: str, port: int, relation: Optional[Relation] = None) -> None: - """Set Kafka host and port. - - This function writes in the application data of the relation, therefore, - only the unit leader can call it. - - Args: - host (str): Kafka hostname or IP address. - port (int): Kafka port. - relation (Optional[Relation]): Relation to update. - If not specified, all relations will be updated. - - Raises: - Exception: if a non-leader unit calls this function. - """ - if not self.model.unit.is_leader(): - raise Exception("only the leader set host information.") - - if relation: - self._update_relation_data(host, port, relation) - return - - for relation in self.model.relations[self._endpoint_name]: - self._update_relation_data(host, port, relation) - - def _update_relation_data(self, host: str, port: int, relation: Relation) -> None: - """Update data in relation if needed.""" - relation.data[self.model.app][KAFKA_HOST_APP_KEY] = host - relation.data[self.model.app][KAFKA_PORT_APP_KEY] = str(port) diff --git a/installers/charm/nbi/metadata.yaml b/installers/charm/nbi/metadata.yaml deleted file mode 100644 index 381497b7..00000000 --- a/installers/charm/nbi/metadata.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -name: osm-nbi -summary: OSM Northbound Interface (NBI) -description: | - A CAAS charm to deploy OSM's Northbound Interface (NBI). -series: - - kubernetes -tags: - - kubernetes - - osm - - nbi -min-juju-version: 2.8.0 -deployment: - type: stateless - service: cluster -resources: - image: - type: oci-image - description: OSM docker image for NBI - upstream-source: "opensourcemano/nbi:latest" -requires: - kafka: - interface: kafka - limit: 1 - mongodb: - interface: mongodb - limit: 1 - keystone: - interface: keystone - limit: 1 - prometheus: - interface: prometheus - limit: 1 -provides: - nbi: - interface: http diff --git a/installers/charm/nbi/requirements-test.txt b/installers/charm/nbi/requirements-test.txt deleted file mode 100644 index 316f6d20..00000000 --- a/installers/charm/nbi/requirements-test.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net - -mock==4.0.3 diff --git a/installers/charm/nbi/requirements.txt b/installers/charm/nbi/requirements.txt deleted file mode 100644 index 8bb93ad3..00000000 --- a/installers/charm/nbi/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master diff --git a/installers/charm/nbi/src/charm.py b/installers/charm/nbi/src/charm.py deleted file mode 100755 index cb47d1ce..00000000 --- a/installers/charm/nbi/src/charm.py +++ /dev/null @@ -1,384 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -# pylint: disable=E0213 - - -from ipaddress import ip_network -import logging -from typing import NoReturn, Optional -from urllib.parse import urlparse - - -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.main import main -from opslib.osm.charm import CharmedOsmBase, RelationsMissing -from opslib.osm.interfaces.http import HttpServer -from opslib.osm.interfaces.keystone import KeystoneClient -from opslib.osm.interfaces.mongo import MongoClient -from opslib.osm.interfaces.prometheus import PrometheusClient -from opslib.osm.pod import ( - ContainerV3Builder, - IngressResourceV3Builder, - PodRestartPolicy, - PodSpecV3Builder, -) -from opslib.osm.validator import ModelValidator, validator - - -logger = logging.getLogger(__name__) - -PORT = 9999 - - -class ConfigModel(ModelValidator): - enable_test: bool - auth_backend: str - database_commonkey: str - log_level: str - max_file_size: int - site_url: Optional[str] - cluster_issuer: Optional[str] - ingress_class: Optional[str] - ingress_whitelist_source_range: Optional[str] - tls_secret_name: Optional[str] - mongodb_uri: Optional[str] - image_pull_policy: str - debug_mode: bool - security_context: bool - - @validator("auth_backend") - def validate_auth_backend(cls, v): - if v not in {"internal", "keystone"}: - raise ValueError("value must be 'internal' or 'keystone'") - return v - - @validator("log_level") - def validate_log_level(cls, v): - if v not in {"INFO", "DEBUG"}: - raise ValueError("value must be INFO or DEBUG") - return v - - @validator("max_file_size") - def validate_max_file_size(cls, v): - if v < 0: - raise ValueError("value must be equal or greater than 0") - return v - - @validator("site_url") - def validate_site_url(cls, v): - if v: - parsed = urlparse(v) - if not parsed.scheme.startswith("http"): - raise ValueError("value must start with http") - return v - - @validator("ingress_whitelist_source_range") - def validate_ingress_whitelist_source_range(cls, v): - if v: - ip_network(v) - return v - - @validator("mongodb_uri") - def validate_mongodb_uri(cls, v): - if v and not v.startswith("mongodb://"): - raise ValueError("mongodb_uri is not properly formed") - return v - - @validator("image_pull_policy") - def validate_image_pull_policy(cls, v): - values = { - "always": "Always", - "ifnotpresent": "IfNotPresent", - "never": "Never", - } - v = v.lower() - if v not in values.keys(): - raise ValueError("value must be always, ifnotpresent or never") - return values[v] - - -class NbiCharm(CharmedOsmBase): - on = KafkaEvents() - - def __init__(self, *args) -> NoReturn: - super().__init__( - *args, - oci_image="image", - vscode_workspace=VSCODE_WORKSPACE, - ) - if self.config.get("debug_mode"): - self.enable_debug_mode( - pubkey=self.config.get("debug_pubkey"), - hostpaths={ - "NBI": { - "hostpath": self.config.get("debug_nbi_local_path"), - "container-path": "/usr/lib/python3/dist-packages/osm_nbi", - }, - "osm_common": { - "hostpath": self.config.get("debug_common_local_path"), - "container-path": "/usr/lib/python3/dist-packages/osm_common", - }, - }, - ) - - self.kafka = KafkaRequires(self) - self.framework.observe(self.on.kafka_available, self.configure_pod) - self.framework.observe(self.on.kafka_broken, self.configure_pod) - - self.mongodb_client = MongoClient(self, "mongodb") - self.framework.observe(self.on["mongodb"].relation_changed, self.configure_pod) - self.framework.observe(self.on["mongodb"].relation_broken, self.configure_pod) - - self.prometheus_client = PrometheusClient(self, "prometheus") - self.framework.observe( - self.on["prometheus"].relation_changed, self.configure_pod - ) - self.framework.observe( - self.on["prometheus"].relation_broken, self.configure_pod - ) - - self.keystone_client = KeystoneClient(self, "keystone") - self.framework.observe(self.on["keystone"].relation_changed, self.configure_pod) - self.framework.observe(self.on["keystone"].relation_broken, self.configure_pod) - - self.http_server = HttpServer(self, "nbi") - self.framework.observe(self.on["nbi"].relation_joined, self._publish_nbi_info) - - def _publish_nbi_info(self, event): - """Publishes NBI information. - - Args: - event (EventBase): RO relation event. - """ - if self.unit.is_leader(): - self.http_server.publish_info(self.app.name, PORT) - - def _check_missing_dependencies(self, config: ConfigModel): - missing_relations = [] - - if not self.kafka.host or not self.kafka.port: - missing_relations.append("kafka") - if not config.mongodb_uri and self.mongodb_client.is_missing_data_in_unit(): - missing_relations.append("mongodb") - if self.prometheus_client.is_missing_data_in_app(): - missing_relations.append("prometheus") - if config.auth_backend == "keystone": - if self.keystone_client.is_missing_data_in_app(): - missing_relations.append("keystone") - - if missing_relations: - raise RelationsMissing(missing_relations) - - def build_pod_spec(self, image_info): - # Validate config - config = ConfigModel(**dict(self.config)) - - if config.mongodb_uri and not self.mongodb_client.is_missing_data_in_unit(): - raise Exception("Mongodb data cannot be provided via config and relation") - - # Check relations - self._check_missing_dependencies(config) - - security_context_enabled = ( - config.security_context if not config.debug_mode else False - ) - - # Create Builder for the PodSpec - pod_spec_builder = PodSpecV3Builder( - enable_security_context=security_context_enabled - ) - - # Add secrets to the pod - mongodb_secret_name = f"{self.app.name}-mongodb-secret" - pod_spec_builder.add_secret( - mongodb_secret_name, - { - "uri": config.mongodb_uri or self.mongodb_client.connection_string, - "commonkey": config.database_commonkey, - }, - ) - - # Build Init Container - pod_spec_builder.add_init_container( - { - "name": "init-check", - "image": "alpine:latest", - "command": [ - "sh", - "-c", - f"until (nc -zvw1 {self.kafka.host} {self.kafka.port} ); do sleep 3; done; exit 0", - ], - } - ) - - # Build Container - container_builder = ContainerV3Builder( - self.app.name, - image_info, - config.image_pull_policy, - run_as_non_root=security_context_enabled, - ) - container_builder.add_port(name=self.app.name, port=PORT) - container_builder.add_tcpsocket_readiness_probe( - PORT, - initial_delay_seconds=5, - timeout_seconds=5, - ) - container_builder.add_tcpsocket_liveness_probe( - PORT, - initial_delay_seconds=45, - timeout_seconds=10, - ) - container_builder.add_envs( - { - # General configuration - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMNBI_SERVER_ENABLE_TEST": config.enable_test, - "OSMNBI_STATIC_DIR": "/app/osm_nbi/html_public", - # Kafka configuration - "OSMNBI_MESSAGE_HOST": self.kafka.host, - "OSMNBI_MESSAGE_DRIVER": "kafka", - "OSMNBI_MESSAGE_PORT": self.kafka.port, - # Database configuration - "OSMNBI_DATABASE_DRIVER": "mongo", - # Storage configuration - "OSMNBI_STORAGE_DRIVER": "mongo", - "OSMNBI_STORAGE_PATH": "/app/storage", - "OSMNBI_STORAGE_COLLECTION": "files", - # Prometheus configuration - "OSMNBI_PROMETHEUS_HOST": self.prometheus_client.hostname, - "OSMNBI_PROMETHEUS_PORT": self.prometheus_client.port, - # Log configuration - "OSMNBI_LOG_LEVEL": config.log_level, - } - ) - container_builder.add_secret_envs( - secret_name=mongodb_secret_name, - envs={ - "OSMNBI_DATABASE_URI": "uri", - "OSMNBI_DATABASE_COMMONKEY": "commonkey", - "OSMNBI_STORAGE_URI": "uri", - }, - ) - if config.auth_backend == "internal": - container_builder.add_env("OSMNBI_AUTHENTICATION_BACKEND", "internal") - elif config.auth_backend == "keystone": - keystone_secret_name = f"{self.app.name}-keystone-secret" - pod_spec_builder.add_secret( - keystone_secret_name, - { - "url": self.keystone_client.host, - "port": self.keystone_client.port, - "user_domain": self.keystone_client.user_domain_name, - "project_domain": self.keystone_client.project_domain_name, - "service_username": self.keystone_client.username, - "service_password": self.keystone_client.password, - "service_project": self.keystone_client.service, - }, - ) - container_builder.add_env("OSMNBI_AUTHENTICATION_BACKEND", "keystone") - container_builder.add_secret_envs( - secret_name=keystone_secret_name, - envs={ - "OSMNBI_AUTHENTICATION_AUTH_URL": "url", - "OSMNBI_AUTHENTICATION_AUTH_PORT": "port", - "OSMNBI_AUTHENTICATION_USER_DOMAIN_NAME": "user_domain", - "OSMNBI_AUTHENTICATION_PROJECT_DOMAIN_NAME": "project_domain", - "OSMNBI_AUTHENTICATION_SERVICE_USERNAME": "service_username", - "OSMNBI_AUTHENTICATION_SERVICE_PASSWORD": "service_password", - "OSMNBI_AUTHENTICATION_SERVICE_PROJECT": "service_project", - }, - ) - container = container_builder.build() - - # Add container to pod spec - pod_spec_builder.add_container(container) - - # Add ingress resources to pod spec if site url exists - if config.site_url: - parsed = urlparse(config.site_url) - annotations = { - "nginx.ingress.kubernetes.io/proxy-body-size": "{}".format( - str(config.max_file_size) + "m" - if config.max_file_size > 0 - else config.max_file_size - ), - "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", - } - if config.ingress_class: - annotations["kubernetes.io/ingress.class"] = config.ingress_class - ingress_resource_builder = IngressResourceV3Builder( - f"{self.app.name}-ingress", annotations - ) - - if config.ingress_whitelist_source_range: - annotations[ - "nginx.ingress.kubernetes.io/whitelist-source-range" - ] = config.ingress_whitelist_source_range - - if config.cluster_issuer: - annotations["cert-manager.io/cluster-issuer"] = config.cluster_issuer - - if parsed.scheme == "https": - ingress_resource_builder.add_tls( - [parsed.hostname], config.tls_secret_name - ) - else: - annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false" - - ingress_resource_builder.add_rule(parsed.hostname, self.app.name, PORT) - ingress_resource = ingress_resource_builder.build() - pod_spec_builder.add_ingress_resource(ingress_resource) - - # Add restart policy - restart_policy = PodRestartPolicy() - restart_policy.add_secrets() - pod_spec_builder.set_restart_policy(restart_policy) - - return pod_spec_builder.build() - - -VSCODE_WORKSPACE = { - "folders": [ - {"path": "/usr/lib/python3/dist-packages/osm_nbi"}, - {"path": "/usr/lib/python3/dist-packages/osm_common"}, - {"path": "/usr/lib/python3/dist-packages/osm_im"}, - ], - "settings": {}, - "launch": { - "version": "0.2.0", - "configurations": [ - { - "name": "NBI", - "type": "python", - "request": "launch", - "module": "osm_nbi.nbi", - "justMyCode": False, - } - ], - }, -} - - -if __name__ == "__main__": - main(NbiCharm) diff --git a/installers/charm/nbi/src/pod_spec.py b/installers/charm/nbi/src/pod_spec.py deleted file mode 100644 index b8f5904d..00000000 --- a/installers/charm/nbi/src/pod_spec.py +++ /dev/null @@ -1,419 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -from ipaddress import ip_network -from typing import Any, Callable, Dict, List, NoReturn -from urllib.parse import urlparse - - -def _validate_max_file_size(max_file_size: int, site_url: str) -> bool: - """Validate max_file_size. - - Args: - max_file_size (int): maximum file size allowed. - site_url (str): endpoint url. - - Returns: - bool: True if valid, false otherwise. - """ - if not site_url: - return True - - parsed = urlparse(site_url) - - if not parsed.scheme.startswith("http"): - return True - - if max_file_size is None: - return False - - return max_file_size >= 0 - - -def _validate_ip_network(network: str) -> bool: - """Validate IP network. - - Args: - network (str): IP network range. - - Returns: - bool: True if valid, false otherwise. - """ - if not network: - return True - - try: - ip_network(network) - except ValueError: - return False - - return True - - -def _validate_keystone_config(keystone: bool, value: Any, validator: Callable) -> bool: - """Validate keystone configurations. - - Args: - keystone (bool): is keystone enabled, true if so, false otherwise. - value (Any): value to be validated. - validator (Callable): function to validate configuration. - - Returns: - bool: true if valid, false otherwise. - """ - if not keystone: - return True - - return validator(value) - - -def _validate_data( - config_data: Dict[str, Any], relation_data: Dict[str, Any], keystone: bool -) -> NoReturn: - """Validate input data. - - Args: - config_data (Dict[str, Any]): configuration data. - relation_data (Dict[str, Any]): relation data. - keystone (bool): is keystone to be used. - """ - config_validators = { - "enable_test": lambda value, _: isinstance(value, bool), - "database_commonkey": lambda value, _: ( - isinstance(value, str) and len(value) > 1 - ), - "log_level": lambda value, _: ( - isinstance(value, str) and value in ("INFO", "DEBUG") - ), - "auth_backend": lambda value, _: ( - isinstance(value, str) and (value == "internal" or value == "keystone") - ), - "site_url": lambda value, _: isinstance(value, str) - if value is not None - else True, - "max_file_size": lambda value, values: _validate_max_file_size( - value, values.get("site_url") - ), - "ingress_whitelist_source_range": lambda value, _: _validate_ip_network(value), - "tls_secret_name": lambda value, _: isinstance(value, str) - if value is not None - else True, - } - relation_validators = { - "message_host": lambda value, _: isinstance(value, str), - "message_port": lambda value, _: isinstance(value, int) and value > 0, - "database_uri": lambda value, _: ( - isinstance(value, str) and value.startswith("mongodb://") - ), - "prometheus_host": lambda value, _: isinstance(value, str), - "prometheus_port": lambda value, _: isinstance(value, int) and value > 0, - "keystone_host": lambda value, _: _validate_keystone_config( - keystone, value, lambda x: isinstance(x, str) and len(x) > 0 - ), - "keystone_port": lambda value, _: _validate_keystone_config( - keystone, value, lambda x: isinstance(x, int) and x > 0 - ), - "keystone_user_domain_name": lambda value, _: _validate_keystone_config( - keystone, value, lambda x: isinstance(x, str) and len(x) > 0 - ), - "keystone_project_domain_name": lambda value, _: _validate_keystone_config( - keystone, value, lambda x: isinstance(x, str) and len(x) > 0 - ), - "keystone_username": lambda value, _: _validate_keystone_config( - keystone, value, lambda x: isinstance(x, str) and len(x) > 0 - ), - "keystone_password": lambda value, _: _validate_keystone_config( - keystone, value, lambda x: isinstance(x, str) and len(x) > 0 - ), - "keystone_service": lambda value, _: _validate_keystone_config( - keystone, value, lambda x: isinstance(x, str) and len(x) > 0 - ), - } - problems = [] - - for key, validator in config_validators.items(): - valid = validator(config_data.get(key), config_data) - - if not valid: - problems.append(key) - - for key, validator in relation_validators.items(): - valid = validator(relation_data.get(key), relation_data) - - if not valid: - problems.append(key) - - if len(problems) > 0: - raise ValueError("Errors found in: {}".format(", ".join(problems))) - - -def _make_pod_ports(port: int) -> List[Dict[str, Any]]: - """Generate pod ports details. - - Args: - port (int): port to expose. - - Returns: - List[Dict[str, Any]]: pod port details. - """ - return [{"name": "nbi", "containerPort": port, "protocol": "TCP"}] - - -def _make_pod_envconfig( - config: Dict[str, Any], relation_state: Dict[str, Any] -) -> Dict[str, Any]: - """Generate pod environment configuration. - - Args: - config (Dict[str, Any]): configuration information. - relation_state (Dict[str, Any]): relation state information. - - Returns: - Dict[str, Any]: pod environment configuration. - """ - envconfig = { - # General configuration - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMNBI_SERVER_ENABLE_TEST": config["enable_test"], - "OSMNBI_STATIC_DIR": "/app/osm_nbi/html_public", - # Kafka configuration - "OSMNBI_MESSAGE_HOST": relation_state["message_host"], - "OSMNBI_MESSAGE_DRIVER": "kafka", - "OSMNBI_MESSAGE_PORT": relation_state["message_port"], - # Database configuration - "OSMNBI_DATABASE_DRIVER": "mongo", - "OSMNBI_DATABASE_URI": relation_state["database_uri"], - "OSMNBI_DATABASE_COMMONKEY": config["database_commonkey"], - # Storage configuration - "OSMNBI_STORAGE_DRIVER": "mongo", - "OSMNBI_STORAGE_PATH": "/app/storage", - "OSMNBI_STORAGE_COLLECTION": "files", - "OSMNBI_STORAGE_URI": relation_state["database_uri"], - # Prometheus configuration - "OSMNBI_PROMETHEUS_HOST": relation_state["prometheus_host"], - "OSMNBI_PROMETHEUS_PORT": relation_state["prometheus_port"], - # Log configuration - "OSMNBI_LOG_LEVEL": config["log_level"], - } - - if config["auth_backend"] == "internal": - envconfig["OSMNBI_AUTHENTICATION_BACKEND"] = "internal" - elif config["auth_backend"] == "keystone": - envconfig.update( - { - "OSMNBI_AUTHENTICATION_BACKEND": "keystone", - "OSMNBI_AUTHENTICATION_AUTH_URL": relation_state["keystone_host"], - "OSMNBI_AUTHENTICATION_AUTH_PORT": relation_state["keystone_port"], - "OSMNBI_AUTHENTICATION_USER_DOMAIN_NAME": relation_state[ - "keystone_user_domain_name" - ], - "OSMNBI_AUTHENTICATION_PROJECT_DOMAIN_NAME": relation_state[ - "keystone_project_domain_name" - ], - "OSMNBI_AUTHENTICATION_SERVICE_USERNAME": relation_state[ - "keystone_username" - ], - "OSMNBI_AUTHENTICATION_SERVICE_PASSWORD": relation_state[ - "keystone_password" - ], - "OSMNBI_AUTHENTICATION_SERVICE_PROJECT": relation_state[ - "keystone_service" - ], - } - ) - else: - raise ValueError("auth_backend needs to be either internal or keystone") - - return envconfig - - -def _make_pod_ingress_resources( - config: Dict[str, Any], app_name: str, port: int -) -> List[Dict[str, Any]]: - """Generate pod ingress resources. - - Args: - config (Dict[str, Any]): configuration information. - app_name (str): application name. - port (int): port to expose. - - Returns: - List[Dict[str, Any]]: pod ingress resources. - """ - site_url = config.get("site_url") - - if not site_url: - return - - parsed = urlparse(site_url) - - if not parsed.scheme.startswith("http"): - return - - max_file_size = config["max_file_size"] - ingress_whitelist_source_range = config["ingress_whitelist_source_range"] - - annotations = { - "nginx.ingress.kubernetes.io/proxy-body-size": "{}".format( - str(max_file_size) + "m" if max_file_size > 0 else max_file_size - ), - "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", - } - - 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 = 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(app_name), - "annotations": annotations, - "spec": { - "rules": [ - { - "host": parsed.hostname, - "http": { - "paths": [ - { - "path": "/", - "backend": { - "serviceName": app_name, - "servicePort": port, - }, - } - ] - }, - } - ] - }, - } - if ingress_spec_tls: - ingress["spec"]["tls"] = ingress_spec_tls - - return [ingress] - - -def _make_startup_probe() -> Dict[str, Any]: - """Generate startup probe. - - Returns: - Dict[str, Any]: startup probe. - """ - return { - "exec": {"command": ["/usr/bin/pgrep python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - -def _make_readiness_probe(port: int) -> Dict[str, Any]: - """Generate readiness probe. - - Args: - port (int): [description] - - Returns: - Dict[str, Any]: readiness probe. - """ - return { - "httpGet": { - "path": "/osm/", - "port": port, - }, - "initialDelaySeconds": 45, - "timeoutSeconds": 5, - } - - -def _make_liveness_probe(port: int) -> Dict[str, Any]: - """Generate liveness probe. - - Args: - port (int): [description] - - Returns: - Dict[str, Any]: liveness probe. - """ - return { - "httpGet": { - "path": "/osm/", - "port": port, - }, - "initialDelaySeconds": 45, - "timeoutSeconds": 5, - } - - -def make_pod_spec( - image_info: Dict[str, str], - config: Dict[str, Any], - relation_state: Dict[str, Any], - app_name: str = "nbi", - port: int = 9999, -) -> Dict[str, Any]: - """Generate the pod spec information. - - Args: - image_info (Dict[str, str]): Object provided by - OCIImageResource("image").fetch(). - config (Dict[str, Any]): Configuration information. - relation_state (Dict[str, Any]): Relation state information. - app_name (str, optional): Application name. Defaults to "nbi". - port (int, optional): Port for the container. Defaults to 9999. - - Returns: - Dict[str, Any]: Pod spec dictionary for the charm. - """ - if not image_info: - return None - - _validate_data(config, relation_state, config.get("auth_backend") == "keystone") - - ports = _make_pod_ports(port) - env_config = _make_pod_envconfig(config, relation_state) - ingress_resources = _make_pod_ingress_resources(config, app_name, port) - - return { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": ports, - "envConfig": env_config, - } - ], - "kubernetesResources": { - "ingressResources": ingress_resources or [], - }, - } diff --git a/installers/charm/nbi/tests/__init__.py b/installers/charm/nbi/tests/__init__.py deleted file mode 100644 index 446d5cee..00000000 --- a/installers/charm/nbi/tests/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -"""Init mocking for unit tests.""" - -import sys - - -import mock - - -class OCIImageResourceErrorMock(Exception): - pass - - -sys.path.append("src") - -oci_image = mock.MagicMock() -oci_image.OCIImageResourceError = OCIImageResourceErrorMock -sys.modules["oci_image"] = oci_image -sys.modules["oci_image"].OCIImageResource().fetch.return_value = {} diff --git a/installers/charm/nbi/tests/test_charm.py b/installers/charm/nbi/tests/test_charm.py deleted file mode 100644 index 92c29808..00000000 --- a/installers/charm/nbi/tests/test_charm.py +++ /dev/null @@ -1,295 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import sys -from typing import NoReturn -import unittest - - -from charm import NbiCharm -from ops.model import ActiveStatus, BlockedStatus -from ops.testing import Harness - - -class TestCharm(unittest.TestCase): - """Prometheus Charm unit tests.""" - - def setUp(self) -> NoReturn: - """Test setup""" - self.image_info = sys.modules["oci_image"].OCIImageResource().fetch() - self.harness = Harness(NbiCharm) - self.harness.set_leader(is_leader=True) - self.harness.begin() - self.config = { - "enable_test": False, - "auth_backend": "internal", - "database_commonkey": "key", - "mongodb_uri": "", - "log_level": "INFO", - "max_file_size": 0, - "ingress_whitelist_source_range": "", - "tls_secret_name": "", - "site_url": "https://nbi.192.168.100.100.nip.io", - "cluster_issuer": "vault-issuer", - } - self.harness.update_config(self.config) - - def test_config_changed_no_relations( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue( - all( - relation in self.harness.charm.unit.status.message - for relation in ["mongodb", "kafka", "prometheus"] - ) - ) - - def test_config_changed_non_leader( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - self.harness.set_leader(is_leader=False) - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus) - - def test_with_relations_internal_and_mongodb_config( - self, - ) -> NoReturn: - "Test with relations and mongodb config (internal)" - self.initialize_kafka_relation() - self.initialize_mongo_config() - self.initialize_prometheus_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_with_relations_internal( - self, - ) -> NoReturn: - "Test with relations (internal)" - self.initialize_kafka_relation() - self.initialize_mongo_relation() - self.initialize_prometheus_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_with_relations_and_mongodb_config_with_keystone_missing( - self, - ) -> NoReturn: - "Test with relations and mongodb config (keystone)" - self.harness.update_config({"auth_backend": "keystone"}) - self.initialize_kafka_relation() - self.initialize_mongo_config() - self.initialize_prometheus_relation() - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue("keystone" in self.harness.charm.unit.status.message) - - def test_with_relations_keystone_missing( - self, - ) -> NoReturn: - "Test with relations (keystone)" - self.harness.update_config({"auth_backend": "keystone"}) - self.initialize_kafka_relation() - self.initialize_mongo_relation() - self.initialize_prometheus_relation() - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue("keystone" in self.harness.charm.unit.status.message) - - def test_with_relations_and_mongodb_config_with_keystone( - self, - ) -> NoReturn: - "Test with relations (keystone)" - self.harness.update_config({"auth_backend": "keystone"}) - self.initialize_kafka_relation() - self.initialize_mongo_config() - self.initialize_prometheus_relation() - self.initialize_keystone_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_with_relations_keystone( - self, - ) -> NoReturn: - "Test with relations (keystone)" - self.harness.update_config({"auth_backend": "keystone"}) - self.initialize_kafka_relation() - self.initialize_mongo_relation() - self.initialize_prometheus_relation() - self.initialize_keystone_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_mongodb_exception_relation_and_config( - self, - ) -> NoReturn: - self.initialize_mongo_config() - self.initialize_mongo_relation() - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def initialize_kafka_relation(self): - kafka_relation_id = self.harness.add_relation("kafka", "kafka") - self.harness.add_relation_unit(kafka_relation_id, "kafka/0") - self.harness.update_relation_data( - kafka_relation_id, "kafka", {"host": "kafka", "port": 9092} - ) - - def initialize_mongo_config(self): - self.harness.update_config({"mongodb_uri": "mongodb://mongo:27017"}) - - def initialize_mongo_relation(self): - mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") - self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") - self.harness.update_relation_data( - mongodb_relation_id, - "mongodb/0", - {"connection_string": "mongodb://mongo:27017"}, - ) - - def initialize_keystone_relation(self): - keystone_relation_id = self.harness.add_relation("keystone", "keystone") - self.harness.add_relation_unit(keystone_relation_id, "keystone/0") - self.harness.update_relation_data( - keystone_relation_id, - "keystone", - { - "host": "host", - "port": 5000, - "user_domain_name": "ud", - "project_domain_name": "pd", - "username": "u", - "password": "p", - "service": "s", - "keystone_db_password": "something", - "region_id": "something", - "admin_username": "something", - "admin_password": "something", - "admin_project_name": "something", - }, - ) - - def initialize_prometheus_relation(self): - prometheus_relation_id = self.harness.add_relation("prometheus", "prometheus") - self.harness.add_relation_unit(prometheus_relation_id, "prometheus/0") - self.harness.update_relation_data( - prometheus_relation_id, - "prometheus", - {"hostname": "prometheus", "port": 9090}, - ) - - -if __name__ == "__main__": - unittest.main() - - -# class TestCharm(unittest.TestCase): -# """Prometheus Charm unit tests.""" - -# def setUp(self) -> NoReturn: -# """Test setup""" -# self.image_info = sys.modules["oci_image"].OCIImageResource().fetch() -# self.harness = Harness(NbiCharm) -# self.harness.set_leader(is_leader=True) -# self.harness.begin() -# self.config = { -# "enable_ng_ro": True, -# "database_commonkey": "commonkey", -# "log_level": "INFO", -# "vim_database": "db_name", -# "ro_database": "ro_db_name", -# "openmano_tenant": "mano", -# } - -# def test_config_changed_no_relations( -# self, -# ) -> NoReturn: -# """Test ingress resources without HTTP.""" - -# self.harness.charm.on.config_changed.emit() - -# # Assertions -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) -# self.assertTrue( -# all( -# relation in self.harness.charm.unit.status.message -# for relation in ["mongodb", "kafka"] -# ) -# ) - -# # Disable ng-ro -# self.harness.update_config({"enable_ng_ro": False}) -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) -# self.assertTrue( -# all( -# relation in self.harness.charm.unit.status.message -# for relation in ["mysql"] -# ) -# ) - -# def test_config_changed_non_leader( -# self, -# ) -> NoReturn: -# """Test ingress resources without HTTP.""" -# self.harness.set_leader(is_leader=False) -# self.harness.charm.on.config_changed.emit() - -# # Assertions -# self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus) - -# def test_with_relations_ng( -# self, -# ) -> NoReturn: -# "Test with relations (ng-ro)" - -# # Initializing the kafka relation -# kafka_relation_id = self.harness.add_relation("kafka", "kafka") -# self.harness.add_relation_unit(kafka_relation_id, "kafka/0") -# self.harness.update_relation_data( -# kafka_relation_id, "kafka/0", {"host": "kafka", "port": 9092} -# ) - -# # Initializing the mongo relation -# mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") -# self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") -# self.harness.update_relation_data( -# mongodb_relation_id, -# "mongodb/0", -# {"connection_string": "mongodb://mongo:27017"}, -# ) - -# self.harness.charm.on.config_changed.emit() - -# # Verifying status -# self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - -# if __name__ == "__main__": -# unittest.main() diff --git a/installers/charm/nbi/tests/test_pod_spec.py b/installers/charm/nbi/tests/test_pod_spec.py deleted file mode 100644 index 360895f0..00000000 --- a/installers/charm/nbi/tests/test_pod_spec.py +++ /dev/null @@ -1,647 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -from typing import NoReturn -import unittest - -import pod_spec - - -class TestPodSpec(unittest.TestCase): - """Pod spec unit tests.""" - - def test_make_pod_ports(self) -> NoReturn: - """Testing make pod ports.""" - port = 9999 - - expected_result = [ - { - "name": "nbi", - "containerPort": port, - "protocol": "TCP", - } - ] - - pod_ports = pod_spec._make_pod_ports(port) - - self.assertListEqual(expected_result, pod_ports) - - def test_make_pod_envconfig_without_keystone(self) -> NoReturn: - """Teting make pod envconfig without Keystone.""" - config = { - "enable_test": False, - "database_commonkey": "commonkey", - "log_level": "DEBUG", - "auth_backend": "internal", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - } - - expected_result = { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMNBI_SERVER_ENABLE_TEST": config["enable_test"], - "OSMNBI_STATIC_DIR": "/app/osm_nbi/html_public", - "OSMNBI_MESSAGE_HOST": relation_state["message_host"], - "OSMNBI_MESSAGE_DRIVER": "kafka", - "OSMNBI_MESSAGE_PORT": relation_state["message_port"], - "OSMNBI_DATABASE_DRIVER": "mongo", - "OSMNBI_DATABASE_URI": relation_state["database_uri"], - "OSMNBI_DATABASE_COMMONKEY": config["database_commonkey"], - "OSMNBI_STORAGE_DRIVER": "mongo", - "OSMNBI_STORAGE_PATH": "/app/storage", - "OSMNBI_STORAGE_COLLECTION": "files", - "OSMNBI_STORAGE_URI": relation_state["database_uri"], - "OSMNBI_PROMETHEUS_HOST": relation_state["prometheus_host"], - "OSMNBI_PROMETHEUS_PORT": relation_state["prometheus_port"], - "OSMNBI_LOG_LEVEL": config["log_level"], - "OSMNBI_AUTHENTICATION_BACKEND": config["auth_backend"], - } - - pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state) - - self.assertDictEqual(expected_result, pod_envconfig) - - def test_make_pod_envconfig_with_keystone(self) -> NoReturn: - """Teting make pod envconfig with Keystone.""" - config = { - "enable_test": False, - "database_commonkey": "commonkey", - "log_level": "DEBUG", - "auth_backend": "keystone", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - "keystone_host": "keystone", - "keystone_port": 5000, - "keystone_user_domain_name": "user_domain", - "keystone_project_domain_name": "project_domain", - "keystone_username": "username", - "keystone_password": "password", - "keystone_service": "service", - } - - expected_result = { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMNBI_SERVER_ENABLE_TEST": config["enable_test"], - "OSMNBI_STATIC_DIR": "/app/osm_nbi/html_public", - "OSMNBI_MESSAGE_HOST": relation_state["message_host"], - "OSMNBI_MESSAGE_DRIVER": "kafka", - "OSMNBI_MESSAGE_PORT": relation_state["message_port"], - "OSMNBI_DATABASE_DRIVER": "mongo", - "OSMNBI_DATABASE_URI": relation_state["database_uri"], - "OSMNBI_DATABASE_COMMONKEY": config["database_commonkey"], - "OSMNBI_STORAGE_DRIVER": "mongo", - "OSMNBI_STORAGE_PATH": "/app/storage", - "OSMNBI_STORAGE_COLLECTION": "files", - "OSMNBI_STORAGE_URI": relation_state["database_uri"], - "OSMNBI_PROMETHEUS_HOST": relation_state["prometheus_host"], - "OSMNBI_PROMETHEUS_PORT": relation_state["prometheus_port"], - "OSMNBI_LOG_LEVEL": config["log_level"], - "OSMNBI_AUTHENTICATION_BACKEND": config["auth_backend"], - "OSMNBI_AUTHENTICATION_AUTH_URL": relation_state["keystone_host"], - "OSMNBI_AUTHENTICATION_AUTH_PORT": relation_state["keystone_port"], - "OSMNBI_AUTHENTICATION_USER_DOMAIN_NAME": relation_state[ - "keystone_user_domain_name" - ], - "OSMNBI_AUTHENTICATION_PROJECT_DOMAIN_NAME": relation_state[ - "keystone_project_domain_name" - ], - "OSMNBI_AUTHENTICATION_SERVICE_USERNAME": relation_state[ - "keystone_username" - ], - "OSMNBI_AUTHENTICATION_SERVICE_PASSWORD": relation_state[ - "keystone_password" - ], - "OSMNBI_AUTHENTICATION_SERVICE_PROJECT": relation_state["keystone_service"], - } - - pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state) - - self.assertDictEqual(expected_result, pod_envconfig) - - def test_make_pod_envconfig_wrong_auth_backend(self) -> NoReturn: - """Teting make pod envconfig with wrong auth_backend.""" - config = { - "enable_test": False, - "database_commonkey": "commonkey", - "log_level": "DEBUG", - "auth_backend": "kerberos", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - "keystone_host": "keystone", - "keystone_port": 5000, - "keystone_user_domain_name": "user_domain", - "keystone_project_domain_name": "project_domain", - "keystone_username": "username", - "keystone_password": "password", - "keystone_service": "service", - } - - with self.assertRaises(ValueError) as exc: - pod_spec._make_pod_envconfig(config, relation_state) - - self.assertTrue( - "auth_backend needs to be either internal or keystone" in str(exc.exception) - ) - - def test_make_pod_ingress_resources_without_site_url(self) -> NoReturn: - """Testing make pod ingress resources without site_url.""" - config = {"site_url": ""} - app_name = "nbi" - port = 9999 - - pod_ingress_resources = pod_spec._make_pod_ingress_resources( - config, app_name, port - ) - - self.assertIsNone(pod_ingress_resources) - - def test_make_pod_ingress_resources(self) -> NoReturn: - """Testing make pod ingress resources.""" - config = { - "site_url": "http://nbi", - "max_file_size": 0, - "ingress_whitelist_source_range": "", - } - app_name = "nbi" - port = 9999 - - expected_result = [ - { - "name": f"{app_name}-ingress", - "annotations": { - "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}", - "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", - "nginx.ingress.kubernetes.io/ssl-redirect": "false", - }, - "spec": { - "rules": [ - { - "host": app_name, - "http": { - "paths": [ - { - "path": "/", - "backend": { - "serviceName": app_name, - "servicePort": port, - }, - } - ] - }, - } - ] - }, - } - ] - - pod_ingress_resources = pod_spec._make_pod_ingress_resources( - config, app_name, port - ) - - self.assertListEqual(expected_result, pod_ingress_resources) - - def test_make_pod_ingress_resources_with_whitelist_source_range(self) -> NoReturn: - """Testing make pod ingress resources with whitelist_source_range.""" - config = { - "site_url": "http://nbi", - "max_file_size": 0, - "ingress_whitelist_source_range": "0.0.0.0/0", - } - app_name = "nbi" - port = 9999 - - expected_result = [ - { - "name": f"{app_name}-ingress", - "annotations": { - "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}", - "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", - "nginx.ingress.kubernetes.io/ssl-redirect": "false", - "nginx.ingress.kubernetes.io/whitelist-source-range": config[ - "ingress_whitelist_source_range" - ], - }, - "spec": { - "rules": [ - { - "host": app_name, - "http": { - "paths": [ - { - "path": "/", - "backend": { - "serviceName": app_name, - "servicePort": port, - }, - } - ] - }, - } - ] - }, - } - ] - - pod_ingress_resources = pod_spec._make_pod_ingress_resources( - config, app_name, port - ) - - self.assertListEqual(expected_result, pod_ingress_resources) - - def test_make_pod_ingress_resources_with_https(self) -> NoReturn: - """Testing make pod ingress resources with HTTPs.""" - config = { - "site_url": "https://nbi", - "max_file_size": 0, - "ingress_whitelist_source_range": "", - "tls_secret_name": "", - } - app_name = "nbi" - port = 9999 - - expected_result = [ - { - "name": f"{app_name}-ingress", - "annotations": { - "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}", - "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", - }, - "spec": { - "rules": [ - { - "host": app_name, - "http": { - "paths": [ - { - "path": "/", - "backend": { - "serviceName": app_name, - "servicePort": port, - }, - } - ] - }, - } - ], - "tls": [{"hosts": [app_name]}], - }, - } - ] - - pod_ingress_resources = pod_spec._make_pod_ingress_resources( - config, app_name, port - ) - - self.assertListEqual(expected_result, pod_ingress_resources) - - def test_make_pod_ingress_resources_with_https_tls_secret_name(self) -> NoReturn: - """Testing make pod ingress resources with HTTPs and TLS secret name.""" - config = { - "site_url": "https://nbi", - "max_file_size": 0, - "ingress_whitelist_source_range": "", - "tls_secret_name": "secret_name", - } - app_name = "nbi" - port = 9999 - - expected_result = [ - { - "name": f"{app_name}-ingress", - "annotations": { - "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}", - "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", - }, - "spec": { - "rules": [ - { - "host": app_name, - "http": { - "paths": [ - { - "path": "/", - "backend": { - "serviceName": app_name, - "servicePort": port, - }, - } - ] - }, - } - ], - "tls": [ - {"hosts": [app_name], "secretName": config["tls_secret_name"]} - ], - }, - } - ] - - pod_ingress_resources = pod_spec._make_pod_ingress_resources( - config, app_name, port - ) - - self.assertListEqual(expected_result, pod_ingress_resources) - - def test_make_startup_probe(self) -> NoReturn: - """Testing make startup probe.""" - expected_result = { - "exec": {"command": ["/usr/bin/pgrep python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - startup_probe = pod_spec._make_startup_probe() - - self.assertDictEqual(expected_result, startup_probe) - - def test_make_readiness_probe(self) -> NoReturn: - """Testing make readiness probe.""" - port = 9999 - - expected_result = { - "httpGet": { - "path": "/osm/", - "port": port, - }, - "initialDelaySeconds": 45, - "timeoutSeconds": 5, - } - - readiness_probe = pod_spec._make_readiness_probe(port) - - self.assertDictEqual(expected_result, readiness_probe) - - def test_make_liveness_probe(self) -> NoReturn: - """Testing make liveness probe.""" - port = 9999 - - expected_result = { - "httpGet": { - "path": "/osm/", - "port": port, - }, - "initialDelaySeconds": 45, - "timeoutSeconds": 5, - } - - liveness_probe = pod_spec._make_liveness_probe(port) - - self.assertDictEqual(expected_result, liveness_probe) - - def test_make_pod_spec_without_image_info(self) -> NoReturn: - """Testing make pod spec without image_info.""" - image_info = None - config = { - "enable_test": False, - "database_commonkey": "commonkey", - "log_level": "DEBUG", - "auth_backend": "internal", - "site_url": "", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - } - app_name = "nbi" - port = 9999 - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertIsNone(spec) - - def test_make_pod_spec_without_config(self) -> NoReturn: - """Testing make pod spec without config.""" - image_info = {"upstream-source": "opensourcemano/nbi:8"} - config = {} - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - } - app_name = "nbi" - port = 9999 - - with self.assertRaises(ValueError): - pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port) - - def test_make_pod_spec_without_relation_state(self) -> NoReturn: - """Testing make pod spec without relation_state.""" - image_info = {"upstream-source": "opensourcemano/nbi:8"} - config = { - "enable_test": False, - "database_commonkey": "commonkey", - "log_level": "DEBUG", - "auth_backend": "internal", - "site_url": "", - } - relation_state = {} - app_name = "nbi" - port = 9999 - - with self.assertRaises(ValueError): - pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port) - - def test_make_pod_spec(self) -> NoReturn: - """Testing make pod spec.""" - image_info = {"upstream-source": "opensourcemano/nbi:8"} - config = { - "enable_test": False, - "database_commonkey": "commonkey", - "log_level": "DEBUG", - "auth_backend": "internal", - "site_url": "", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - } - app_name = "nbi" - port = 9999 - - expected_result = { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": [ - { - "name": "nbi", - "containerPort": port, - "protocol": "TCP", - } - ], - "envConfig": { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMNBI_SERVER_ENABLE_TEST": config["enable_test"], - "OSMNBI_STATIC_DIR": "/app/osm_nbi/html_public", - "OSMNBI_MESSAGE_HOST": relation_state["message_host"], - "OSMNBI_MESSAGE_DRIVER": "kafka", - "OSMNBI_MESSAGE_PORT": relation_state["message_port"], - "OSMNBI_DATABASE_DRIVER": "mongo", - "OSMNBI_DATABASE_URI": relation_state["database_uri"], - "OSMNBI_DATABASE_COMMONKEY": config["database_commonkey"], - "OSMNBI_STORAGE_DRIVER": "mongo", - "OSMNBI_STORAGE_PATH": "/app/storage", - "OSMNBI_STORAGE_COLLECTION": "files", - "OSMNBI_STORAGE_URI": relation_state["database_uri"], - "OSMNBI_PROMETHEUS_HOST": relation_state["prometheus_host"], - "OSMNBI_PROMETHEUS_PORT": relation_state["prometheus_port"], - "OSMNBI_LOG_LEVEL": config["log_level"], - "OSMNBI_AUTHENTICATION_BACKEND": config["auth_backend"], - }, - } - ], - "kubernetesResources": { - "ingressResources": [], - }, - } - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertDictEqual(expected_result, spec) - - def test_make_pod_spec_with_keystone(self) -> NoReturn: - """Testing make pod spec with keystone.""" - image_info = {"upstream-source": "opensourcemano/nbi:8"} - config = { - "enable_test": False, - "database_commonkey": "commonkey", - "log_level": "DEBUG", - "auth_backend": "keystone", - "site_url": "", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - "prometheus_host": "prometheus", - "prometheus_port": 9082, - "keystone_host": "keystone", - "keystone_port": 5000, - "keystone_user_domain_name": "user_domain", - "keystone_project_domain_name": "project_domain", - "keystone_username": "username", - "keystone_password": "password", - "keystone_service": "service", - } - app_name = "nbi" - port = 9999 - - expected_result = { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": [ - { - "name": "nbi", - "containerPort": port, - "protocol": "TCP", - } - ], - "envConfig": { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMNBI_SERVER_ENABLE_TEST": config["enable_test"], - "OSMNBI_STATIC_DIR": "/app/osm_nbi/html_public", - "OSMNBI_MESSAGE_HOST": relation_state["message_host"], - "OSMNBI_MESSAGE_DRIVER": "kafka", - "OSMNBI_MESSAGE_PORT": relation_state["message_port"], - "OSMNBI_DATABASE_DRIVER": "mongo", - "OSMNBI_DATABASE_URI": relation_state["database_uri"], - "OSMNBI_DATABASE_COMMONKEY": config["database_commonkey"], - "OSMNBI_STORAGE_DRIVER": "mongo", - "OSMNBI_STORAGE_PATH": "/app/storage", - "OSMNBI_STORAGE_COLLECTION": "files", - "OSMNBI_STORAGE_URI": relation_state["database_uri"], - "OSMNBI_PROMETHEUS_HOST": relation_state["prometheus_host"], - "OSMNBI_PROMETHEUS_PORT": relation_state["prometheus_port"], - "OSMNBI_LOG_LEVEL": config["log_level"], - "OSMNBI_AUTHENTICATION_BACKEND": config["auth_backend"], - "OSMNBI_AUTHENTICATION_AUTH_URL": relation_state[ - "keystone_host" - ], - "OSMNBI_AUTHENTICATION_AUTH_PORT": relation_state[ - "keystone_port" - ], - "OSMNBI_AUTHENTICATION_USER_DOMAIN_NAME": relation_state[ - "keystone_user_domain_name" - ], - "OSMNBI_AUTHENTICATION_PROJECT_DOMAIN_NAME": relation_state[ - "keystone_project_domain_name" - ], - "OSMNBI_AUTHENTICATION_SERVICE_USERNAME": relation_state[ - "keystone_username" - ], - "OSMNBI_AUTHENTICATION_SERVICE_PASSWORD": relation_state[ - "keystone_password" - ], - "OSMNBI_AUTHENTICATION_SERVICE_PROJECT": relation_state[ - "keystone_service" - ], - }, - } - ], - "kubernetesResources": { - "ingressResources": [], - }, - } - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertDictEqual(expected_result, spec) - - -if __name__ == "__main__": - unittest.main() diff --git a/installers/charm/nbi/tox.ini b/installers/charm/nbi/tox.ini deleted file mode 100644 index f3c91440..00000000 --- a/installers/charm/nbi/tox.ini +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## -####################################################################################### - -[tox] -envlist = black, cover, flake8, pylint, yamllint, safety -skipsdist = true - -[tox:jenkins] -toxworkdir = /tmp/.tox - -[testenv] -basepython = python3.8 -setenv = - VIRTUAL_ENV={envdir} - PYTHONPATH = {toxinidir}:{toxinidir}/lib:{toxinidir}/src - PYTHONDONTWRITEBYTECODE = 1 -deps = -r{toxinidir}/requirements.txt - - -####################################################################################### -[testenv:black] -deps = black -commands = - black --check --diff src/ tests/ - - -####################################################################################### -[testenv:cover] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - coverage - nose2 -commands = - sh -c 'rm -f nosetests.xml' - coverage erase - nose2 -C --coverage src - coverage report --omit='*tests*' - coverage html -d ./cover --omit='*tests*' - coverage xml -o coverage.xml --omit=*tests* -whitelist_externals = sh - - -####################################################################################### -[testenv:flake8] -deps = flake8 - flake8-import-order -commands = - flake8 src/ tests/ - - -####################################################################################### -[testenv:pylint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - pylint==2.10.2 -commands = - pylint -E src/ tests/ - - -####################################################################################### -[testenv:safety] -setenv = - LC_ALL=C.UTF-8 - LANG=C.UTF-8 -deps = {[testenv]deps} - safety -commands = - - safety check --full-report - - -####################################################################################### -[testenv:yamllint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - yamllint -commands = yamllint . - -####################################################################################### -[testenv:build] -passenv=HTTP_PROXY HTTPS_PROXY NO_PROXY -whitelist_externals = - charmcraft - sh -commands = - charmcraft pack - sh -c 'ubuntu_version=20.04; \ - architectures="amd64-aarch64-arm64"; \ - charm_name=`cat metadata.yaml | grep -E "^name: " | cut -f 2 -d " "`; \ - mv $charm_name"_ubuntu-"$ubuntu_version-$architectures.charm $charm_name.charm' - -####################################################################################### -[flake8] -ignore = - W291, - W293, - W503, - E123, - E125, - E226, - E241, -exclude = - .git, - __pycache__, - .tox, -max-line-length = 120 -show-source = True -builtins = _ -max-complexity = 10 -import-order-style = google diff --git a/installers/charm/ng-ui/.gitignore b/installers/charm/ng-ui/.gitignore deleted file mode 100644 index 493739ef..00000000 --- a/installers/charm/ng-ui/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.stestr -cover -release diff --git a/installers/charm/ng-ui/.jujuignore b/installers/charm/ng-ui/.jujuignore deleted file mode 100644 index 3ae3e7dc..00000000 --- a/installers/charm/ng-ui/.jujuignore +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.gitignore -.stestr -cover -release -tests/ -requirements* -tox.ini diff --git a/installers/charm/ng-ui/.yamllint.yaml b/installers/charm/ng-ui/.yamllint.yaml deleted file mode 100644 index d71fb69f..00000000 --- a/installers/charm/ng-ui/.yamllint.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - ---- -extends: default - -yaml-files: - - "*.yaml" - - "*.yml" - - ".yamllint" -ignore: | - .tox - cover/ - build/ - venv - release/ diff --git a/installers/charm/ng-ui/README.md b/installers/charm/ng-ui/README.md deleted file mode 100644 index 9b77b5d5..00000000 --- a/installers/charm/ng-ui/README.md +++ /dev/null @@ -1,47 +0,0 @@ - - -# NG-UI Charm - -## How to deploy - -```bash -juju deploy . # cs:~charmed-osm/ng-ui --channel edge -juju relate ng-ui nbi -``` - -## How to expose the NG-UI through ingress - -```bash -juju config ng-ui site_url=ng..xip.io -juju expose ng-ui -``` - -> Note: The is the IP of the K8s worker node. With microk8s, you can see the IP with `microk8s.config`. It is usually the IP of your host machine. - -## How to scale - -```bash - juju scale-application ng-ui 3 -``` - - -## Config Examples - -```bash -juju config ng-ui image=opensourcemano/ng-ui: -juju config ng-ui port=80 -juju config server_name= -juju config max_file_size=25 -``` diff --git a/installers/charm/ng-ui/charmcraft.yaml b/installers/charm/ng-ui/charmcraft.yaml deleted file mode 100644 index 0a285a9d..00000000 --- a/installers/charm/ng-ui/charmcraft.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -type: charm -bases: - - build-on: - - name: ubuntu - channel: "20.04" - architectures: ["amd64"] - run-on: - - name: ubuntu - channel: "20.04" - architectures: - - amd64 - - aarch64 - - arm64 -parts: - charm: - build-packages: [git] diff --git a/installers/charm/ng-ui/config.yaml b/installers/charm/ng-ui/config.yaml deleted file mode 100644 index c5f447bf..00000000 --- a/installers/charm/ng-ui/config.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 Arctos Labs Scandinavia AB -# -# 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: - server_name: - description: Server name - type: string - default: localhost - port: - description: Port to expose - type: int - default: 80 - 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. - Note: if set to 0, there is no limit. - default: 0 - ingress_class: - type: string - description: | - Ingress class name. This is useful for selecting the ingress to be used - in case there are multiple ingresses in the underlying k8s clusters. - ingress_whitelist_source_range: - type: string - description: | - A comma-separated list of CIDRs to store in the - ingress.kubernetes.io/whitelist-source-range annotation. - default: "" - tls_secret_name: - type: string - description: TLS Secret name - default: "" - site_url: - type: string - description: Ingress URL - default: "" - cluster_issuer: - type: string - description: Name of the cluster issuer for TLS certificates - default: "" - image_pull_policy: - type: string - description: | - ImagePullPolicy configuration for the pod. - Possible values: always, ifnotpresent, never - default: always - security_context: - description: Enables the security context of the pods - type: boolean - default: false diff --git a/installers/charm/ng-ui/metadata.yaml b/installers/charm/ng-ui/metadata.yaml deleted file mode 100644 index 60643b57..00000000 --- a/installers/charm/ng-ui/metadata.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# 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: osm-ng-ui -summary: A Next Generation UI charm for Opensource MANO -description: | - New UI for OSM -series: - - kubernetes -min-juju-version: 2.7.0 -deployment: - type: stateless - service: cluster -requires: - nbi: - interface: http -resources: - image: - type: oci-image - description: OSM docker image for NBI - upstream-source: "opensourcemano/ng-ui:latest" diff --git a/installers/charm/ng-ui/requirements-test.txt b/installers/charm/ng-ui/requirements-test.txt deleted file mode 100644 index cf61dd4e..00000000 --- a/installers/charm/ng-ui/requirements-test.txt +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -mock==4.0.3 diff --git a/installers/charm/ng-ui/requirements.txt b/installers/charm/ng-ui/requirements.txt deleted file mode 100644 index 10ade5db..00000000 --- a/installers/charm/ng-ui/requirements.txt +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -pydantic # TODO: remove it -git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master \ No newline at end of file diff --git a/installers/charm/ng-ui/src/charm.py b/installers/charm/ng-ui/src/charm.py deleted file mode 100755 index 39675d05..00000000 --- a/installers/charm/ng-ui/src/charm.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -# pylint: disable=E0213 - - -from ipaddress import ip_network -import logging -from pathlib import Path -from string import Template -from typing import NoReturn, Optional -from urllib.parse import urlparse - -from ops.main import main -from opslib.osm.charm import CharmedOsmBase, RelationsMissing -from opslib.osm.interfaces.http import HttpClient -from opslib.osm.pod import ( - ContainerV3Builder, - FilesV3Builder, - IngressResourceV3Builder, - PodSpecV3Builder, -) -from opslib.osm.validator import ModelValidator, validator - - -logger = logging.getLogger(__name__) - - -class ConfigModel(ModelValidator): - port: int - server_name: str - max_file_size: int - site_url: Optional[str] - cluster_issuer: Optional[str] - ingress_class: Optional[str] - ingress_whitelist_source_range: Optional[str] - tls_secret_name: Optional[str] - image_pull_policy: str - security_context: bool - - @validator("port") - def validate_port(cls, v): - if v <= 0: - raise ValueError("value must be greater than 0") - return v - - @validator("max_file_size") - def validate_max_file_size(cls, v): - if v < 0: - raise ValueError("value must be equal or greater than 0") - return v - - @validator("site_url") - def validate_site_url(cls, v): - if v: - parsed = urlparse(v) - if not parsed.scheme.startswith("http"): - raise ValueError("value must start with http") - return v - - @validator("ingress_whitelist_source_range") - def validate_ingress_whitelist_source_range(cls, v): - if v: - ip_network(v) - return v - - @validator("image_pull_policy") - def validate_image_pull_policy(cls, v): - values = { - "always": "Always", - "ifnotpresent": "IfNotPresent", - "never": "Never", - } - v = v.lower() - if v not in values.keys(): - raise ValueError("value must be always, ifnotpresent or never") - return values[v] - - -class NgUiCharm(CharmedOsmBase): - def __init__(self, *args) -> NoReturn: - super().__init__(*args, oci_image="image") - - self.nbi_client = HttpClient(self, "nbi") - self.framework.observe(self.on["nbi"].relation_changed, self.configure_pod) - self.framework.observe(self.on["nbi"].relation_broken, self.configure_pod) - - def _check_missing_dependencies(self, config: ConfigModel): - missing_relations = [] - - if self.nbi_client.is_missing_data_in_app(): - missing_relations.append("nbi") - - if missing_relations: - raise RelationsMissing(missing_relations) - - def _build_files(self, config: ConfigModel): - files_builder = FilesV3Builder() - files_builder.add_file( - "default", - Template(Path("templates/default.template").read_text()).substitute( - port=config.port, - server_name=config.server_name, - max_file_size=config.max_file_size, - nbi_host=self.nbi_client.host, - nbi_port=self.nbi_client.port, - ), - ) - return files_builder.build() - - def build_pod_spec(self, image_info): - # Validate config - config = ConfigModel(**dict(self.config)) - # Check relations - self._check_missing_dependencies(config) - # Create Builder for the PodSpec - pod_spec_builder = PodSpecV3Builder( - enable_security_context=config.security_context - ) - # Build Container - container_builder = ContainerV3Builder( - self.app.name, - image_info, - config.image_pull_policy, - run_as_non_root=config.security_context, - ) - container_builder.add_port(name=self.app.name, port=config.port) - container = container_builder.build() - container_builder.add_tcpsocket_readiness_probe( - config.port, - initial_delay_seconds=45, - timeout_seconds=5, - ) - container_builder.add_tcpsocket_liveness_probe( - config.port, - initial_delay_seconds=45, - timeout_seconds=15, - ) - container_builder.add_volume_config( - "configuration", - "/etc/nginx/sites-available/", - self._build_files(config), - ) - # Add container to pod spec - pod_spec_builder.add_container(container) - # Add ingress resources to pod spec if site url exists - if config.site_url: - parsed = urlparse(config.site_url) - annotations = { - "nginx.ingress.kubernetes.io/proxy-body-size": "{}".format( - str(config.max_file_size) + "m" - if config.max_file_size > 0 - else config.max_file_size - ) - } - if config.ingress_class: - annotations["kubernetes.io/ingress.class"] = config.ingress_class - ingress_resource_builder = IngressResourceV3Builder( - f"{self.app.name}-ingress", annotations - ) - - if config.ingress_whitelist_source_range: - annotations[ - "nginx.ingress.kubernetes.io/whitelist-source-range" - ] = config.ingress_whitelist_source_range - - if config.cluster_issuer: - annotations["cert-manager.io/cluster-issuer"] = config.cluster_issuer - - if parsed.scheme == "https": - ingress_resource_builder.add_tls( - [parsed.hostname], config.tls_secret_name - ) - else: - annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false" - - ingress_resource_builder.add_rule( - parsed.hostname, self.app.name, config.port - ) - ingress_resource = ingress_resource_builder.build() - pod_spec_builder.add_ingress_resource(ingress_resource) - return pod_spec_builder.build() - - -if __name__ == "__main__": - main(NgUiCharm) diff --git a/installers/charm/ng-ui/src/pod_spec.py b/installers/charm/ng-ui/src/pod_spec.py deleted file mode 100644 index 95d5f72e..00000000 --- a/installers/charm/ng-ui/src/pod_spec.py +++ /dev/null @@ -1,299 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -# pylint: disable=E0213,E0611 - - -import logging -from pydantic import ( - BaseModel, - conint, - IPvAnyNetwork, - PositiveInt, - validator, -) -from typing import Any, Dict, List, Optional -from urllib.parse import urlparse -from pathlib import Path -from string import Template - -logger = logging.getLogger(__name__) - - -class ConfigData(BaseModel): - """Configuration data model.""" - - port: PositiveInt - site_url: Optional[str] - max_file_size: Optional[conint(ge=0)] - ingress_whitelist_source_range: Optional[IPvAnyNetwork] - tls_secret_name: Optional[str] - - @validator("max_file_size", pre=True, always=True) - def validate_max_file_size(cls, value, values, **kwargs): - site_url = values.get("site_url") - - if not site_url: - return value - - parsed = urlparse(site_url) - - if not parsed.scheme.startswith("http"): - return value - - if value is None: - raise ValueError("max_file_size needs to be defined if site_url is defined") - - return value - - @validator("ingress_whitelist_source_range", pre=True, always=True) - def validate_ingress_whitelist_source_range(cls, value, values, **kwargs): - if not value: - return None - - return value - - -class RelationData(BaseModel): - """Relation data model.""" - - nbi_host: str - nbi_port: PositiveInt - - -def _make_pod_ports(port: int) -> List[Dict[str, Any]]: - """Generate pod ports details. - - Args: - port (int): Port to expose. - - Returns: - List[Dict[str, Any]]: pod port details. - """ - return [ - {"name": "http", "containerPort": port, "protocol": "TCP"}, - ] - - -def _make_pod_ingress_resources( - config: Dict[str, Any], app_name: str, port: int -) -> List[Dict[str, Any]]: - """Generate pod ingress resources. - - Args: - config (Dict[str, Any]): configuration information. - app_name (str): application name. - port (int): port to expose. - - Returns: - List[Dict[str, Any]]: pod ingress resources. - """ - site_url = config.get("site_url") - - if not site_url: - return - - parsed = urlparse(site_url) - - if not parsed.scheme.startswith("http"): - return - - max_file_size = config["max_file_size"] - ingress_whitelist_source_range = config["ingress_whitelist_source_range"] - - annotations = { - "nginx.ingress.kubernetes.io/proxy-body-size": "{}".format( - str(max_file_size) + "m" if max_file_size > 0 else 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 = 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(app_name), - "annotations": annotations, - "spec": { - "rules": [ - { - "host": parsed.hostname, - "http": { - "paths": [ - { - "path": "/", - "backend": { - "serviceName": app_name, - "servicePort": port, - }, - } - ] - }, - } - ] - }, - } - if ingress_spec_tls: - ingress["spec"]["tls"] = ingress_spec_tls - - return [ingress] - - -def _make_startup_probe() -> Dict[str, Any]: - """Generate startup probe. - - Returns: - Dict[str, Any]: startup probe. - """ - return { - "exec": {"command": ["/usr/bin/pgrep python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - -def _make_readiness_probe(port: int) -> Dict[str, Any]: - """Generate readiness probe. - - Args: - port (int): [description] - - Returns: - Dict[str, Any]: readiness probe. - """ - return { - "tcpSocket": { - "port": port, - }, - "initialDelaySeconds": 45, - "timeoutSeconds": 5, - } - - -def _make_liveness_probe(port: int) -> Dict[str, Any]: - """Generate liveness probe. - - Args: - port (int): [description] - - Returns: - Dict[str, Any]: liveness probe. - """ - return { - "tcpSocket": { - "port": port, - }, - "initialDelaySeconds": 45, - "timeoutSeconds": 5, - } - - -def _make_pod_volume_config( - config: Dict[str, Any], - relation_state: Dict[str, Any], -) -> List[Dict[str, Any]]: - """Generate volume config with files. - - Args: - config (Dict[str, Any]): configuration information. - - Returns: - Dict[str, Any]: volume config. - """ - template_data = {**config, **relation_state} - template_data["max_file_size"] = f'{template_data["max_file_size"]}M' - return [ - { - "name": "configuration", - "mountPath": "/etc/nginx/sites-available/", - "files": [ - { - "path": "default", - "content": Template(Path("files/default").read_text()).substitute( - template_data - ), - } - ], - } - ] - - -def make_pod_spec( - image_info: Dict[str, str], - config: Dict[str, Any], - relation_state: Dict[str, Any], - app_name: str = "ng-ui", -) -> Dict[str, Any]: - """Generate the pod spec information. - - Args: - image_info (Dict[str, str]): Object provided by - OCIImageResource("image").fetch(). - config (Dict[str, Any]): Configuration information. - relation_state (Dict[str, Any]): Relation state information. - app_name (str, optional): Application name. Defaults to "ng-ui". - port (int, optional): Port for the container. Defaults to 80. - - Returns: - Dict[str, Any]: Pod spec dictionary for the charm. - """ - if not image_info: - return None - - ConfigData(**(config)) - RelationData(**(relation_state)) - - ports = _make_pod_ports(config["port"]) - ingress_resources = _make_pod_ingress_resources(config, app_name, config["port"]) - kubernetes = { - # "startupProbe": _make_startup_probe(), - "readinessProbe": _make_readiness_probe(config["port"]), - "livenessProbe": _make_liveness_probe(config["port"]), - } - volume_config = _make_pod_volume_config(config, relation_state) - return { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": ports, - "kubernetes": kubernetes, - "volumeConfig": volume_config, - } - ], - "kubernetesResources": { - "ingressResources": ingress_resources or [], - }, - } diff --git a/installers/charm/ng-ui/templates/default.template b/installers/charm/ng-ui/templates/default.template deleted file mode 100644 index f946263f..00000000 --- a/installers/charm/ng-ui/templates/default.template +++ /dev/null @@ -1,33 +0,0 @@ -# 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. - - - -server { - listen $port; - server_name $server_name; - root /usr/share/nginx/html; - index index.html index.htm; - client_max_body_size $max_file_size; - - location /osm { - proxy_pass https://$nbi_host:$nbi_port; - proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; - proxy_set_header Accept-Encoding ""; - } - - location / { - try_files $$uri $$uri/ /index.html; - } -} diff --git a/installers/charm/ng-ui/tests/__init__.py b/installers/charm/ng-ui/tests/__init__.py deleted file mode 100644 index 446d5cee..00000000 --- a/installers/charm/ng-ui/tests/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -"""Init mocking for unit tests.""" - -import sys - - -import mock - - -class OCIImageResourceErrorMock(Exception): - pass - - -sys.path.append("src") - -oci_image = mock.MagicMock() -oci_image.OCIImageResourceError = OCIImageResourceErrorMock -sys.modules["oci_image"] = oci_image -sys.modules["oci_image"].OCIImageResource().fetch.return_value = {} diff --git a/installers/charm/ng-ui/tests/test_charm.py b/installers/charm/ng-ui/tests/test_charm.py deleted file mode 100644 index 2765e81c..00000000 --- a/installers/charm/ng-ui/tests/test_charm.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import sys -from typing import NoReturn -import unittest - -from charm import NgUiCharm -from ops.model import ActiveStatus, BlockedStatus -from ops.testing import Harness - - -class TestCharm(unittest.TestCase): - """Prometheus Charm unit tests.""" - - def setUp(self) -> NoReturn: - """Test setup""" - self.image_info = sys.modules["oci_image"].OCIImageResource().fetch() - self.harness = Harness(NgUiCharm) - self.harness.set_leader(is_leader=True) - self.harness.begin() - self.config = { - "server_name": "localhost", - "port": 80, - "max_file_size": 0, - "ingress_whitelist_source_range": "", - "tls_secret_name": "", - "site_url": "https://ui.192.168.100.100.nip.io", - "cluster_issuer": "vault-issuer", - } - self.harness.update_config(self.config) - - def test_config_changed_no_relations( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue( - all( - relation in self.harness.charm.unit.status.message - for relation in ["nbi"] - ) - ) - - def test_config_changed_non_leader( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - self.harness.set_leader(is_leader=False) - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus) - - def test_with_relations( - self, - ) -> NoReturn: - "Test with relations (internal)" - self.initialize_nbi_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def initialize_nbi_relation(self): - http_relation_id = self.harness.add_relation("nbi", "nbi") - self.harness.add_relation_unit(http_relation_id, "nbi") - self.harness.update_relation_data( - http_relation_id, - "nbi", - {"host": "nbi", "port": 9999}, - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/installers/charm/ng-ui/tox.ini b/installers/charm/ng-ui/tox.ini deleted file mode 100644 index 58e13a66..00000000 --- a/installers/charm/ng-ui/tox.ini +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## -####################################################################################### - -[tox] -envlist = black, cover, flake8, pylint, yamllint, safety -skipsdist = true - -[tox:jenkins] -toxworkdir = /tmp/.tox - -[testenv] -basepython = python3.8 -setenv = VIRTUAL_ENV={envdir} - PYTHONDONTWRITEBYTECODE = 1 -deps = -r{toxinidir}/requirements.txt - - -####################################################################################### -[testenv:black] -deps = black -commands = - black --check --diff src/ tests/ - - -####################################################################################### -[testenv:cover] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - coverage - nose2 -commands = - sh -c 'rm -f nosetests.xml' - coverage erase - nose2 -C --coverage src - coverage report --omit='*tests*' - coverage html -d ./cover --omit='*tests*' - coverage xml -o coverage.xml --omit=*tests* -whitelist_externals = sh - - -####################################################################################### -[testenv:flake8] -deps = flake8 - flake8-import-order -commands = - flake8 src/ tests/ --exclude=*pod_spec* - - -####################################################################################### -[testenv:pylint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - pylint==2.10.2 -commands = - pylint -E src/ tests/ - - -####################################################################################### -[testenv:safety] -setenv = - LC_ALL=C.UTF-8 - LANG=C.UTF-8 -deps = {[testenv]deps} - safety -commands = - - safety check --full-report - - -####################################################################################### -[testenv:yamllint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - yamllint -commands = yamllint . - -####################################################################################### -[testenv:build] -passenv=HTTP_PROXY HTTPS_PROXY NO_PROXY -whitelist_externals = - charmcraft - sh -commands = - charmcraft pack - sh -c 'ubuntu_version=20.04; \ - architectures="amd64-aarch64-arm64"; \ - charm_name=`cat metadata.yaml | grep -E "^name: " | cut -f 2 -d " "`; \ - mv $charm_name"_ubuntu-"$ubuntu_version-$architectures.charm $charm_name.charm' - -####################################################################################### -[flake8] -ignore = - W291, - W293, - W503, - E123, - E125, - E226, - E241, -exclude = - .git, - __pycache__, - .tox, -max-line-length = 120 -show-source = True -builtins = _ -max-complexity = 10 -import-order-style = google diff --git a/installers/charm/pla/.gitignore b/installers/charm/pla/.gitignore deleted file mode 100644 index 493739ef..00000000 --- a/installers/charm/pla/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.stestr -cover -release diff --git a/installers/charm/pla/.jujuignore b/installers/charm/pla/.jujuignore deleted file mode 100644 index 3ae3e7dc..00000000 --- a/installers/charm/pla/.jujuignore +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.gitignore -.stestr -cover -release -tests/ -requirements* -tox.ini diff --git a/installers/charm/pla/.yamllint.yaml b/installers/charm/pla/.yamllint.yaml deleted file mode 100644 index d71fb69f..00000000 --- a/installers/charm/pla/.yamllint.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - ---- -extends: default - -yaml-files: - - "*.yaml" - - "*.yml" - - ".yamllint" -ignore: | - .tox - cover/ - build/ - venv - release/ diff --git a/installers/charm/pla/README.md b/installers/charm/pla/README.md deleted file mode 100644 index 8d486d00..00000000 --- a/installers/charm/pla/README.md +++ /dev/null @@ -1,14 +0,0 @@ - -# PLA Charm \ No newline at end of file diff --git a/installers/charm/pla/charmcraft.yaml b/installers/charm/pla/charmcraft.yaml deleted file mode 100644 index 0a285a9d..00000000 --- a/installers/charm/pla/charmcraft.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -type: charm -bases: - - build-on: - - name: ubuntu - channel: "20.04" - architectures: ["amd64"] - run-on: - - name: ubuntu - channel: "20.04" - architectures: - - amd64 - - aarch64 - - arm64 -parts: - charm: - build-packages: [git] diff --git a/installers/charm/pla/config.yaml b/installers/charm/pla/config.yaml deleted file mode 100644 index 642c165e..00000000 --- a/installers/charm/pla/config.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 Arctos Labs Scandinavia AB -# -# 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: - log_level: - description: Log level - type: string - default: INFO - database_commonkey: - description: Common Key for Mongo database - type: string - default: osm - mongodb_uri: - type: string - description: MongoDB URI (external database) - image_pull_policy: - type: string - description: | - ImagePullPolicy configuration for the pod. - Possible values: always, ifnotpresent, never - default: always - security_context: - description: Enables the security context of the pods - type: boolean - default: false diff --git a/installers/charm/pla/lib/charms/kafka_k8s/v0/kafka.py b/installers/charm/pla/lib/charms/kafka_k8s/v0/kafka.py deleted file mode 100644 index 1baf9a88..00000000 --- a/installers/charm/pla/lib/charms/kafka_k8s/v0/kafka.py +++ /dev/null @@ -1,207 +0,0 @@ -# Copyright 2022 Canonical Ltd. -# See LICENSE file for licensing details. -# -# 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. - -"""Kafka library. - -This [library](https://juju.is/docs/sdk/libraries) implements both sides of the -`kafka` [interface](https://juju.is/docs/sdk/relations). - -The *provider* side of this interface is implemented by the -[kafka-k8s Charmed Operator](https://charmhub.io/kafka-k8s). - -Any Charmed Operator that *requires* Kafka for providing its -service should implement the *requirer* side of this interface. - -In a nutshell using this library to implement a Charmed Operator *requiring* -Kafka would look like - -``` -$ charmcraft fetch-lib charms.kafka_k8s.v0.kafka -``` - -`metadata.yaml`: - -``` -requires: - kafka: - interface: kafka - limit: 1 -``` - -`src/charm.py`: - -``` -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.charm import CharmBase - - -class MyCharm(CharmBase): - - on = KafkaEvents() - - def __init__(self, *args): - super().__init__(*args) - self.kafka = KafkaRequires(self) - self.framework.observe( - self.on.kafka_available, - self._on_kafka_available, - ) - self.framework.observe( - self.on.kafka_broken, - self._on_kafka_broken, - ) - - def _on_kafka_available(self, event): - # Get Kafka host and port - host: str = self.kafka.host - port: int = self.kafka.port - # host => "kafka-k8s" - # port => 9092 - - def _on_kafka_broken(self, event): - # Stop service - # ... - self.unit.status = BlockedStatus("need kafka relation") -``` - -You can file bugs -[here](https://github.com/charmed-osm/kafka-k8s-operator/issues)! -""" - -from typing import Optional - -from ops.charm import CharmBase, CharmEvents -from ops.framework import EventBase, EventSource, Object - -# The unique Charmhub library identifier, never change it -from ops.model import Relation - -LIBID = "eacc8c85082347c9aae740e0220b8376" - -# 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 = 3 - - -KAFKA_HOST_APP_KEY = "host" -KAFKA_PORT_APP_KEY = "port" - - -class _KafkaAvailableEvent(EventBase): - """Event emitted when Kafka is available.""" - - -class _KafkaBrokenEvent(EventBase): - """Event emitted when Kafka relation is broken.""" - - -class KafkaEvents(CharmEvents): - """Kafka events. - - This class defines the events that Kafka can emit. - - Events: - kafka_available (_KafkaAvailableEvent) - """ - - kafka_available = EventSource(_KafkaAvailableEvent) - kafka_broken = EventSource(_KafkaBrokenEvent) - - -class KafkaRequires(Object): - """Requires-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self.charm = charm - self._endpoint_name = endpoint_name - - # Observe relation events - event_observe_mapping = { - charm.on[self._endpoint_name].relation_changed: self._on_relation_changed, - charm.on[self._endpoint_name].relation_broken: self._on_relation_broken, - } - for event, observer in event_observe_mapping.items(): - self.framework.observe(event, observer) - - def _on_relation_changed(self, event) -> None: - if event.relation.app and all( - key in event.relation.data[event.relation.app] - for key in (KAFKA_HOST_APP_KEY, KAFKA_PORT_APP_KEY) - ): - self.charm.on.kafka_available.emit() - - def _on_relation_broken(self, _) -> None: - self.charm.on.kafka_broken.emit() - - @property - def host(self) -> str: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - relation.data[relation.app].get(KAFKA_HOST_APP_KEY) - if relation and relation.app - else None - ) - - @property - def port(self) -> int: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - int(relation.data[relation.app].get(KAFKA_PORT_APP_KEY)) - if relation and relation.app - else None - ) - - -class KafkaProvides(Object): - """Provides-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self._endpoint_name = endpoint_name - - def set_host_info(self, host: str, port: int, relation: Optional[Relation] = None) -> None: - """Set Kafka host and port. - - This function writes in the application data of the relation, therefore, - only the unit leader can call it. - - Args: - host (str): Kafka hostname or IP address. - port (int): Kafka port. - relation (Optional[Relation]): Relation to update. - If not specified, all relations will be updated. - - Raises: - Exception: if a non-leader unit calls this function. - """ - if not self.model.unit.is_leader(): - raise Exception("only the leader set host information.") - - if relation: - self._update_relation_data(host, port, relation) - return - - for relation in self.model.relations[self._endpoint_name]: - self._update_relation_data(host, port, relation) - - def _update_relation_data(self, host: str, port: int, relation: Relation) -> None: - """Update data in relation if needed.""" - relation.data[self.model.app][KAFKA_HOST_APP_KEY] = host - relation.data[self.model.app][KAFKA_PORT_APP_KEY] = str(port) diff --git a/installers/charm/pla/metadata.yaml b/installers/charm/pla/metadata.yaml deleted file mode 100644 index bd8b279d..00000000 --- a/installers/charm/pla/metadata.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# 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: osm-pla -summary: A Placement charm for Opensource MANO -description: | - Placement module for OSM -series: - - kubernetes -min-juju-version: 2.7.0 -deployment: - type: stateless - service: cluster -resources: - image: - type: oci-image - description: OSM docker image for POL - upstream-source: "opensourcemano/pla:latest" -requires: - kafka: - interface: kafka - mongodb: - interface: mongodb diff --git a/installers/charm/pla/requirements-test.txt b/installers/charm/pla/requirements-test.txt deleted file mode 100644 index cf61dd4e..00000000 --- a/installers/charm/pla/requirements-test.txt +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -mock==4.0.3 diff --git a/installers/charm/pla/requirements.txt b/installers/charm/pla/requirements.txt deleted file mode 100644 index 1a8928c7..00000000 --- a/installers/charm/pla/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master \ No newline at end of file diff --git a/installers/charm/pla/src/charm.py b/installers/charm/pla/src/charm.py deleted file mode 100755 index d907f0bc..00000000 --- a/installers/charm/pla/src/charm.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -# pylint: disable=E0213 - - -import logging -from typing import NoReturn, Optional - -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.main import main -from opslib.osm.charm import CharmedOsmBase, RelationsMissing -from opslib.osm.interfaces.mongo import MongoClient -from opslib.osm.pod import ( - ContainerV3Builder, - PodRestartPolicy, - PodSpecV3Builder, -) -from opslib.osm.validator import ModelValidator, validator - - -logger = logging.getLogger(__name__) - -PORT = 9999 - - -class ConfigModel(ModelValidator): - database_commonkey: str - mongodb_uri: Optional[str] - log_level: str - image_pull_policy: str - security_context: bool - - @validator("log_level") - def validate_log_level(cls, v): - if v not in {"INFO", "DEBUG"}: - raise ValueError("value must be INFO or DEBUG") - return v - - @validator("mongodb_uri") - def validate_mongodb_uri(cls, v): - if v and not v.startswith("mongodb://"): - raise ValueError("mongodb_uri is not properly formed") - return v - - @validator("image_pull_policy") - def validate_image_pull_policy(cls, v): - values = { - "always": "Always", - "ifnotpresent": "IfNotPresent", - "never": "Never", - } - v = v.lower() - if v not in values.keys(): - raise ValueError("value must be always, ifnotpresent or never") - return values[v] - - -class PlaCharm(CharmedOsmBase): - on = KafkaEvents() - - def __init__(self, *args) -> NoReturn: - super().__init__(*args, oci_image="image") - - self.kafka = KafkaRequires(self) - self.framework.observe(self.on.kafka_available, self.configure_pod) - self.framework.observe(self.on.kafka_broken, self.configure_pod) - - self.mongodb_client = MongoClient(self, "mongodb") - self.framework.observe(self.on["mongodb"].relation_changed, self.configure_pod) - self.framework.observe(self.on["mongodb"].relation_broken, self.configure_pod) - - def _check_missing_dependencies(self, config: ConfigModel): - missing_relations = [] - - if not self.kafka.host or not self.kafka.port: - missing_relations.append("kafka") - if not config.mongodb_uri and self.mongodb_client.is_missing_data_in_unit(): - missing_relations.append("mongodb") - - if missing_relations: - raise RelationsMissing(missing_relations) - - def build_pod_spec(self, image_info): - # Validate config - config = ConfigModel(**dict(self.config)) - - if config.mongodb_uri and not self.mongodb_client.is_missing_data_in_unit(): - raise Exception("Mongodb data cannot be provided via config and relation") - - # Check relations - self._check_missing_dependencies(config) - - # Create Builder for the PodSpec - pod_spec_builder = PodSpecV3Builder( - enable_security_context=config.security_context - ) - - # Add secrets to the pod - mongodb_secret_name = f"{self.app.name}-mongodb-secret" - pod_spec_builder.add_secret( - mongodb_secret_name, - { - "uri": config.mongodb_uri or self.mongodb_client.connection_string, - "commonkey": config.database_commonkey, - }, - ) - - # Build Container - container_builder = ContainerV3Builder( - self.app.name, - image_info, - config.image_pull_policy, - run_as_non_root=config.security_context, - ) - container_builder.add_port(name=self.app.name, port=PORT) - container_builder.add_envs( - { - # General configuration - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMPLA_GLOBAL_LOG_LEVEL": config.log_level, - # Kafka configuration - "OSMPLA_MESSAGE_DRIVER": "kafka", - "OSMPLA_MESSAGE_HOST": self.kafka.host, - "OSMPLA_MESSAGE_PORT": self.kafka.port, - # Database configuration - "OSMPLA_DATABASE_DRIVER": "mongo", - } - ) - - container_builder.add_secret_envs( - secret_name=mongodb_secret_name, - envs={ - "OSMPLA_DATABASE_URI": "uri", - "OSMPLA_DATABASE_COMMONKEY": "commonkey", - }, - ) - - container = container_builder.build() - - # Add Pod restart policy - restart_policy = PodRestartPolicy() - restart_policy.add_secrets(secret_names=(mongodb_secret_name,)) - pod_spec_builder.set_restart_policy(restart_policy) - - # Add container to pod spec - pod_spec_builder.add_container(container) - - return pod_spec_builder.build() - - -if __name__ == "__main__": - main(PlaCharm) diff --git a/installers/charm/pla/tests/__init__.py b/installers/charm/pla/tests/__init__.py deleted file mode 100644 index 446d5cee..00000000 --- a/installers/charm/pla/tests/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -"""Init mocking for unit tests.""" - -import sys - - -import mock - - -class OCIImageResourceErrorMock(Exception): - pass - - -sys.path.append("src") - -oci_image = mock.MagicMock() -oci_image.OCIImageResourceError = OCIImageResourceErrorMock -sys.modules["oci_image"] = oci_image -sys.modules["oci_image"].OCIImageResource().fetch.return_value = {} diff --git a/installers/charm/pla/tests/test_charm.py b/installers/charm/pla/tests/test_charm.py deleted file mode 100644 index d577e9fb..00000000 --- a/installers/charm/pla/tests/test_charm.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import sys -from typing import NoReturn -import unittest - - -from charm import PlaCharm -from ops.model import ActiveStatus, BlockedStatus -from ops.testing import Harness - - -class TestCharm(unittest.TestCase): - """Pla Charm unit tests.""" - - def setUp(self) -> NoReturn: - """Test setup""" - self.image_info = sys.modules["oci_image"].OCIImageResource().fetch() - self.harness = Harness(PlaCharm) - self.harness.set_leader(is_leader=True) - self.harness.begin() - self.config = { - "log_level": "INFO", - "mongodb_uri": "", - } - self.harness.update_config(self.config) - - def test_config_changed_no_relations( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue( - all( - relation in self.harness.charm.unit.status.message - for relation in ["mongodb", "kafka"] - ) - ) - - def test_config_changed_non_leader( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - self.harness.set_leader(is_leader=False) - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus) - - def test_with_relations_and_mongodb_config( - self, - ) -> NoReturn: - "Test with relations and mongodb config (internal)" - self.initialize_kafka_relation() - self.initialize_mongo_config() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_with_relations( - self, - ) -> NoReturn: - "Test with relations (internal)" - self.initialize_kafka_relation() - self.initialize_mongo_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_exception_mongodb_relation_and_config( - self, - ) -> NoReturn: - "Test with relation and config for Mongodb. Test must fail" - self.initialize_mongo_relation() - self.initialize_mongo_config() - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def initialize_kafka_relation(self): - kafka_relation_id = self.harness.add_relation("kafka", "kafka") - self.harness.add_relation_unit(kafka_relation_id, "kafka/0") - self.harness.update_relation_data( - kafka_relation_id, "kafka", {"host": "kafka", "port": 9092} - ) - - def initialize_mongo_config(self): - self.harness.update_config({"mongodb_uri": "mongodb://mongo:27017"}) - - def initialize_mongo_relation(self): - mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") - self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") - self.harness.update_relation_data( - mongodb_relation_id, - "mongodb/0", - {"connection_string": "mongodb://mongo:27017"}, - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/installers/charm/pla/tox.ini b/installers/charm/pla/tox.ini deleted file mode 100644 index f3c91440..00000000 --- a/installers/charm/pla/tox.ini +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## -####################################################################################### - -[tox] -envlist = black, cover, flake8, pylint, yamllint, safety -skipsdist = true - -[tox:jenkins] -toxworkdir = /tmp/.tox - -[testenv] -basepython = python3.8 -setenv = - VIRTUAL_ENV={envdir} - PYTHONPATH = {toxinidir}:{toxinidir}/lib:{toxinidir}/src - PYTHONDONTWRITEBYTECODE = 1 -deps = -r{toxinidir}/requirements.txt - - -####################################################################################### -[testenv:black] -deps = black -commands = - black --check --diff src/ tests/ - - -####################################################################################### -[testenv:cover] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - coverage - nose2 -commands = - sh -c 'rm -f nosetests.xml' - coverage erase - nose2 -C --coverage src - coverage report --omit='*tests*' - coverage html -d ./cover --omit='*tests*' - coverage xml -o coverage.xml --omit=*tests* -whitelist_externals = sh - - -####################################################################################### -[testenv:flake8] -deps = flake8 - flake8-import-order -commands = - flake8 src/ tests/ - - -####################################################################################### -[testenv:pylint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - pylint==2.10.2 -commands = - pylint -E src/ tests/ - - -####################################################################################### -[testenv:safety] -setenv = - LC_ALL=C.UTF-8 - LANG=C.UTF-8 -deps = {[testenv]deps} - safety -commands = - - safety check --full-report - - -####################################################################################### -[testenv:yamllint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - yamllint -commands = yamllint . - -####################################################################################### -[testenv:build] -passenv=HTTP_PROXY HTTPS_PROXY NO_PROXY -whitelist_externals = - charmcraft - sh -commands = - charmcraft pack - sh -c 'ubuntu_version=20.04; \ - architectures="amd64-aarch64-arm64"; \ - charm_name=`cat metadata.yaml | grep -E "^name: " | cut -f 2 -d " "`; \ - mv $charm_name"_ubuntu-"$ubuntu_version-$architectures.charm $charm_name.charm' - -####################################################################################### -[flake8] -ignore = - W291, - W293, - W503, - E123, - E125, - E226, - E241, -exclude = - .git, - __pycache__, - .tox, -max-line-length = 120 -show-source = True -builtins = _ -max-complexity = 10 -import-order-style = google diff --git a/installers/charm/pol/.gitignore b/installers/charm/pol/.gitignore deleted file mode 100644 index 2885df27..00000000 --- a/installers/charm/pol/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.stestr -cover -release \ No newline at end of file diff --git a/installers/charm/pol/.jujuignore b/installers/charm/pol/.jujuignore deleted file mode 100644 index 3ae3e7dc..00000000 --- a/installers/charm/pol/.jujuignore +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.gitignore -.stestr -cover -release -tests/ -requirements* -tox.ini diff --git a/installers/charm/pol/.yamllint.yaml b/installers/charm/pol/.yamllint.yaml deleted file mode 100644 index d71fb69f..00000000 --- a/installers/charm/pol/.yamllint.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - ---- -extends: default - -yaml-files: - - "*.yaml" - - "*.yml" - - ".yamllint" -ignore: | - .tox - cover/ - build/ - venv - release/ diff --git a/installers/charm/pol/README.md b/installers/charm/pol/README.md deleted file mode 100644 index 12e60dfc..00000000 --- a/installers/charm/pol/README.md +++ /dev/null @@ -1,23 +0,0 @@ - - -# POL operator Charm for Kubernetes - -## Requirements diff --git a/installers/charm/pol/charmcraft.yaml b/installers/charm/pol/charmcraft.yaml deleted file mode 100644 index 0a285a9d..00000000 --- a/installers/charm/pol/charmcraft.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -type: charm -bases: - - build-on: - - name: ubuntu - channel: "20.04" - architectures: ["amd64"] - run-on: - - name: ubuntu - channel: "20.04" - architectures: - - amd64 - - aarch64 - - arm64 -parts: - charm: - build-packages: [git] diff --git a/installers/charm/pol/config.yaml b/installers/charm/pol/config.yaml deleted file mode 100644 index a2eef474..00000000 --- a/installers/charm/pol/config.yaml +++ /dev/null @@ -1,69 +0,0 @@ -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -options: - log_level: - description: "Log Level" - type: string - default: "INFO" - mongodb_uri: - type: string - description: MongoDB URI (external database) - mysql_uri: - type: string - description: | - Mysql URI with the following format: - mysql://:@:/ - image_pull_policy: - type: string - description: | - ImagePullPolicy configuration for the pod. - Possible values: always, ifnotpresent, never - default: always - debug_mode: - description: | - If true, debug mode is activated. It means that the service will not run, - and instead, the command for the container will be a `sleep infinity`. - Note: If enabled, security_context will be disabled. - type: boolean - default: false - debug_pubkey: - description: | - Public SSH key that will be injected to the application pod. - type: string - debug_pol_local_path: - description: | - Local full path to the POL project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - debug_common_local_path: - description: | - Local full path to the COMMON project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - security_context: - description: Enables the security context of the pods - type: boolean - default: false diff --git a/installers/charm/pol/lib/charms/kafka_k8s/v0/kafka.py b/installers/charm/pol/lib/charms/kafka_k8s/v0/kafka.py deleted file mode 100644 index 1baf9a88..00000000 --- a/installers/charm/pol/lib/charms/kafka_k8s/v0/kafka.py +++ /dev/null @@ -1,207 +0,0 @@ -# Copyright 2022 Canonical Ltd. -# See LICENSE file for licensing details. -# -# 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. - -"""Kafka library. - -This [library](https://juju.is/docs/sdk/libraries) implements both sides of the -`kafka` [interface](https://juju.is/docs/sdk/relations). - -The *provider* side of this interface is implemented by the -[kafka-k8s Charmed Operator](https://charmhub.io/kafka-k8s). - -Any Charmed Operator that *requires* Kafka for providing its -service should implement the *requirer* side of this interface. - -In a nutshell using this library to implement a Charmed Operator *requiring* -Kafka would look like - -``` -$ charmcraft fetch-lib charms.kafka_k8s.v0.kafka -``` - -`metadata.yaml`: - -``` -requires: - kafka: - interface: kafka - limit: 1 -``` - -`src/charm.py`: - -``` -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.charm import CharmBase - - -class MyCharm(CharmBase): - - on = KafkaEvents() - - def __init__(self, *args): - super().__init__(*args) - self.kafka = KafkaRequires(self) - self.framework.observe( - self.on.kafka_available, - self._on_kafka_available, - ) - self.framework.observe( - self.on.kafka_broken, - self._on_kafka_broken, - ) - - def _on_kafka_available(self, event): - # Get Kafka host and port - host: str = self.kafka.host - port: int = self.kafka.port - # host => "kafka-k8s" - # port => 9092 - - def _on_kafka_broken(self, event): - # Stop service - # ... - self.unit.status = BlockedStatus("need kafka relation") -``` - -You can file bugs -[here](https://github.com/charmed-osm/kafka-k8s-operator/issues)! -""" - -from typing import Optional - -from ops.charm import CharmBase, CharmEvents -from ops.framework import EventBase, EventSource, Object - -# The unique Charmhub library identifier, never change it -from ops.model import Relation - -LIBID = "eacc8c85082347c9aae740e0220b8376" - -# 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 = 3 - - -KAFKA_HOST_APP_KEY = "host" -KAFKA_PORT_APP_KEY = "port" - - -class _KafkaAvailableEvent(EventBase): - """Event emitted when Kafka is available.""" - - -class _KafkaBrokenEvent(EventBase): - """Event emitted when Kafka relation is broken.""" - - -class KafkaEvents(CharmEvents): - """Kafka events. - - This class defines the events that Kafka can emit. - - Events: - kafka_available (_KafkaAvailableEvent) - """ - - kafka_available = EventSource(_KafkaAvailableEvent) - kafka_broken = EventSource(_KafkaBrokenEvent) - - -class KafkaRequires(Object): - """Requires-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self.charm = charm - self._endpoint_name = endpoint_name - - # Observe relation events - event_observe_mapping = { - charm.on[self._endpoint_name].relation_changed: self._on_relation_changed, - charm.on[self._endpoint_name].relation_broken: self._on_relation_broken, - } - for event, observer in event_observe_mapping.items(): - self.framework.observe(event, observer) - - def _on_relation_changed(self, event) -> None: - if event.relation.app and all( - key in event.relation.data[event.relation.app] - for key in (KAFKA_HOST_APP_KEY, KAFKA_PORT_APP_KEY) - ): - self.charm.on.kafka_available.emit() - - def _on_relation_broken(self, _) -> None: - self.charm.on.kafka_broken.emit() - - @property - def host(self) -> str: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - relation.data[relation.app].get(KAFKA_HOST_APP_KEY) - if relation and relation.app - else None - ) - - @property - def port(self) -> int: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - int(relation.data[relation.app].get(KAFKA_PORT_APP_KEY)) - if relation and relation.app - else None - ) - - -class KafkaProvides(Object): - """Provides-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self._endpoint_name = endpoint_name - - def set_host_info(self, host: str, port: int, relation: Optional[Relation] = None) -> None: - """Set Kafka host and port. - - This function writes in the application data of the relation, therefore, - only the unit leader can call it. - - Args: - host (str): Kafka hostname or IP address. - port (int): Kafka port. - relation (Optional[Relation]): Relation to update. - If not specified, all relations will be updated. - - Raises: - Exception: if a non-leader unit calls this function. - """ - if not self.model.unit.is_leader(): - raise Exception("only the leader set host information.") - - if relation: - self._update_relation_data(host, port, relation) - return - - for relation in self.model.relations[self._endpoint_name]: - self._update_relation_data(host, port, relation) - - def _update_relation_data(self, host: str, port: int, relation: Relation) -> None: - """Update data in relation if needed.""" - relation.data[self.model.app][KAFKA_HOST_APP_KEY] = host - relation.data[self.model.app][KAFKA_PORT_APP_KEY] = str(port) diff --git a/installers/charm/pol/metadata.yaml b/installers/charm/pol/metadata.yaml deleted file mode 100644 index f9f69231..00000000 --- a/installers/charm/pol/metadata.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -name: osm-pol -summary: OSM Policy Module (POL) -description: | - A CAAS charm to deploy OSM's Policy Module (POL). -series: - - kubernetes -tags: - - kubernetes - - osm - - pol -min-juju-version: 2.8.0 -deployment: - type: stateless - service: cluster -resources: - image: - type: oci-image - description: OSM docker image for POL - upstream-source: "opensourcemano/pol:latest" -requires: - kafka: - interface: kafka - mongodb: - interface: mongodb - mysql: - interface: mysql - limit: 1 diff --git a/installers/charm/pol/requirements-test.txt b/installers/charm/pol/requirements-test.txt deleted file mode 100644 index cf61dd4e..00000000 --- a/installers/charm/pol/requirements-test.txt +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -mock==4.0.3 diff --git a/installers/charm/pol/requirements.txt b/installers/charm/pol/requirements.txt deleted file mode 100644 index 1a8928c7..00000000 --- a/installers/charm/pol/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master \ No newline at end of file diff --git a/installers/charm/pol/src/charm.py b/installers/charm/pol/src/charm.py deleted file mode 100755 index 94f6ecb8..00000000 --- a/installers/charm/pol/src/charm.py +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -# pylint: disable=E0213 - - -import logging -import re -from typing import NoReturn, Optional - -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.main import main -from opslib.osm.charm import CharmedOsmBase, RelationsMissing -from opslib.osm.interfaces.mongo import MongoClient -from opslib.osm.interfaces.mysql import MysqlClient -from opslib.osm.pod import ( - ContainerV3Builder, - PodRestartPolicy, - PodSpecV3Builder, -) -from opslib.osm.validator import ModelValidator, validator - - -logger = logging.getLogger(__name__) - -PORT = 9999 -DEFAULT_MYSQL_DATABASE = "pol" - - -class ConfigModel(ModelValidator): - log_level: str - mongodb_uri: Optional[str] - mysql_uri: Optional[str] - image_pull_policy: str - debug_mode: bool - security_context: bool - - @validator("log_level") - def validate_log_level(cls, v): - if v not in {"INFO", "DEBUG"}: - raise ValueError("value must be INFO or DEBUG") - return v - - @validator("mongoddb_uri") - def validate_mongodb_uri(cls, v): - if v and not v.startswith("mongodb://"): - raise ValueError("mongodb_uri is not properly formed") - return v - - @validator("mysql_uri") - def validate_mysql_uri(cls, v): - pattern = re.compile("^mysql:\/\/.*:.*@.*:\d+\/.*$") # noqa: W605 - if v and not pattern.search(v): - raise ValueError("mysql_uri is not properly formed") - return v - - @validator("image_pull_policy") - def validate_image_pull_policy(cls, v): - values = { - "always": "Always", - "ifnotpresent": "IfNotPresent", - "never": "Never", - } - v = v.lower() - if v not in values.keys(): - raise ValueError("value must be always, ifnotpresent or never") - return values[v] - - -class PolCharm(CharmedOsmBase): - on = KafkaEvents() - - def __init__(self, *args) -> NoReturn: - super().__init__( - *args, - oci_image="image", - vscode_workspace=VSCODE_WORKSPACE, - ) - if self.config.get("debug_mode"): - self.enable_debug_mode( - pubkey=self.config.get("debug_pubkey"), - hostpaths={ - "POL": { - "hostpath": self.config.get("debug_pol_local_path"), - "container-path": "/usr/lib/python3/dist-packages/osm_policy_module", - }, - "osm_common": { - "hostpath": self.config.get("debug_common_local_path"), - "container-path": "/usr/lib/python3/dist-packages/osm_common", - }, - }, - ) - self.kafka = KafkaRequires(self) - self.framework.observe(self.on.kafka_available, self.configure_pod) - self.framework.observe(self.on.kafka_broken, self.configure_pod) - - self.mongodb_client = MongoClient(self, "mongodb") - self.framework.observe(self.on["mongodb"].relation_changed, self.configure_pod) - self.framework.observe(self.on["mongodb"].relation_broken, self.configure_pod) - - self.mysql_client = MysqlClient(self, "mysql") - self.framework.observe(self.on["mysql"].relation_changed, self.configure_pod) - self.framework.observe(self.on["mysql"].relation_broken, self.configure_pod) - - def _check_missing_dependencies(self, config: ConfigModel): - missing_relations = [] - - if not self.kafka.host or not self.kafka.port: - missing_relations.append("kafka") - if not config.mongodb_uri and self.mongodb_client.is_missing_data_in_unit(): - missing_relations.append("mongodb") - if not config.mysql_uri and self.mysql_client.is_missing_data_in_unit(): - missing_relations.append("mysql") - if missing_relations: - raise RelationsMissing(missing_relations) - - def build_pod_spec(self, image_info): - # Validate config - config = ConfigModel(**dict(self.config)) - - if config.mongodb_uri and not self.mongodb_client.is_missing_data_in_unit(): - raise Exception("Mongodb data cannot be provided via config and relation") - if config.mysql_uri and not self.mysql_client.is_missing_data_in_unit(): - raise Exception("Mysql data cannot be provided via config and relation") - - # Check relations - self._check_missing_dependencies(config) - - security_context_enabled = ( - config.security_context if not config.debug_mode else False - ) - - # Create Builder for the PodSpec - pod_spec_builder = PodSpecV3Builder( - enable_security_context=security_context_enabled - ) - - # Add secrets to the pod - mongodb_secret_name = f"{self.app.name}-mongodb-secret" - pod_spec_builder.add_secret( - mongodb_secret_name, - {"uri": config.mongodb_uri or self.mongodb_client.connection_string}, - ) - mysql_secret_name = f"{self.app.name}-mysql-secret" - pod_spec_builder.add_secret( - mysql_secret_name, - { - "uri": config.mysql_uri - or self.mysql_client.get_root_uri(DEFAULT_MYSQL_DATABASE) - }, - ) - - # Build Container - container_builder = ContainerV3Builder( - self.app.name, - image_info, - config.image_pull_policy, - run_as_non_root=security_context_enabled, - ) - container_builder.add_port(name=self.app.name, port=PORT) - container_builder.add_envs( - { - # General configuration - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMPOL_GLOBAL_LOGLEVEL": config.log_level, - # Kafka configuration - "OSMPOL_MESSAGE_DRIVER": "kafka", - "OSMPOL_MESSAGE_HOST": self.kafka.host, - "OSMPOL_MESSAGE_PORT": self.kafka.port, - # Database configuration - "OSMPOL_DATABASE_DRIVER": "mongo", - } - ) - container_builder.add_secret_envs( - mongodb_secret_name, {"OSMPOL_DATABASE_URI": "uri"} - ) - container_builder.add_secret_envs( - mysql_secret_name, {"OSMPOL_SQL_DATABASE_URI": "uri"} - ) - container = container_builder.build() - - # Add Pod restart policy - restart_policy = PodRestartPolicy() - restart_policy.add_secrets( - secret_names=(mongodb_secret_name, mysql_secret_name) - ) - pod_spec_builder.set_restart_policy(restart_policy) - - # Add container to pod spec - pod_spec_builder.add_container(container) - - return pod_spec_builder.build() - - -VSCODE_WORKSPACE = { - "folders": [ - {"path": "/usr/lib/python3/dist-packages/osm_policy_module"}, - {"path": "/usr/lib/python3/dist-packages/osm_common"}, - ], - "settings": {}, - "launch": { - "version": "0.2.0", - "configurations": [ - { - "name": "POL", - "type": "python", - "request": "launch", - "module": "osm_policy_module.cmd.policy_module_agent", - "justMyCode": False, - } - ], - }, -} - - -if __name__ == "__main__": - main(PolCharm) diff --git a/installers/charm/pol/src/pod_spec.py b/installers/charm/pol/src/pod_spec.py deleted file mode 100644 index 5ad4217a..00000000 --- a/installers/charm/pol/src/pod_spec.py +++ /dev/null @@ -1,198 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import logging -from typing import Any, Dict, List, NoReturn - -logger = logging.getLogger(__name__) - - -def _validate_data( - config_data: Dict[str, Any], relation_data: Dict[str, Any] -) -> NoReturn: - """Validate input data. - - Args: - config_data (Dict[str, Any]): configuration data. - relation_data (Dict[str, Any]): relation data. - """ - config_validators = { - "log_level": lambda value, _: ( - isinstance(value, str) and value in ("INFO", "DEBUG") - ), - } - relation_validators = { - "message_host": lambda value, _: isinstance(value, str) and len(value) > 0, - "message_port": lambda value, _: isinstance(value, int) and value > 0, - "database_uri": lambda value, _: ( - isinstance(value, str) and value.startswith("mongodb://") - ), - } - problems = [] - - for key, validator in config_validators.items(): - valid = validator(config_data.get(key), config_data) - - if not valid: - problems.append(key) - - for key, validator in relation_validators.items(): - valid = validator(relation_data.get(key), relation_data) - - if not valid: - problems.append(key) - - if len(problems) > 0: - raise ValueError("Errors found in: {}".format(", ".join(problems))) - - -def _make_pod_ports(port: int) -> List[Dict[str, Any]]: - """Generate pod ports details. - - Args: - port (int): port to expose. - - Returns: - List[Dict[str, Any]]: pod port details. - """ - return [{"name": "pol", "containerPort": port, "protocol": "TCP"}] - - -def _make_pod_envconfig( - config: Dict[str, Any], relation_state: Dict[str, Any] -) -> Dict[str, Any]: - """Generate pod environment configuration. - - Args: - config (Dict[str, Any]): configuration information. - relation_state (Dict[str, Any]): relation state information. - - Returns: - Dict[str, Any]: pod environment configuration. - """ - envconfig = { - # General configuration - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMPOL_GLOBAL_LOGLEVEL": config["log_level"], - # Kafka configuration - "OSMPOL_MESSAGE_HOST": relation_state["message_host"], - "OSMPOL_MESSAGE_DRIVER": "kafka", - "OSMPOL_MESSAGE_PORT": relation_state["message_port"], - # Database configuration - "OSMPOL_DATABASE_DRIVER": "mongo", - "OSMPOL_DATABASE_URI": relation_state["database_uri"], - } - - return envconfig - - -def _make_startup_probe() -> Dict[str, Any]: - """Generate startup probe. - - Returns: - Dict[str, Any]: startup probe. - """ - return { - "exec": {"command": ["/usr/bin/pgrep", "python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - -def _make_readiness_probe() -> Dict[str, Any]: - """Generate readiness probe. - - Returns: - Dict[str, Any]: readiness probe. - """ - return { - "exec": { - "command": ["sh", "-c", "osm-pol-healthcheck || exit 1"], - }, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - -def _make_liveness_probe() -> Dict[str, Any]: - """Generate liveness probe. - - Returns: - Dict[str, Any]: liveness probe. - """ - return { - "exec": { - "command": ["sh", "-c", "osm-pol-healthcheck || exit 1"], - }, - "initialDelaySeconds": 45, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - -def make_pod_spec( - image_info: Dict[str, str], - config: Dict[str, Any], - relation_state: Dict[str, Any], - app_name: str = "pol", - port: int = 80, -) -> Dict[str, Any]: - """Generate the pod spec information. - - Args: - image_info (Dict[str, str]): Object provided by - OCIImageResource("image").fetch(). - config (Dict[str, Any]): Configuration information. - relation_state (Dict[str, Any]): Relation state information. - app_name (str, optional): Application name. Defaults to "pol". - port (int, optional): Port for the container. Defaults to 80. - - Returns: - Dict[str, Any]: Pod spec dictionary for the charm. - """ - if not image_info: - return None - - _validate_data(config, relation_state) - - ports = _make_pod_ports(port) - env_config = _make_pod_envconfig(config, relation_state) - - return { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": ports, - "envConfig": env_config, - } - ], - "kubernetesResources": { - "ingressResources": [], - }, - } diff --git a/installers/charm/pol/tests/__init__.py b/installers/charm/pol/tests/__init__.py deleted file mode 100644 index 446d5cee..00000000 --- a/installers/charm/pol/tests/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -"""Init mocking for unit tests.""" - -import sys - - -import mock - - -class OCIImageResourceErrorMock(Exception): - pass - - -sys.path.append("src") - -oci_image = mock.MagicMock() -oci_image.OCIImageResourceError = OCIImageResourceErrorMock -sys.modules["oci_image"] = oci_image -sys.modules["oci_image"].OCIImageResource().fetch.return_value = {} diff --git a/installers/charm/pol/tests/test_charm.py b/installers/charm/pol/tests/test_charm.py deleted file mode 100644 index 6cf435d0..00000000 --- a/installers/charm/pol/tests/test_charm.py +++ /dev/null @@ -1,326 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import sys -from typing import NoReturn -import unittest - -from charm import PolCharm -from ops.model import ActiveStatus, BlockedStatus -from ops.testing import Harness - - -class TestCharm(unittest.TestCase): - """Pol Charm unit tests.""" - - def setUp(self) -> NoReturn: - """Test setup""" - self.image_info = sys.modules["oci_image"].OCIImageResource().fetch() - self.harness = Harness(PolCharm) - self.harness.set_leader(is_leader=True) - self.harness.begin() - self.config = { - "log_level": "INFO", - "mongodb_uri": "", - } - self.harness.update_config(self.config) - - def test_config_changed_no_relations( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue( - all( - relation in self.harness.charm.unit.status.message - for relation in ["mongodb", "kafka"] - ) - ) - - def test_config_changed_non_leader( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - self.harness.set_leader(is_leader=False) - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus) - - def test_with_relations_and_mongodb_config( - self, - ) -> NoReturn: - "Test with relations and mongodb config (internal)" - self.initialize_mysql_relation() - self.initialize_kafka_relation() - self.initialize_mongo_config() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_with_relations( - self, - ) -> NoReturn: - "Test with relations (internal)" - self.initialize_kafka_relation() - self.initialize_mongo_relation() - self.initialize_mysql_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_exception_mongodb_relation_and_config( - self, - ) -> NoReturn: - "Test with relation and config for Mongodb. Must fail" - self.initialize_mongo_relation() - self.initialize_mongo_config() - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_mysql_config_success(self): - self.initialize_kafka_relation() - self.initialize_mongo_relation() - self.initialize_mysql_config() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_mysql_config_wrong_value(self): - self.initialize_kafka_relation() - self.initialize_mongo_relation() - self.initialize_mysql_config(uri="wrong_uri") - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertIn( - "mysql_uri is not properly formed", - self.harness.charm.unit.status.message, - ) - - def test_mysql_config_and_relation(self): - self.initialize_mysql_relation() - self.initialize_mysql_config() - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - # import pdb; pdb.set_trace() - self.assertIn( - "Mysql data cannot be provided via config and relation", - self.harness.charm.unit.status.message, - ) - - def initialize_kafka_relation(self): - kafka_relation_id = self.harness.add_relation("kafka", "kafka") - self.harness.add_relation_unit(kafka_relation_id, "kafka/0") - self.harness.update_relation_data( - kafka_relation_id, "kafka", {"host": "kafka", "port": 9092} - ) - - def initialize_mongo_config(self): - self.harness.update_config({"mongodb_uri": "mongodb://mongo:27017"}) - - def initialize_mongo_relation(self): - mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") - self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") - self.harness.update_relation_data( - mongodb_relation_id, - "mongodb/0", - {"connection_string": "mongodb://mongo:27017"}, - ) - - def initialize_mysql_config(self, uri=None): - self.harness.update_config( - {"mysql_uri": uri or "mysql://user:pass@mysql-host:3306/database"} - ) - - def initialize_mysql_relation(self): - mongodb_relation_id = self.harness.add_relation("mysql", "mysql") - self.harness.add_relation_unit(mongodb_relation_id, "mysql/0") - self.harness.update_relation_data( - mongodb_relation_id, - "mysql/0", - { - "user": "user", - "password": "pass", - "host": "host", - "port": "1234", - "database": "pol", - "root_password": "root_password", - }, - ) - - -if __name__ == "__main__": - unittest.main() - - -# class TestCharm(unittest.TestCase): -# """POL Charm unit tests.""" - -# def setUp(self) -> NoReturn: -# """Test setup""" -# self.harness = Harness(PolCharm) -# self.harness.set_leader(is_leader=True) -# self.harness.begin() - -# def test_on_start_without_relations(self) -> NoReturn: -# """Test installation without any relation.""" -# self.harness.charm.on.start.emit() - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - -# def test_on_start_with_relations(self) -> NoReturn: -# """Test deployment without keystone.""" -# expected_result = { -# "version": 3, -# "containers": [ -# { -# "name": "pol", -# "imageDetails": self.harness.charm.image.fetch(), -# "imagePullPolicy": "Always", -# "ports": [ -# { -# "name": "pol", -# "containerPort": 80, -# "protocol": "TCP", -# } -# ], -# "envConfig": { -# "ALLOW_ANONYMOUS_LOGIN": "yes", -# "OSMPOL_GLOBAL_LOGLEVEL": "INFO", -# "OSMPOL_MESSAGE_HOST": "kafka", -# "OSMPOL_MESSAGE_DRIVER": "kafka", -# "OSMPOL_MESSAGE_PORT": 9092, -# "OSMPOL_DATABASE_DRIVER": "mongo", -# "OSMPOL_DATABASE_URI": "mongodb://mongo:27017", -# }, -# } -# ], -# "kubernetesResources": {"ingressResources": []}, -# } - -# self.harness.charm.on.start.emit() - -# # Check if kafka datastore is initialized -# self.assertIsNone(self.harness.charm.state.message_host) -# self.assertIsNone(self.harness.charm.state.message_port) - -# # Check if mongodb datastore is initialized -# self.assertIsNone(self.harness.charm.state.database_uri) - -# # Initializing the kafka relation -# kafka_relation_id = self.harness.add_relation("kafka", "kafka") -# self.harness.add_relation_unit(kafka_relation_id, "kafka/0") -# self.harness.update_relation_data( -# kafka_relation_id, "kafka/0", {"host": "kafka", "port": 9092} -# ) - -# # Initializing the mongo relation -# mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") -# self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") -# self.harness.update_relation_data( -# mongodb_relation_id, -# "mongodb/0", -# {"connection_string": "mongodb://mongo:27017"}, -# ) - -# # Checking if kafka data is stored -# self.assertEqual(self.harness.charm.state.message_host, "kafka") -# self.assertEqual(self.harness.charm.state.message_port, 9092) - -# # Checking if mongodb data is stored -# self.assertEqual(self.harness.charm.state.database_uri, "mongodb://mongo:27017") - -# # Verifying status -# self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# pod_spec, _ = self.harness.get_pod_spec() - -# self.assertDictEqual(expected_result, pod_spec) - -# def test_on_kafka_unit_relation_changed(self) -> NoReturn: -# """Test to see if kafka relation is updated.""" -# self.harness.charm.on.start.emit() - -# self.assertIsNone(self.harness.charm.state.message_host) -# self.assertIsNone(self.harness.charm.state.message_port) - -# relation_id = self.harness.add_relation("kafka", "kafka") -# self.harness.add_relation_unit(relation_id, "kafka/0") -# self.harness.update_relation_data( -# relation_id, "kafka/0", {"host": "kafka", "port": 9092} -# ) - -# self.assertEqual(self.harness.charm.state.message_host, "kafka") -# self.assertEqual(self.harness.charm.state.message_port, 9092) - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertNotIn("kafka", self.harness.charm.unit.status.message) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relation")) - -# def test_on_mongodb_unit_relation_changed(self) -> NoReturn: -# """Test to see if mongodb relation is updated.""" -# self.harness.charm.on.start.emit() - -# self.assertIsNone(self.harness.charm.state.database_uri) - -# relation_id = self.harness.add_relation("mongodb", "mongodb") -# self.harness.add_relation_unit(relation_id, "mongodb/0") -# self.harness.update_relation_data( -# relation_id, "mongodb/0", {"connection_string": "mongodb://mongo:27017"} -# ) - -# self.assertEqual(self.harness.charm.state.database_uri, "mongodb://mongo:27017") - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertNotIn("mongodb", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relation")) - - -# if __name__ == "__main__": -# unittest.main() diff --git a/installers/charm/pol/tests/test_pod_spec.py b/installers/charm/pol/tests/test_pod_spec.py deleted file mode 100644 index eb5f5cf3..00000000 --- a/installers/charm/pol/tests/test_pod_spec.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -from typing import NoReturn -import unittest - -import pod_spec - - -class TestPodSpec(unittest.TestCase): - """Pod spec unit tests.""" - - def test_make_pod_ports(self) -> NoReturn: - """Testing make pod ports.""" - port = 80 - - expected_result = [ - { - "name": "pol", - "containerPort": port, - "protocol": "TCP", - } - ] - - pod_ports = pod_spec._make_pod_ports(port) - - self.assertListEqual(expected_result, pod_ports) - - def test_make_pod_envconfig(self) -> NoReturn: - """Teting make pod envconfig.""" - config = { - "log_level": "INFO", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - } - - expected_result = { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMPOL_GLOBAL_LOGLEVEL": config["log_level"], - "OSMPOL_MESSAGE_HOST": relation_state["message_host"], - "OSMPOL_MESSAGE_DRIVER": "kafka", - "OSMPOL_MESSAGE_PORT": relation_state["message_port"], - "OSMPOL_DATABASE_DRIVER": "mongo", - "OSMPOL_DATABASE_URI": relation_state["database_uri"], - } - - pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state) - - self.assertDictEqual(expected_result, pod_envconfig) - - def test_make_startup_probe(self) -> NoReturn: - """Testing make startup probe.""" - expected_result = { - "exec": {"command": ["/usr/bin/pgrep", "python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - startup_probe = pod_spec._make_startup_probe() - - self.assertDictEqual(expected_result, startup_probe) - - def test_make_readiness_probe(self) -> NoReturn: - """Testing make readiness probe.""" - expected_result = { - "exec": { - "command": ["sh", "-c", "osm-pol-healthcheck || exit 1"], - }, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - readiness_probe = pod_spec._make_readiness_probe() - - self.assertDictEqual(expected_result, readiness_probe) - - def test_make_liveness_probe(self) -> NoReturn: - """Testing make liveness probe.""" - expected_result = { - "exec": { - "command": ["sh", "-c", "osm-pol-healthcheck || exit 1"], - }, - "initialDelaySeconds": 45, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - liveness_probe = pod_spec._make_liveness_probe() - - self.assertDictEqual(expected_result, liveness_probe) - - def test_make_pod_spec(self) -> NoReturn: - """Testing make pod spec.""" - image_info = {"upstream-source": "opensourcemano/pol:8"} - config = { - "log_level": "INFO", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - } - app_name = "pol" - port = 80 - - expected_result = { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": [ - { - "name": app_name, - "containerPort": port, - "protocol": "TCP", - } - ], - "envConfig": { - "ALLOW_ANONYMOUS_LOGIN": "yes", - "OSMPOL_GLOBAL_LOGLEVEL": config["log_level"], - "OSMPOL_MESSAGE_HOST": relation_state["message_host"], - "OSMPOL_MESSAGE_DRIVER": "kafka", - "OSMPOL_MESSAGE_PORT": relation_state["message_port"], - "OSMPOL_DATABASE_DRIVER": "mongo", - "OSMPOL_DATABASE_URI": relation_state["database_uri"], - }, - } - ], - "kubernetesResources": {"ingressResources": []}, - } - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertDictEqual(expected_result, spec) - - def test_make_pod_spec_without_image_info(self) -> NoReturn: - """Testing make pod spec without image_info.""" - image_info = None - config = { - "log_level": "INFO", - } - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - } - app_name = "pol" - port = 80 - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertIsNone(spec) - - def test_make_pod_spec_without_config(self) -> NoReturn: - """Testing make pod spec without config.""" - image_info = {"upstream-source": "opensourcemano/pol:8"} - config = {} - relation_state = { - "message_host": "kafka", - "message_port": 9090, - "database_uri": "mongodb://mongo", - } - app_name = "pol" - port = 80 - - with self.assertRaises(ValueError): - pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port) - - def test_make_pod_spec_without_relation_state(self) -> NoReturn: - """Testing make pod spec without relation_state.""" - image_info = {"upstream-source": "opensourcemano/pol:8"} - config = { - "log_level": "INFO", - } - relation_state = {} - app_name = "pol" - port = 80 - - with self.assertRaises(ValueError): - pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port) - - -if __name__ == "__main__": - unittest.main() diff --git a/installers/charm/pol/tox.ini b/installers/charm/pol/tox.ini deleted file mode 100644 index f3c91440..00000000 --- a/installers/charm/pol/tox.ini +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## -####################################################################################### - -[tox] -envlist = black, cover, flake8, pylint, yamllint, safety -skipsdist = true - -[tox:jenkins] -toxworkdir = /tmp/.tox - -[testenv] -basepython = python3.8 -setenv = - VIRTUAL_ENV={envdir} - PYTHONPATH = {toxinidir}:{toxinidir}/lib:{toxinidir}/src - PYTHONDONTWRITEBYTECODE = 1 -deps = -r{toxinidir}/requirements.txt - - -####################################################################################### -[testenv:black] -deps = black -commands = - black --check --diff src/ tests/ - - -####################################################################################### -[testenv:cover] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - coverage - nose2 -commands = - sh -c 'rm -f nosetests.xml' - coverage erase - nose2 -C --coverage src - coverage report --omit='*tests*' - coverage html -d ./cover --omit='*tests*' - coverage xml -o coverage.xml --omit=*tests* -whitelist_externals = sh - - -####################################################################################### -[testenv:flake8] -deps = flake8 - flake8-import-order -commands = - flake8 src/ tests/ - - -####################################################################################### -[testenv:pylint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - pylint==2.10.2 -commands = - pylint -E src/ tests/ - - -####################################################################################### -[testenv:safety] -setenv = - LC_ALL=C.UTF-8 - LANG=C.UTF-8 -deps = {[testenv]deps} - safety -commands = - - safety check --full-report - - -####################################################################################### -[testenv:yamllint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - yamllint -commands = yamllint . - -####################################################################################### -[testenv:build] -passenv=HTTP_PROXY HTTPS_PROXY NO_PROXY -whitelist_externals = - charmcraft - sh -commands = - charmcraft pack - sh -c 'ubuntu_version=20.04; \ - architectures="amd64-aarch64-arm64"; \ - charm_name=`cat metadata.yaml | grep -E "^name: " | cut -f 2 -d " "`; \ - mv $charm_name"_ubuntu-"$ubuntu_version-$architectures.charm $charm_name.charm' - -####################################################################################### -[flake8] -ignore = - W291, - W293, - W503, - E123, - E125, - E226, - E241, -exclude = - .git, - __pycache__, - .tox, -max-line-length = 120 -show-source = True -builtins = _ -max-complexity = 10 -import-order-style = google diff --git a/installers/charm/release_edge.sh b/installers/charm/release_edge.sh deleted file mode 100755 index 67d0b316..00000000 --- a/installers/charm/release_edge.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/bash -# 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. -set -eux - -channel=edge -tag=testing-daily - -# 1. Build charms -./build.sh - - -# New charms (with resources) -charms="ng-ui nbi pla keystone ro lcm mon pol" -for charm in $charms; do - echo "Releasing $charm charm" - cs_revision=$(charm push $charm/$charm.charm cs:~charmed-osm/$charm | tail -n +1 | head -1 | awk '{print $2}') - resource_revision=$(charm attach $cs_revision image=external::opensourcemano/$charm:$tag | tail -n +1 | sed 's/[^0-9]*//g') - image_revision_num=$(echo $resource_revision | awk '{print $NF}') - resources_string="--resource image-$image_revision_num" - charm release --channel $channel $cs_revision $resources_string - echo "$charm charm released!" -done - -charms="mongodb-exporter kafka-exporter mysqld-exporter" -for charm in $charms; do - echo "Releasing $charm charm" - cs_revision=$(charm push $charm/$charm.charm cs:~charmed-osm/$charm | tail -n +1 | head -1 | awk '{print $2}') - resource_revision=$(charm attach $cs_revision image=external::bitnami/$charm:latest | tail -n +1 | sed 's/[^0-9]*//g') - image_revision_num=$(echo $resource_revision | awk '{print $NF}') - resources_string="--resource image-$image_revision_num" - charm release --channel $channel $cs_revision $resources_string - echo "$charm charm released!" -done - -charm="prometheus" -echo "Releasing $charm charm" -cs_revision=$(charm push $charm/$charm.charm cs:~charmed-osm/$charm | tail -n +1 | head -1 | awk '{print $2}') -resource_revision=$(charm attach $cs_revision image=external::ubuntu/$charm:latest | tail -n +1 | sed 's/[^0-9]*//g') -image_revision_num=$(echo $resource_revision | awk '{print $NF}') -backup_resource_revision=$(charm attach $cs_revision backup-image=external::ed1000/prometheus-backup:latest | tail -n +1 | sed 's/[^0-9]*//g') -backup_image_revision_num=$(echo $backup_resource_revision | awk '{print $NF}') -resources_string="--resource image-$image_revision_num --resource backup-image-$backup_image_revision_num" -charm release --channel $channel $cs_revision $resources_string -echo "$charm charm released!" - - -charm="grafana" -echo "Releasing $charm charm" -cs_revision=$(charm push $charm/$charm.charm cs:~charmed-osm/$charm | tail -n +1 | head -1 | awk '{print $2}') -resource_revision=$(charm attach $cs_revision image=external::ubuntu/$charm:latest | tail -n +1 | sed 's/[^0-9]*//g') -image_revision_num=$(echo $resource_revision | awk '{print $NF}') -resources_string="--resource image-$image_revision_num" -charm release --channel $channel $cs_revision $resources_string -echo "$charm charm released!" - - -charm="zookeeper" -echo "Releasing $charm charm" -cs_revision=$(charm push $charm/$charm.charm cs:~charmed-osm/$charm | tail -n +1 | head -1 | awk '{print $2}') -resource_revision=$(charm attach $cs_revision image=external::rocks.canonical.com:443/k8s.gcr.io/kubernetes-zookeeper:1.0-3.4.10 | tail -n +1 | sed 's/[^0-9]*//g') -image_revision_num=$(echo $resource_revision | awk '{print $NF}') -resources_string="--resource image-$image_revision_num" -charm release --channel $channel $cs_revision $resources_string -echo "$charm charm released!" - - -charm="kafka" -echo "Releasing $charm charm" -cs_revision=$(charm push $charm/$charm.charm cs:~charmed-osm/$charm | tail -n +1 | head -1 | awk '{print $2}') -resource_revision=$(charm attach $cs_revision image=external::rocks.canonical.com:443/wurstmeister/kafka:2.12-2.2.1 | tail -n +1 | sed 's/[^0-9]*//g') -image_revision_num=$(echo $resource_revision | awk '{print $NF}') -resources_string="--resource image-$image_revision_num" -charm release --channel $channel $cs_revision $resources_string -echo "$charm charm released!" - - -# 3. Grant permissions -all_charms="ng-ui nbi pla keystone ro lcm mon pol grafana prometheus mongodb-exporter kafka-exporter mysqld-exporter zookeeper kafka" -for charm in $all_charms; do - echo "Granting permission for $charm charm" - charm grant cs:~charmed-osm/$charm --channel $channel --acl read everyone -done diff --git a/installers/charm/ro/.gitignore b/installers/charm/ro/.gitignore deleted file mode 100644 index 2885df27..00000000 --- a/installers/charm/ro/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.stestr -cover -release \ No newline at end of file diff --git a/installers/charm/ro/.jujuignore b/installers/charm/ro/.jujuignore deleted file mode 100644 index 3ae3e7dc..00000000 --- a/installers/charm/ro/.jujuignore +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.gitignore -.stestr -cover -release -tests/ -requirements* -tox.ini diff --git a/installers/charm/ro/.yamllint.yaml b/installers/charm/ro/.yamllint.yaml deleted file mode 100644 index d71fb69f..00000000 --- a/installers/charm/ro/.yamllint.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - ---- -extends: default - -yaml-files: - - "*.yaml" - - "*.yml" - - ".yamllint" -ignore: | - .tox - cover/ - build/ - venv - release/ diff --git a/installers/charm/ro/README.md b/installers/charm/ro/README.md deleted file mode 100644 index 9cf42000..00000000 --- a/installers/charm/ro/README.md +++ /dev/null @@ -1,23 +0,0 @@ - - -# RO operator Charm for Kubernetes - -## Requirements diff --git a/installers/charm/ro/charmcraft.yaml b/installers/charm/ro/charmcraft.yaml deleted file mode 100644 index 0a285a9d..00000000 --- a/installers/charm/ro/charmcraft.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -type: charm -bases: - - build-on: - - name: ubuntu - channel: "20.04" - architectures: ["amd64"] - run-on: - - name: ubuntu - channel: "20.04" - architectures: - - amd64 - - aarch64 - - arm64 -parts: - charm: - build-packages: [git] diff --git a/installers/charm/ro/config.yaml b/installers/charm/ro/config.yaml deleted file mode 100644 index 31bf8cb0..00000000 --- a/installers/charm/ro/config.yaml +++ /dev/null @@ -1,116 +0,0 @@ -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -options: - enable_ng_ro: - description: Enable NG-RO - type: boolean - default: true - database_commonkey: - description: Database COMMON KEY - type: string - default: osm - mongodb_uri: - type: string - description: MongoDB URI (external database) - log_level: - description: "Log Level" - type: string - default: "INFO" - period_refresh_active: - type: int - description: | - Updates the VNF status from VIM for every given period of time seconds. - Values equal or greater than 60 is allowed. - Disable the updates from VIM by setting -1. - Example: - $ juju config ro period_refresh_active=-1 - $ juju config ro period_refresh_active=100 - mysql_host: - type: string - description: MySQL Host (external database) - mysql_port: - type: int - description: MySQL Port (external database) - mysql_user: - type: string - description: MySQL User (external database) - mysql_password: - type: string - description: MySQL Password (external database) - mysql_root_password: - type: string - description: MySQL Root Password (external database) - vim_database: - type: string - description: "The database name." - default: "mano_vim_db" - ro_database: - type: string - description: "The database name." - default: "mano_db" - openmano_tenant: - type: string - description: "Openmano Tenant" - default: "osm" - certificates: - type: string - description: | - comma-separated list of : certificates. - Where: - name: name of the file for the certificate - content: base64 content of the certificate - The path for the files is /certs. - image_pull_policy: - type: string - description: | - ImagePullPolicy configuration for the pod. - Possible values: always, ifnotpresent, never - default: always - debug_mode: - description: | - If true, debug mode is activated. It means that the service will not run, - and instead, the command for the container will be a `sleep infinity`. - Note: If enabled, security_context will be disabled. - type: boolean - default: false - debug_pubkey: - description: | - Public SSH key that will be injected to the application pod. - type: string - debug_ro_local_path: - description: | - Local full path to the RO project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - debug_common_local_path: - description: | - Local full path to the COMMON project. - - The path will be mounted to the docker image, - which means changes during the debugging will be saved in your local path. - type: string - security_context: - description: Enables the security context of the pods - type: boolean - default: false diff --git a/installers/charm/ro/lib/charms/kafka_k8s/v0/kafka.py b/installers/charm/ro/lib/charms/kafka_k8s/v0/kafka.py deleted file mode 100644 index 1baf9a88..00000000 --- a/installers/charm/ro/lib/charms/kafka_k8s/v0/kafka.py +++ /dev/null @@ -1,207 +0,0 @@ -# Copyright 2022 Canonical Ltd. -# See LICENSE file for licensing details. -# -# 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. - -"""Kafka library. - -This [library](https://juju.is/docs/sdk/libraries) implements both sides of the -`kafka` [interface](https://juju.is/docs/sdk/relations). - -The *provider* side of this interface is implemented by the -[kafka-k8s Charmed Operator](https://charmhub.io/kafka-k8s). - -Any Charmed Operator that *requires* Kafka for providing its -service should implement the *requirer* side of this interface. - -In a nutshell using this library to implement a Charmed Operator *requiring* -Kafka would look like - -``` -$ charmcraft fetch-lib charms.kafka_k8s.v0.kafka -``` - -`metadata.yaml`: - -``` -requires: - kafka: - interface: kafka - limit: 1 -``` - -`src/charm.py`: - -``` -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.charm import CharmBase - - -class MyCharm(CharmBase): - - on = KafkaEvents() - - def __init__(self, *args): - super().__init__(*args) - self.kafka = KafkaRequires(self) - self.framework.observe( - self.on.kafka_available, - self._on_kafka_available, - ) - self.framework.observe( - self.on.kafka_broken, - self._on_kafka_broken, - ) - - def _on_kafka_available(self, event): - # Get Kafka host and port - host: str = self.kafka.host - port: int = self.kafka.port - # host => "kafka-k8s" - # port => 9092 - - def _on_kafka_broken(self, event): - # Stop service - # ... - self.unit.status = BlockedStatus("need kafka relation") -``` - -You can file bugs -[here](https://github.com/charmed-osm/kafka-k8s-operator/issues)! -""" - -from typing import Optional - -from ops.charm import CharmBase, CharmEvents -from ops.framework import EventBase, EventSource, Object - -# The unique Charmhub library identifier, never change it -from ops.model import Relation - -LIBID = "eacc8c85082347c9aae740e0220b8376" - -# 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 = 3 - - -KAFKA_HOST_APP_KEY = "host" -KAFKA_PORT_APP_KEY = "port" - - -class _KafkaAvailableEvent(EventBase): - """Event emitted when Kafka is available.""" - - -class _KafkaBrokenEvent(EventBase): - """Event emitted when Kafka relation is broken.""" - - -class KafkaEvents(CharmEvents): - """Kafka events. - - This class defines the events that Kafka can emit. - - Events: - kafka_available (_KafkaAvailableEvent) - """ - - kafka_available = EventSource(_KafkaAvailableEvent) - kafka_broken = EventSource(_KafkaBrokenEvent) - - -class KafkaRequires(Object): - """Requires-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self.charm = charm - self._endpoint_name = endpoint_name - - # Observe relation events - event_observe_mapping = { - charm.on[self._endpoint_name].relation_changed: self._on_relation_changed, - charm.on[self._endpoint_name].relation_broken: self._on_relation_broken, - } - for event, observer in event_observe_mapping.items(): - self.framework.observe(event, observer) - - def _on_relation_changed(self, event) -> None: - if event.relation.app and all( - key in event.relation.data[event.relation.app] - for key in (KAFKA_HOST_APP_KEY, KAFKA_PORT_APP_KEY) - ): - self.charm.on.kafka_available.emit() - - def _on_relation_broken(self, _) -> None: - self.charm.on.kafka_broken.emit() - - @property - def host(self) -> str: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - relation.data[relation.app].get(KAFKA_HOST_APP_KEY) - if relation and relation.app - else None - ) - - @property - def port(self) -> int: - relation: Relation = self.model.get_relation(self._endpoint_name) - return ( - int(relation.data[relation.app].get(KAFKA_PORT_APP_KEY)) - if relation and relation.app - else None - ) - - -class KafkaProvides(Object): - """Provides-side of the Kafka relation.""" - - def __init__(self, charm: CharmBase, endpoint_name: str = "kafka") -> None: - super().__init__(charm, endpoint_name) - self._endpoint_name = endpoint_name - - def set_host_info(self, host: str, port: int, relation: Optional[Relation] = None) -> None: - """Set Kafka host and port. - - This function writes in the application data of the relation, therefore, - only the unit leader can call it. - - Args: - host (str): Kafka hostname or IP address. - port (int): Kafka port. - relation (Optional[Relation]): Relation to update. - If not specified, all relations will be updated. - - Raises: - Exception: if a non-leader unit calls this function. - """ - if not self.model.unit.is_leader(): - raise Exception("only the leader set host information.") - - if relation: - self._update_relation_data(host, port, relation) - return - - for relation in self.model.relations[self._endpoint_name]: - self._update_relation_data(host, port, relation) - - def _update_relation_data(self, host: str, port: int, relation: Relation) -> None: - """Update data in relation if needed.""" - relation.data[self.model.app][KAFKA_HOST_APP_KEY] = host - relation.data[self.model.app][KAFKA_PORT_APP_KEY] = str(port) diff --git a/installers/charm/ro/metadata.yaml b/installers/charm/ro/metadata.yaml deleted file mode 100644 index 6e82e8cc..00000000 --- a/installers/charm/ro/metadata.yaml +++ /dev/null @@ -1,53 +0,0 @@ -# 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -name: osm-ro -summary: OSM Resource Orchestrator (RO) -description: | - A CAAS charm to deploy OSM's Resource Orchestrator (RO). -series: - - kubernetes -tags: - - kubernetes - - osm - - ro -min-juju-version: 2.8.0 -deployment: - type: stateless - service: cluster -resources: - image: - type: oci-image - description: OSM docker image for RO - upstream-source: "opensourcemano/ro:8" -provides: - ro: - interface: http -requires: - kafka: - interface: kafka - limit: 1 - mongodb: - interface: mongodb - limit: 1 - mysql: - interface: mysql - limit: 1 diff --git a/installers/charm/ro/requirements-test.txt b/installers/charm/ro/requirements-test.txt deleted file mode 100644 index cf61dd4e..00000000 --- a/installers/charm/ro/requirements-test.txt +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -mock==4.0.3 diff --git a/installers/charm/ro/requirements.txt b/installers/charm/ro/requirements.txt deleted file mode 100644 index 1a8928c7..00000000 --- a/installers/charm/ro/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master \ No newline at end of file diff --git a/installers/charm/ro/src/charm.py b/installers/charm/ro/src/charm.py deleted file mode 100755 index 028dc0a4..00000000 --- a/installers/charm/ro/src/charm.py +++ /dev/null @@ -1,465 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -# pylint: disable=E0213 - -import base64 -import logging -from typing import Dict, NoReturn, Optional - -from charms.kafka_k8s.v0.kafka import KafkaEvents, KafkaRequires -from ops.main import main -from opslib.osm.charm import CharmedOsmBase, RelationsMissing -from opslib.osm.interfaces.mongo import MongoClient -from opslib.osm.interfaces.mysql import MysqlClient -from opslib.osm.pod import ( - ContainerV3Builder, - FilesV3Builder, - PodRestartPolicy, - PodSpecV3Builder, -) -from opslib.osm.validator import ModelValidator, validator - -logger = logging.getLogger(__name__) - -PORT = 9090 - - -def _check_certificate_data(name: str, content: str): - if not name or not content: - raise ValueError("certificate name and content must be a non-empty string") - - -def _extract_certificates(certs_config: str): - certificates = {} - if certs_config: - cert_list = certs_config.split(",") - for cert in cert_list: - name, content = cert.split(":") - _check_certificate_data(name, content) - certificates[name] = content - return certificates - - -def decode(content: str): - return base64.b64decode(content.encode("utf-8")).decode("utf-8") - - -class ConfigModel(ModelValidator): - enable_ng_ro: bool - database_commonkey: str - mongodb_uri: Optional[str] - log_level: str - mysql_host: Optional[str] - mysql_port: Optional[int] - mysql_user: Optional[str] - mysql_password: Optional[str] - mysql_root_password: Optional[str] - vim_database: str - ro_database: str - openmano_tenant: str - certificates: Optional[str] - image_pull_policy: str - debug_mode: bool - security_context: bool - period_refresh_active: Optional[int] - - @validator("log_level") - def validate_log_level(cls, v): - if v not in {"INFO", "DEBUG"}: - raise ValueError("value must be INFO or DEBUG") - return v - - @validator("certificates") - def validate_certificates(cls, v): - # Raises an exception if it cannot extract the certificates - _extract_certificates(v) - return v - - @validator("mongodb_uri") - def validate_mongodb_uri(cls, v): - if v and not v.startswith("mongodb://"): - raise ValueError("mongodb_uri is not properly formed") - return v - - @validator("mysql_port") - def validate_mysql_port(cls, v): - if v and (v <= 0 or v >= 65535): - raise ValueError("Mysql port out of range") - return v - - @validator("image_pull_policy") - def validate_image_pull_policy(cls, v): - values = { - "always": "Always", - "ifnotpresent": "IfNotPresent", - "never": "Never", - } - v = v.lower() - if v not in values.keys(): - raise ValueError("value must be always, ifnotpresent or never") - return values[v] - - @property - def certificates_dict(cls): - return _extract_certificates(cls.certificates) if cls.certificates else {} - - @validator("period_refresh_active") - def validate_vim_refresh_period(cls, v): - if v and v < 60 and v != -1: - raise ValueError( - "Refresh Period is too tight, insert >= 60 seconds or disable using -1" - ) - return v - - -class RoCharm(CharmedOsmBase): - """GrafanaCharm Charm.""" - - on = KafkaEvents() - - def __init__(self, *args) -> NoReturn: - """Prometheus Charm constructor.""" - super().__init__( - *args, - oci_image="image", - vscode_workspace=VSCODE_WORKSPACE, - ) - if self.config.get("debug_mode"): - self.enable_debug_mode( - pubkey=self.config.get("debug_pubkey"), - hostpaths={ - "osm_common": { - "hostpath": self.config.get("debug_common_local_path"), - "container-path": "/usr/lib/python3/dist-packages/osm_common", - }, - **_get_ro_host_paths(self.config.get("debug_ro_local_path")), - }, - ) - self.kafka = KafkaRequires(self) - self.framework.observe(self.on.kafka_available, self.configure_pod) - self.framework.observe(self.on.kafka_broken, self.configure_pod) - - self.mysql_client = MysqlClient(self, "mysql") - self.framework.observe(self.on["mysql"].relation_changed, self.configure_pod) - self.framework.observe(self.on["mysql"].relation_broken, self.configure_pod) - - self.mongodb_client = MongoClient(self, "mongodb") - self.framework.observe(self.on["mongodb"].relation_changed, self.configure_pod) - self.framework.observe(self.on["mongodb"].relation_broken, self.configure_pod) - - self.framework.observe(self.on["ro"].relation_joined, self._publish_ro_info) - - def _publish_ro_info(self, event): - """Publishes RO information. - - Args: - event (EventBase): RO relation event. - """ - if self.unit.is_leader(): - rel_data = { - "host": self.model.app.name, - "port": str(PORT), - } - for k, v in rel_data.items(): - event.relation.data[self.app][k] = v - - def _check_missing_dependencies(self, config: ConfigModel): - missing_relations = [] - - if config.enable_ng_ro: - if not self.kafka.host or not self.kafka.port: - missing_relations.append("kafka") - if not config.mongodb_uri and self.mongodb_client.is_missing_data_in_unit(): - missing_relations.append("mongodb") - else: - if not config.mysql_host and self.mysql_client.is_missing_data_in_unit(): - missing_relations.append("mysql") - if missing_relations: - raise RelationsMissing(missing_relations) - - def _validate_mysql_config(self, config: ConfigModel): - invalid_values = [] - if not config.mysql_user: - invalid_values.append("Mysql user is empty") - if not config.mysql_password: - invalid_values.append("Mysql password is empty") - if not config.mysql_root_password: - invalid_values.append("Mysql root password empty") - - if invalid_values: - raise ValueError("Invalid values: " + ", ".join(invalid_values)) - - def _build_cert_files( - self, - config: ConfigModel, - ): - cert_files_builder = FilesV3Builder() - for name, content in config.certificates_dict.items(): - cert_files_builder.add_file(name, decode(content), mode=0o600) - return cert_files_builder.build() - - def build_pod_spec(self, image_info): - # Validate config - config = ConfigModel(**dict(self.config)) - - if config.enable_ng_ro: - if config.mongodb_uri and not self.mongodb_client.is_missing_data_in_unit(): - raise Exception( - "Mongodb data cannot be provided via config and relation" - ) - else: - if config.mysql_host and not self.mysql_client.is_missing_data_in_unit(): - raise Exception("Mysql data cannot be provided via config and relation") - - if config.mysql_host: - self._validate_mysql_config(config) - - # Check relations - self._check_missing_dependencies(config) - - security_context_enabled = ( - config.security_context if not config.debug_mode else False - ) - - # Create Builder for the PodSpec - pod_spec_builder = PodSpecV3Builder( - enable_security_context=security_context_enabled - ) - - # Build Container - container_builder = ContainerV3Builder( - self.app.name, - image_info, - config.image_pull_policy, - run_as_non_root=security_context_enabled, - ) - certs_files = self._build_cert_files(config) - - if certs_files: - container_builder.add_volume_config("certs", "/certs", certs_files) - - container_builder.add_port(name=self.app.name, port=PORT) - container_builder.add_http_readiness_probe( - "/ro/" if config.enable_ng_ro else "/openmano/tenants", - PORT, - initial_delay_seconds=10, - period_seconds=10, - timeout_seconds=5, - failure_threshold=3, - ) - container_builder.add_http_liveness_probe( - "/ro/" if config.enable_ng_ro else "/openmano/tenants", - PORT, - initial_delay_seconds=600, - period_seconds=10, - timeout_seconds=5, - failure_threshold=3, - ) - container_builder.add_envs( - { - "OSMRO_LOG_LEVEL": config.log_level, - } - ) - if config.period_refresh_active: - container_builder.add_envs( - { - "OSMRO_PERIOD_REFRESH_ACTIVE": config.period_refresh_active, - } - ) - if config.enable_ng_ro: - # Add secrets to the pod - mongodb_secret_name = f"{self.app.name}-mongodb-secret" - pod_spec_builder.add_secret( - mongodb_secret_name, - { - "uri": config.mongodb_uri or self.mongodb_client.connection_string, - "commonkey": config.database_commonkey, - }, - ) - container_builder.add_envs( - { - "OSMRO_MESSAGE_DRIVER": "kafka", - "OSMRO_MESSAGE_HOST": self.kafka.host, - "OSMRO_MESSAGE_PORT": self.kafka.port, - # MongoDB configuration - "OSMRO_DATABASE_DRIVER": "mongo", - } - ) - container_builder.add_secret_envs( - secret_name=mongodb_secret_name, - envs={ - "OSMRO_DATABASE_URI": "uri", - "OSMRO_DATABASE_COMMONKEY": "commonkey", - }, - ) - restart_policy = PodRestartPolicy() - restart_policy.add_secrets(secret_names=(mongodb_secret_name,)) - pod_spec_builder.set_restart_policy(restart_policy) - - else: - container_builder.add_envs( - { - "RO_DB_HOST": config.mysql_host or self.mysql_client.host, - "RO_DB_OVIM_HOST": config.mysql_host or self.mysql_client.host, - "RO_DB_PORT": config.mysql_port or self.mysql_client.port, - "RO_DB_OVIM_PORT": config.mysql_port or self.mysql_client.port, - "RO_DB_USER": config.mysql_user or self.mysql_client.user, - "RO_DB_OVIM_USER": config.mysql_user or self.mysql_client.user, - "RO_DB_PASSWORD": config.mysql_password - or self.mysql_client.password, - "RO_DB_OVIM_PASSWORD": config.mysql_password - or self.mysql_client.password, - "RO_DB_ROOT_PASSWORD": config.mysql_root_password - or self.mysql_client.root_password, - "RO_DB_OVIM_ROOT_PASSWORD": config.mysql_root_password - or self.mysql_client.root_password, - "RO_DB_NAME": config.ro_database, - "RO_DB_OVIM_NAME": config.vim_database, - "OPENMANO_TENANT": config.openmano_tenant, - } - ) - container = container_builder.build() - - # Add container to pod spec - pod_spec_builder.add_container(container) - - return pod_spec_builder.build() - - -VSCODE_WORKSPACE = { - "folders": [ - {"path": "/usr/lib/python3/dist-packages/osm_ng_ro"}, - {"path": "/usr/lib/python3/dist-packages/osm_common"}, - {"path": "/usr/lib/python3/dist-packages/osm_ro_plugin"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_arista_cloudvision"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_dpb"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_dynpac"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_floodlightof"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_ietfl2vpn"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_juniper_contrail"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_odlof"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_onos_vpls"}, - {"path": "/usr/lib/python3/dist-packages/osm_rosdn_onosof"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_aws"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_azure"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_gcp"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_openstack"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_openvim"}, - {"path": "/usr/lib/python3/dist-packages/osm_rovim_vmware"}, - ], - "launch": { - "configurations": [ - { - "module": "osm_ng_ro.ro_main", - "name": "NG RO", - "request": "launch", - "type": "python", - "justMyCode": False, - } - ], - "version": "0.2.0", - }, - "settings": {}, -} - - -def _get_ro_host_paths(ro_host_path: str) -> Dict: - """Get RO host paths""" - return ( - { - "NG-RO": { - "hostpath": f"{ro_host_path}/NG-RO", - "container-path": "/usr/lib/python3/dist-packages/osm_ng_ro", - }, - "RO-plugin": { - "hostpath": f"{ro_host_path}/RO-plugin", - "container-path": "/usr/lib/python3/dist-packages/osm_ro_plugin", - }, - "RO-SDN-arista_cloudvision": { - "hostpath": f"{ro_host_path}/RO-SDN-arista_cloudvision", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_arista_cloudvision", - }, - "RO-SDN-dpb": { - "hostpath": f"{ro_host_path}/RO-SDN-dpb", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_dpb", - }, - "RO-SDN-dynpac": { - "hostpath": f"{ro_host_path}/RO-SDN-dynpac", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_dynpac", - }, - "RO-SDN-floodlight_openflow": { - "hostpath": f"{ro_host_path}/RO-SDN-floodlight_openflow", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_floodlightof", - }, - "RO-SDN-ietfl2vpn": { - "hostpath": f"{ro_host_path}/RO-SDN-ietfl2vpn", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_ietfl2vpn", - }, - "RO-SDN-juniper_contrail": { - "hostpath": f"{ro_host_path}/RO-SDN-juniper_contrail", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_juniper_contrail", - }, - "RO-SDN-odl_openflow": { - "hostpath": f"{ro_host_path}/RO-SDN-odl_openflow", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_odlof", - }, - "RO-SDN-onos_openflow": { - "hostpath": f"{ro_host_path}/RO-SDN-onos_openflow", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_onosof", - }, - "RO-SDN-onos_vpls": { - "hostpath": f"{ro_host_path}/RO-SDN-onos_vpls", - "container-path": "/usr/lib/python3/dist-packages/osm_rosdn_onos_vpls", - }, - "RO-VIM-aws": { - "hostpath": f"{ro_host_path}/RO-VIM-aws", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_aws", - }, - "RO-VIM-azure": { - "hostpath": f"{ro_host_path}/RO-VIM-azure", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_azure", - }, - "RO-VIM-gcp": { - "hostpath": f"{ro_host_path}/RO-VIM-gcp", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_gcp", - }, - "RO-VIM-openstack": { - "hostpath": f"{ro_host_path}/RO-VIM-openstack", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_openstack", - }, - "RO-VIM-openvim": { - "hostpath": f"{ro_host_path}/RO-VIM-openvim", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_openvim", - }, - "RO-VIM-vmware": { - "hostpath": f"{ro_host_path}/RO-VIM-vmware", - "container-path": "/usr/lib/python3/dist-packages/osm_rovim_vmware", - }, - } - if ro_host_path - else {} - ) - - -if __name__ == "__main__": - main(RoCharm) diff --git a/installers/charm/ro/src/pod_spec.py b/installers/charm/ro/src/pod_spec.py deleted file mode 100644 index 1beba17a..00000000 --- a/installers/charm/ro/src/pod_spec.py +++ /dev/null @@ -1,276 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import logging -from typing import Any, Dict, List, NoReturn - -logger = logging.getLogger(__name__) - - -def _validate_data( - config_data: Dict[str, Any], relation_data: Dict[str, Any] -) -> NoReturn: - """Validates passed information. - - Args: - config_data (Dict[str, Any]): configuration information. - relation_data (Dict[str, Any]): relation information - - Raises: - ValueError: when config and/or relation data is not valid. - """ - config_validators = { - "enable_ng_ro": lambda value, _: isinstance(value, bool), - "database_commonkey": lambda value, values: ( - isinstance(value, str) and len(value) > 0 - ) - if values.get("enable_ng_ro", True) - else True, - "log_level": lambda value, _: ( - isinstance(value, str) and value in ("INFO", "DEBUG") - ), - "vim_database": lambda value, values: ( - isinstance(value, str) and len(value) > 0 - ) - if not values.get("enable_ng_ro", True) - else True, - "ro_database": lambda value, values: (isinstance(value, str) and len(value) > 0) - if not values.get("enable_ng_ro", True) - else True, - "openmano_tenant": lambda value, values: ( - isinstance(value, str) and len(value) > 0 - ) - if not values.get("enable_ng_ro", True) - else True, - } - relation_validators = { - "kafka_host": lambda value, _: (isinstance(value, str) and len(value) > 0) - if config_data.get("enable_ng_ro", True) - else True, - "kafka_port": lambda value, _: (isinstance(value, str) and len(value) > 0) - if config_data.get("enable_ng_ro", True) - else True, - "mongodb_connection_string": lambda value, _: ( - isinstance(value, str) and value.startswith("mongodb://") - ) - if config_data.get("enable_ng_ro", True) - else True, - "mysql_host": lambda value, _: (isinstance(value, str) and len(value) > 0) - if not config_data.get("enable_ng_ro", True) - else True, - "mysql_port": lambda value, _: (isinstance(value, int) and value > 0) - if not config_data.get("enable_ng_ro", True) - else True, - "mysql_user": lambda value, _: (isinstance(value, str) and len(value) > 0) - if not config_data.get("enable_ng_ro", True) - else True, - "mysql_password": lambda value, _: (isinstance(value, str) and len(value) > 0) - if not config_data.get("enable_ng_ro", True) - else True, - "mysql_root_password": lambda value, _: ( - isinstance(value, str) and len(value) > 0 - ) - if not config_data.get("enable_ng_ro", True) - else True, - } - problems = [] - - for key, validator in config_validators.items(): - valid = validator(config_data.get(key), config_data) - - if not valid: - problems.append(key) - - for key, validator in relation_validators.items(): - valid = validator(relation_data.get(key), relation_data) - - if not valid: - problems.append(key) - - if len(problems) > 0: - raise ValueError("Errors found in: {}".format(", ".join(problems))) - - -def _make_pod_ports(port: int) -> List[Dict[str, Any]]: - """Generate pod ports details. - - Args: - port (int): port to expose. - - Returns: - List[Dict[str, Any]]: pod port details. - """ - return [{"name": "ro", "containerPort": port, "protocol": "TCP"}] - - -def _make_pod_envconfig( - config: Dict[str, Any], relation_state: Dict[str, Any] -) -> Dict[str, Any]: - """Generate pod environment configuration. - - Args: - config (Dict[str, Any]): configuration information. - relation_state (Dict[str, Any]): relation state information. - - Returns: - Dict[str, Any]: pod environment configuration. - """ - envconfig = { - # General configuration - "OSMRO_LOG_LEVEL": config["log_level"], - } - - if config.get("enable_ng_ro", True): - # Kafka configuration - envconfig["OSMRO_MESSAGE_DRIVER"] = "kafka" - envconfig["OSMRO_MESSAGE_HOST"] = relation_state["kafka_host"] - envconfig["OSMRO_MESSAGE_PORT"] = relation_state["kafka_port"] - - # MongoDB configuration - envconfig["OSMRO_DATABASE_DRIVER"] = "mongo" - envconfig["OSMRO_DATABASE_URI"] = relation_state["mongodb_connection_string"] - envconfig["OSMRO_DATABASE_COMMONKEY"] = config["database_commonkey"] - else: - envconfig["RO_DB_HOST"] = relation_state["mysql_host"] - envconfig["RO_DB_OVIM_HOST"] = relation_state["mysql_host"] - envconfig["RO_DB_PORT"] = relation_state["mysql_port"] - envconfig["RO_DB_OVIM_PORT"] = relation_state["mysql_port"] - envconfig["RO_DB_USER"] = relation_state["mysql_user"] - envconfig["RO_DB_OVIM_USER"] = relation_state["mysql_user"] - envconfig["RO_DB_PASSWORD"] = relation_state["mysql_password"] - envconfig["RO_DB_OVIM_PASSWORD"] = relation_state["mysql_password"] - envconfig["RO_DB_ROOT_PASSWORD"] = relation_state["mysql_root_password"] - envconfig["RO_DB_OVIM_ROOT_PASSWORD"] = relation_state["mysql_root_password"] - envconfig["RO_DB_NAME"] = config["ro_database"] - envconfig["RO_DB_OVIM_NAME"] = config["vim_database"] - envconfig["OPENMANO_TENANT"] = config["openmano_tenant"] - - return envconfig - - -def _make_startup_probe() -> Dict[str, Any]: - """Generate startup probe. - - Returns: - Dict[str, Any]: startup probe. - """ - return { - "exec": {"command": ["/usr/bin/pgrep", "python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - -def _make_readiness_probe(port: int) -> Dict[str, Any]: - """Generate readiness probe. - - Args: - port (int): service port. - - Returns: - Dict[str, Any]: readiness probe. - """ - return { - "httpGet": { - "path": "/openmano/tenants", - "port": port, - }, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - -def _make_liveness_probe(port: int) -> Dict[str, Any]: - """Generate liveness probe. - - Args: - port (int): service port. - - Returns: - Dict[str, Any]: liveness probe. - """ - return { - "httpGet": { - "path": "/openmano/tenants", - "port": port, - }, - "initialDelaySeconds": 600, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - -def make_pod_spec( - image_info: Dict[str, str], - config: Dict[str, Any], - relation_state: Dict[str, Any], - app_name: str = "ro", - port: int = 9090, -) -> Dict[str, Any]: - """Generate the pod spec information. - - Args: - image_info (Dict[str, str]): Object provided by - OCIImageResource("image").fetch(). - config (Dict[str, Any]): Configuration information. - relation_state (Dict[str, Any]): Relation state information. - app_name (str, optional): Application name. Defaults to "ro". - port (int, optional): Port for the container. Defaults to 9090. - - Returns: - Dict[str, Any]: Pod spec dictionary for the charm. - """ - if not image_info: - return None - - _validate_data(config, relation_state) - - ports = _make_pod_ports(port) - env_config = _make_pod_envconfig(config, relation_state) - startup_probe = _make_startup_probe() - readiness_probe = _make_readiness_probe(port) - liveness_probe = _make_liveness_probe(port) - - return { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": ports, - "envConfig": env_config, - "kubernetes": { - "startupProbe": startup_probe, - "readinessProbe": readiness_probe, - "livenessProbe": liveness_probe, - }, - } - ], - "kubernetesResources": { - "ingressResources": [], - }, - } diff --git a/installers/charm/ro/tests/__init__.py b/installers/charm/ro/tests/__init__.py deleted file mode 100644 index 446d5cee..00000000 --- a/installers/charm/ro/tests/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -"""Init mocking for unit tests.""" - -import sys - - -import mock - - -class OCIImageResourceErrorMock(Exception): - pass - - -sys.path.append("src") - -oci_image = mock.MagicMock() -oci_image.OCIImageResourceError = OCIImageResourceErrorMock -sys.modules["oci_image"] = oci_image -sys.modules["oci_image"].OCIImageResource().fetch.return_value = {} diff --git a/installers/charm/ro/tests/test_charm.py b/installers/charm/ro/tests/test_charm.py deleted file mode 100644 index f18e7682..00000000 --- a/installers/charm/ro/tests/test_charm.py +++ /dev/null @@ -1,505 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -import base64 -from typing import NoReturn -import unittest - -from charm import RoCharm -from ops.model import ActiveStatus, BlockedStatus -from ops.testing import Harness - - -def encode(content: str): - return base64.b64encode(content.encode("ascii")).decode("utf-8") - - -certificate_pem = encode( - """ ------BEGIN CERTIFICATE----- -MIIDazCCAlOgAwIBAgIUf1b0s3UKtrxHXH2rge7UaQyfJAMwDQYJKoZIhvcNAQEL -BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAzMjIxNzEyMjdaFw0zMTAz -MjAxNzEyMjdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw -HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQCgCfCBgYAN6ON0yHDXuW407rFtJVRf0u46Jrp0Dk7J -kkSZ1e7Kq14r7yFHazEBWv78oOdwBocvWrd8leLuf3bYGcHR65hRy6A/fbYm5Aje -cKpwlFwaqfR4BLelwJl79jZ2rJX738cCBVrIk1nAVdOxGrXV4MTWUaKR2c+uKKvc -OKRT+5VqCeP4N5FWeATZ/KqGu8uV9E9WhFgwIZyStemLyLaDbn5PmAQ6S9oeR5jJ -o2gEEp/lDKvsqOWs76KFumSKa9hQs5Dw2lj0mb1UoyYK1gYc4ubzVChJadv44AU8 -MYtIjlFn1X1P+RjaKZNUIAGXkoLwYn6SizF6y6LiuFS9AgMBAAGjUzBRMB0GA1Ud -DgQWBBRl+/23CB+FXczeAZRQyYcfOdy9YDAfBgNVHSMEGDAWgBRl+/23CB+FXcze -AZRQyYcfOdy9YDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAd -dkeDym6lRN8kWFtfu3IyiLF8G8sn91qNbH3Yr4TuTBhgcjYyW6PgisSbrNgA9ysE -GoaF7ohb8GeVfCsQdK23+NpAlj/+DZ3OnGcxwXj1RUAz4yr9kanV1yuEtr1q2xJI -UaECWr8HZlwGBAKNTGx2EXT2/2aFzgULpDcxzTKD+MRpKpMUrWhf9ULvVrclvHWe -POLYhobUFuBHuo6rt5Rcq16j67zCX9EVTlAE3o2OECIWByK22sXdeOidYMpTkl4q -8FrOqjNsx5d+SBPJBv/pqtBm4bA47Vx1P8tbWOQ4bXS0UmXgwpeBOU/O/ot30+KS -JnKEy+dYyvVBKg77sRHw ------END CERTIFICATE----- -""" -) - - -class TestCharm(unittest.TestCase): - """Prometheus Charm unit tests.""" - - def setUp(self) -> NoReturn: - """Test setup""" - self.harness = Harness(RoCharm) - self.harness.set_leader(is_leader=True) - self.harness.begin() - self.config = { - "enable_ng_ro": True, - "database_commonkey": "commonkey", - "mongodb_uri": "", - "log_level": "INFO", - "vim_database": "db_name", - "ro_database": "ro_db_name", - "openmano_tenant": "mano", - "certificates": f"cert1:{certificate_pem}", - } - self.harness.update_config(self.config) - - def test_config_changed_no_relations( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue( - all( - relation in self.harness.charm.unit.status.message - for relation in ["mongodb", "kafka"] - ) - ) - - # Disable ng-ro - self.harness.update_config({"enable_ng_ro": False}) - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue( - all( - relation in self.harness.charm.unit.status.message - for relation in ["mysql"] - ) - ) - - def test_config_changed_non_leader( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - self.harness.set_leader(is_leader=False) - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus) - - def test_with_relations_and_mongodb_config_ng( - self, - ) -> NoReturn: - "Test with relations (ng-ro)" - - # Initializing the kafka relation - kafka_relation_id = self.harness.add_relation("kafka", "kafka") - self.harness.add_relation_unit(kafka_relation_id, "kafka/0") - self.harness.update_relation_data( - kafka_relation_id, "kafka", {"host": "kafka", "port": 9092} - ) - - # Initializing the mongodb config - self.harness.update_config({"mongodb_uri": "mongodb://mongo:27017"}) - - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_with_relations_ng( - self, - ) -> NoReturn: - "Test with relations (ng-ro)" - - # Initializing the kafka relation - kafka_relation_id = self.harness.add_relation("kafka", "kafka") - self.harness.add_relation_unit(kafka_relation_id, "kafka/0") - self.harness.update_relation_data( - kafka_relation_id, "kafka", {"host": "kafka", "port": 9092} - ) - - # Initializing the mongo relation - mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") - self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") - self.harness.update_relation_data( - mongodb_relation_id, - "mongodb/0", - {"connection_string": "mongodb://mongo:27017"}, - ) - - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_ng_exception_mongodb_relation_and_config( - self, - ) -> NoReturn: - "Test NG-RO mongodb relation and config. Must fail" - # Initializing the mongo relation - mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") - self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") - self.harness.update_relation_data( - mongodb_relation_id, - "mongodb/0", - {"connection_string": "mongodb://mongo:27017"}, - ) - - # Initializing the mongodb config - self.harness.update_config({"mongodb_uri": "mongodb://mongo:27017"}) - - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - - -if __name__ == "__main__": - unittest.main() - -# class TestCharm(unittest.TestCase): -# """RO Charm unit tests.""" - -# def setUp(self) -> NoReturn: -# """Test setup""" -# self.harness = Harness(RoCharm) -# self.harness.set_leader(is_leader=True) -# self.harness.begin() - -# def test_on_start_without_relations_ng_ro(self) -> NoReturn: -# """Test installation without any relation.""" -# self.harness.charm.on.start.emit() - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - -# def test_on_start_without_relations_no_ng_ro(self) -> NoReturn: -# """Test installation without any relation.""" -# self.harness.update_config({"enable_ng_ro": False}) - -# self.harness.charm.on.start.emit() - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("mysql", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relation")) - -# def test_on_start_with_relations_ng_ro(self) -> NoReturn: -# """Test deployment with NG-RO.""" -# expected_result = { -# "version": 3, -# "containers": [ -# { -# "name": "ro", -# "imageDetails": self.harness.charm.image.fetch(), -# "imagePullPolicy": "Always", -# "ports": [ -# { -# "name": "ro", -# "containerPort": 9090, -# "protocol": "TCP", -# } -# ], -# "envConfig": { -# "OSMRO_LOG_LEVEL": "INFO", -# "OSMRO_MESSAGE_DRIVER": "kafka", -# "OSMRO_MESSAGE_HOST": "kafka", -# "OSMRO_MESSAGE_PORT": "9090", -# "OSMRO_DATABASE_DRIVER": "mongo", -# "OSMRO_DATABASE_URI": "mongodb://mongo", -# "OSMRO_DATABASE_COMMONKEY": "osm", -# }, -# "kubernetes": { -# "startupProbe": { -# "exec": {"command": ["/usr/bin/pgrep", "python3"]}, -# "initialDelaySeconds": 60, -# "timeoutSeconds": 5, -# }, -# "readinessProbe": { -# "httpGet": { -# "path": "/openmano/tenants", -# "port": 9090, -# }, -# "periodSeconds": 10, -# "timeoutSeconds": 5, -# "successThreshold": 1, -# "failureThreshold": 3, -# }, -# "livenessProbe": { -# "httpGet": { -# "path": "/openmano/tenants", -# "port": 9090, -# }, -# "initialDelaySeconds": 600, -# "periodSeconds": 10, -# "timeoutSeconds": 5, -# "successThreshold": 1, -# "failureThreshold": 3, -# }, -# }, -# } -# ], -# "kubernetesResources": {"ingressResources": []}, -# } - -# self.harness.charm.on.start.emit() - -# # Initializing the kafka relation -# relation_id = self.harness.add_relation("kafka", "kafka") -# self.harness.add_relation_unit(relation_id, "kafka/0") -# self.harness.update_relation_data( -# relation_id, -# "kafka/0", -# { -# "host": "kafka", -# "port": "9090", -# }, -# ) - -# # Initializing the mongodb relation -# relation_id = self.harness.add_relation("mongodb", "mongodb") -# self.harness.add_relation_unit(relation_id, "mongodb/0") -# self.harness.update_relation_data( -# relation_id, -# "mongodb/0", -# { -# "connection_string": "mongodb://mongo", -# }, -# ) - -# # Verifying status -# self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# pod_spec, _ = self.harness.get_pod_spec() - -# self.assertDictEqual(expected_result, pod_spec) - -# def test_on_start_with_relations_no_ng_ro(self) -> NoReturn: -# """Test deployment with old RO.""" -# self.harness.update_config({"enable_ng_ro": False}) - -# expected_result = { -# "version": 3, -# "containers": [ -# { -# "name": "ro", -# "imageDetails": self.harness.charm.image.fetch(), -# "imagePullPolicy": "Always", -# "ports": [ -# { -# "name": "ro", -# "containerPort": 9090, -# "protocol": "TCP", -# } -# ], -# "envConfig": { -# "OSMRO_LOG_LEVEL": "INFO", -# "RO_DB_HOST": "mysql", -# "RO_DB_OVIM_HOST": "mysql", -# "RO_DB_PORT": 3306, -# "RO_DB_OVIM_PORT": 3306, -# "RO_DB_USER": "mano", -# "RO_DB_OVIM_USER": "mano", -# "RO_DB_PASSWORD": "manopw", -# "RO_DB_OVIM_PASSWORD": "manopw", -# "RO_DB_ROOT_PASSWORD": "rootmanopw", -# "RO_DB_OVIM_ROOT_PASSWORD": "rootmanopw", -# "RO_DB_NAME": "mano_db", -# "RO_DB_OVIM_NAME": "mano_vim_db", -# "OPENMANO_TENANT": "osm", -# }, -# "kubernetes": { -# "startupProbe": { -# "exec": {"command": ["/usr/bin/pgrep", "python3"]}, -# "initialDelaySeconds": 60, -# "timeoutSeconds": 5, -# }, -# "readinessProbe": { -# "httpGet": { -# "path": "/openmano/tenants", -# "port": 9090, -# }, -# "periodSeconds": 10, -# "timeoutSeconds": 5, -# "successThreshold": 1, -# "failureThreshold": 3, -# }, -# "livenessProbe": { -# "httpGet": { -# "path": "/openmano/tenants", -# "port": 9090, -# }, -# "initialDelaySeconds": 600, -# "periodSeconds": 10, -# "timeoutSeconds": 5, -# "successThreshold": 1, -# "failureThreshold": 3, -# }, -# }, -# } -# ], -# "kubernetesResources": {"ingressResources": []}, -# } - -# self.harness.charm.on.start.emit() - -# # Initializing the mysql relation -# relation_id = self.harness.add_relation("mysql", "mysql") -# self.harness.add_relation_unit(relation_id, "mysql/0") -# self.harness.update_relation_data( -# relation_id, -# "mysql/0", -# { -# "host": "mysql", -# "port": 3306, -# "user": "mano", -# "password": "manopw", -# "root_password": "rootmanopw", -# }, -# ) - -# # Verifying status -# self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# pod_spec, _ = self.harness.get_pod_spec() - -# self.assertDictEqual(expected_result, pod_spec) - -# def test_on_kafka_unit_relation_changed(self) -> NoReturn: -# """Test to see if kafka relation is updated.""" -# self.harness.charm.on.start.emit() - -# relation_id = self.harness.add_relation("kafka", "kafka") -# self.harness.add_relation_unit(relation_id, "kafka/0") -# self.harness.update_relation_data( -# relation_id, -# "kafka/0", -# { -# "host": "kafka", -# "port": 9090, -# }, -# ) - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relation")) - -# def test_on_mongodb_unit_relation_changed(self) -> NoReturn: -# """Test to see if mongodb relation is updated.""" -# self.harness.charm.on.start.emit() - -# relation_id = self.harness.add_relation("mongodb", "mongodb") -# self.harness.add_relation_unit(relation_id, "mongodb/0") -# self.harness.update_relation_data( -# relation_id, -# "mongodb/0", -# { -# "connection_string": "mongodb://mongo", -# }, -# ) - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relation")) - -# def test_on_mysql_unit_relation_changed(self) -> NoReturn: -# """Test to see if mysql relation is updated.""" -# self.harness.charm.on.start.emit() - -# relation_id = self.harness.add_relation("mysql", "mysql") -# self.harness.add_relation_unit(relation_id, "mysql/0") -# self.harness.update_relation_data( -# relation_id, -# "mysql/0", -# { -# "host": "mysql", -# "port": 3306, -# "user": "mano", -# "password": "manopw", -# "root_password": "rootmanopw", -# }, -# ) - -# # Verifying status -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - -# # Verifying status message -# self.assertGreater(len(self.harness.charm.unit.status.message), 0) -# self.assertTrue( -# self.harness.charm.unit.status.message.startswith("Waiting for ") -# ) -# self.assertIn("kafka", self.harness.charm.unit.status.message) -# self.assertIn("mongodb", self.harness.charm.unit.status.message) -# self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations")) - -# def test_publish_ro_info(self) -> NoReturn: -# """Test to see if ro relation is updated.""" -# expected_result = { -# "host": "ro", -# "port": "9090", -# } - -# self.harness.charm.on.start.emit() - -# relation_id = self.harness.add_relation("ro", "lcm") -# self.harness.add_relation_unit(relation_id, "lcm/0") -# relation_data = self.harness.get_relation_data(relation_id, "ro") - -# self.assertDictEqual(expected_result, relation_data) - - -if __name__ == "__main__": - unittest.main() diff --git a/installers/charm/ro/tests/test_pod_spec.py b/installers/charm/ro/tests/test_pod_spec.py deleted file mode 100644 index e6162420..00000000 --- a/installers/charm/ro/tests/test_pod_spec.py +++ /dev/null @@ -1,389 +0,0 @@ -#!/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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## - -from typing import NoReturn -import unittest - -import pod_spec - - -class TestPodSpec(unittest.TestCase): - """Pod spec unit tests.""" - - def test_make_pod_ports(self) -> NoReturn: - """Testing make pod ports.""" - port = 9090 - - expected_result = [ - { - "name": "ro", - "containerPort": port, - "protocol": "TCP", - } - ] - - pod_ports = pod_spec._make_pod_ports(port) - - self.assertListEqual(expected_result, pod_ports) - - def test_make_pod_envconfig_ng_ro(self) -> NoReturn: - """Teting make pod envconfig.""" - config = { - "enable_ng_ro": True, - "database_commonkey": "osm", - "log_level": "INFO", - } - relation_state = { - "kafka_host": "kafka", - "kafka_port": "9090", - "mongodb_connection_string": "mongodb://mongo", - } - - expected_result = { - "OSMRO_LOG_LEVEL": config["log_level"], - "OSMRO_MESSAGE_DRIVER": "kafka", - "OSMRO_MESSAGE_HOST": relation_state["kafka_host"], - "OSMRO_MESSAGE_PORT": relation_state["kafka_port"], - "OSMRO_DATABASE_DRIVER": "mongo", - "OSMRO_DATABASE_URI": relation_state["mongodb_connection_string"], - "OSMRO_DATABASE_COMMONKEY": config["database_commonkey"], - } - - pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state) - - self.assertDictEqual(expected_result, pod_envconfig) - - def test_make_pod_envconfig_no_ng_ro(self) -> NoReturn: - """Teting make pod envconfig.""" - config = { - "log_level": "INFO", - "enable_ng_ro": False, - "vim_database": "mano_vim_db", - "ro_database": "mano_db", - "openmano_tenant": "osm", - } - relation_state = { - "mysql_host": "mysql", - "mysql_port": 3306, - "mysql_user": "mano", - "mysql_password": "manopw", - "mysql_root_password": "rootmanopw", - } - - expected_result = { - "OSMRO_LOG_LEVEL": config["log_level"], - "RO_DB_HOST": relation_state["mysql_host"], - "RO_DB_OVIM_HOST": relation_state["mysql_host"], - "RO_DB_PORT": relation_state["mysql_port"], - "RO_DB_OVIM_PORT": relation_state["mysql_port"], - "RO_DB_USER": relation_state["mysql_user"], - "RO_DB_OVIM_USER": relation_state["mysql_user"], - "RO_DB_PASSWORD": relation_state["mysql_password"], - "RO_DB_OVIM_PASSWORD": relation_state["mysql_password"], - "RO_DB_ROOT_PASSWORD": relation_state["mysql_root_password"], - "RO_DB_OVIM_ROOT_PASSWORD": relation_state["mysql_root_password"], - "RO_DB_NAME": config["ro_database"], - "RO_DB_OVIM_NAME": config["vim_database"], - "OPENMANO_TENANT": config["openmano_tenant"], - } - - pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state) - - self.assertDictEqual(expected_result, pod_envconfig) - - def test_make_startup_probe(self) -> NoReturn: - """Testing make startup probe.""" - expected_result = { - "exec": {"command": ["/usr/bin/pgrep", "python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - } - - startup_probe = pod_spec._make_startup_probe() - - self.assertDictEqual(expected_result, startup_probe) - - def test_make_readiness_probe(self) -> NoReturn: - """Testing make readiness probe.""" - port = 9090 - - expected_result = { - "httpGet": { - "path": "/openmano/tenants", - "port": port, - }, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - readiness_probe = pod_spec._make_readiness_probe(port) - - self.assertDictEqual(expected_result, readiness_probe) - - def test_make_liveness_probe(self) -> NoReturn: - """Testing make liveness probe.""" - port = 9090 - - expected_result = { - "httpGet": { - "path": "/openmano/tenants", - "port": port, - }, - "initialDelaySeconds": 600, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - } - - liveness_probe = pod_spec._make_liveness_probe(port) - - self.assertDictEqual(expected_result, liveness_probe) - - def test_make_pod_spec_ng_ro(self) -> NoReturn: - """Testing make pod spec.""" - image_info = {"upstream-source": "opensourcemano/ro:8"} - config = { - "database_commonkey": "osm", - "log_level": "INFO", - "enable_ng_ro": True, - } - relation_state = { - "kafka_host": "kafka", - "kafka_port": "9090", - "mongodb_connection_string": "mongodb://mongo", - } - app_name = "ro" - port = 9090 - - expected_result = { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": [ - { - "name": app_name, - "containerPort": port, - "protocol": "TCP", - } - ], - "envConfig": { - "OSMRO_LOG_LEVEL": config["log_level"], - "OSMRO_MESSAGE_DRIVER": "kafka", - "OSMRO_MESSAGE_HOST": relation_state["kafka_host"], - "OSMRO_MESSAGE_PORT": relation_state["kafka_port"], - "OSMRO_DATABASE_DRIVER": "mongo", - "OSMRO_DATABASE_URI": relation_state[ - "mongodb_connection_string" - ], - "OSMRO_DATABASE_COMMONKEY": config["database_commonkey"], - }, - "kubernetes": { - "startupProbe": { - "exec": {"command": ["/usr/bin/pgrep", "python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - }, - "readinessProbe": { - "httpGet": { - "path": "/openmano/tenants", - "port": port, - }, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - }, - "livenessProbe": { - "httpGet": { - "path": "/openmano/tenants", - "port": port, - }, - "initialDelaySeconds": 600, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - }, - }, - } - ], - "kubernetesResources": {"ingressResources": []}, - } - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertDictEqual(expected_result, spec) - - def test_make_pod_spec_no_ng_ro(self) -> NoReturn: - """Testing make pod spec.""" - image_info = {"upstream-source": "opensourcemano/ro:8"} - config = { - "log_level": "INFO", - "enable_ng_ro": False, - "vim_database": "mano_vim_db", - "ro_database": "mano_db", - "openmano_tenant": "osm", - } - relation_state = { - "mysql_host": "mysql", - "mysql_port": 3306, - "mysql_user": "mano", - "mysql_password": "manopw", - "mysql_root_password": "rootmanopw", - } - app_name = "ro" - port = 9090 - - expected_result = { - "version": 3, - "containers": [ - { - "name": app_name, - "imageDetails": image_info, - "imagePullPolicy": "Always", - "ports": [ - { - "name": app_name, - "containerPort": port, - "protocol": "TCP", - } - ], - "envConfig": { - "OSMRO_LOG_LEVEL": config["log_level"], - "RO_DB_HOST": relation_state["mysql_host"], - "RO_DB_OVIM_HOST": relation_state["mysql_host"], - "RO_DB_PORT": relation_state["mysql_port"], - "RO_DB_OVIM_PORT": relation_state["mysql_port"], - "RO_DB_USER": relation_state["mysql_user"], - "RO_DB_OVIM_USER": relation_state["mysql_user"], - "RO_DB_PASSWORD": relation_state["mysql_password"], - "RO_DB_OVIM_PASSWORD": relation_state["mysql_password"], - "RO_DB_ROOT_PASSWORD": relation_state["mysql_root_password"], - "RO_DB_OVIM_ROOT_PASSWORD": relation_state[ - "mysql_root_password" - ], - "RO_DB_NAME": config["ro_database"], - "RO_DB_OVIM_NAME": config["vim_database"], - "OPENMANO_TENANT": config["openmano_tenant"], - }, - "kubernetes": { - "startupProbe": { - "exec": {"command": ["/usr/bin/pgrep", "python3"]}, - "initialDelaySeconds": 60, - "timeoutSeconds": 5, - }, - "readinessProbe": { - "httpGet": { - "path": "/openmano/tenants", - "port": port, - }, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - }, - "livenessProbe": { - "httpGet": { - "path": "/openmano/tenants", - "port": port, - }, - "initialDelaySeconds": 600, - "periodSeconds": 10, - "timeoutSeconds": 5, - "successThreshold": 1, - "failureThreshold": 3, - }, - }, - } - ], - "kubernetesResources": {"ingressResources": []}, - } - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertDictEqual(expected_result, spec) - - def test_make_pod_spec_without_image_info(self) -> NoReturn: - """Testing make pod spec without image_info.""" - image_info = None - config = { - "enable_ng_ro": True, - "database_commonkey": "osm", - "log_level": "INFO", - } - relation_state = { - "kafka_host": "kafka", - "kafka_port": 9090, - "mongodb_connection_string": "mongodb://mongo", - } - app_name = "ro" - port = 9090 - - spec = pod_spec.make_pod_spec( - image_info, config, relation_state, app_name, port - ) - - self.assertIsNone(spec) - - def test_make_pod_spec_without_config(self) -> NoReturn: - """Testing make pod spec without config.""" - image_info = {"upstream-source": "opensourcemano/ro:8"} - config = {} - relation_state = { - "kafka_host": "kafka", - "kafka_port": 9090, - "mongodb_connection_string": "mongodb://mongo", - } - app_name = "ro" - port = 9090 - - with self.assertRaises(ValueError): - pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port) - - def test_make_pod_spec_without_relation_state(self) -> NoReturn: - """Testing make pod spec without relation_state.""" - image_info = {"upstream-source": "opensourcemano/ro:8"} - config = { - "enable_ng_ro": True, - "database_commonkey": "osm", - "log_level": "INFO", - } - relation_state = {} - app_name = "ro" - port = 9090 - - with self.assertRaises(ValueError): - pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port) - - -if __name__ == "__main__": - unittest.main() diff --git a/installers/charm/ro/tox.ini b/installers/charm/ro/tox.ini deleted file mode 100644 index f3c91440..00000000 --- a/installers/charm/ro/tox.ini +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright 2021 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. -# -# For those usages not covered by the Apache License, Version 2.0 please -# contact: legal@canonical.com -# -# To get in touch with the maintainers, please contact: -# osm-charmers@lists.launchpad.net -## -####################################################################################### - -[tox] -envlist = black, cover, flake8, pylint, yamllint, safety -skipsdist = true - -[tox:jenkins] -toxworkdir = /tmp/.tox - -[testenv] -basepython = python3.8 -setenv = - VIRTUAL_ENV={envdir} - PYTHONPATH = {toxinidir}:{toxinidir}/lib:{toxinidir}/src - PYTHONDONTWRITEBYTECODE = 1 -deps = -r{toxinidir}/requirements.txt - - -####################################################################################### -[testenv:black] -deps = black -commands = - black --check --diff src/ tests/ - - -####################################################################################### -[testenv:cover] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - coverage - nose2 -commands = - sh -c 'rm -f nosetests.xml' - coverage erase - nose2 -C --coverage src - coverage report --omit='*tests*' - coverage html -d ./cover --omit='*tests*' - coverage xml -o coverage.xml --omit=*tests* -whitelist_externals = sh - - -####################################################################################### -[testenv:flake8] -deps = flake8 - flake8-import-order -commands = - flake8 src/ tests/ - - -####################################################################################### -[testenv:pylint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - pylint==2.10.2 -commands = - pylint -E src/ tests/ - - -####################################################################################### -[testenv:safety] -setenv = - LC_ALL=C.UTF-8 - LANG=C.UTF-8 -deps = {[testenv]deps} - safety -commands = - - safety check --full-report - - -####################################################################################### -[testenv:yamllint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - yamllint -commands = yamllint . - -####################################################################################### -[testenv:build] -passenv=HTTP_PROXY HTTPS_PROXY NO_PROXY -whitelist_externals = - charmcraft - sh -commands = - charmcraft pack - sh -c 'ubuntu_version=20.04; \ - architectures="amd64-aarch64-arm64"; \ - charm_name=`cat metadata.yaml | grep -E "^name: " | cut -f 2 -d " "`; \ - mv $charm_name"_ubuntu-"$ubuntu_version-$architectures.charm $charm_name.charm' - -####################################################################################### -[flake8] -ignore = - W291, - W293, - W503, - E123, - E125, - E226, - E241, -exclude = - .git, - __pycache__, - .tox, -max-line-length = 120 -show-source = True -builtins = _ -max-complexity = 10 -import-order-style = google diff --git a/installers/charm/update-bundle-revisions.sh b/installers/charm/update-bundle-revisions.sh deleted file mode 100755 index 1a8d8cb2..00000000 --- a/installers/charm/update-bundle-revisions.sh +++ /dev/null @@ -1,35 +0,0 @@ -## -# Copyright 2019 ETSI -# -# 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. -## - -charms=`cat bundles/osm/bundle.yaml | grep cs | grep -v k8s | awk '{print $2}' | tr -d \"` -for charm_uri in $charms; do - charm_without_rev=`echo $charm_uri| rev | cut -d "-" -f 2-5 | rev` - latest_revision=`charm show --channel edge $charm_without_rev | grep Revision | awk '{print $2}'` - new_charm_uri=$charm_without_rev-$latest_revision - old_uri=`echo $charm_uri | sed 's/\//\\\\\//g'` - new_uri=`echo $new_charm_uri | sed 's/\//\\\\\//g'` - sed -i "s/"$old_uri"/"$new_uri"/g" bundles/osm/bundle.yaml -done - -charms=`cat bundles/osm-ha/bundle.yaml | grep cs | grep -v k8s | awk '{print $2}' | tr -d \"` -for charm_uri in $charms; do - charm_without_rev=`echo $charm_uri| rev | cut -d "-" -f 2-5 | rev` - latest_revision=`charm show --channel edge $charm_without_rev | grep Revision | awk '{print $2}'` - new_charm_uri=$charm_without_rev-$latest_revision - old_uri=`echo $charm_uri | sed 's/\//\\\\\//g'` - new_uri=`echo $new_charm_uri | sed 's/\//\\\\\//g'` - sed -i "s/"$old_uri"/"$new_uri"/g" bundles/osm-ha/bundle.yaml -done \ No newline at end of file