diff --git a/charms/masakari-k8s/.sunbeam-build.yaml b/charms/masakari-k8s/.sunbeam-build.yaml new file mode 100644 index 0000000..05266d5 --- /dev/null +++ b/charms/masakari-k8s/.sunbeam-build.yaml @@ -0,0 +1,17 @@ +external-libraries: + - charms.data_platform_libs.v0.data_interfaces + - charms.rabbitmq_k8s.v0.rabbitmq + - charms.traefik_k8s.v2.ingress + - charms.certificate_transfer_interface.v0.certificate_transfer + - charms.loki_k8s.v1.loki_push_api + - charms.tempo_k8s.v2.tracing + - charms.tempo_k8s.v1.charm_tracing +internal-libraries: + - charms.keystone_k8s.v1.identity_service +templates: + - parts/database-connection + - parts/database-connection-settings + - parts/section-identity + - parts/section-database + - parts/identity-data + - ca-bundle.pem.j2 \ No newline at end of file diff --git a/charms/masakari-k8s/CONTRIBUTING.md b/charms/masakari-k8s/CONTRIBUTING.md new file mode 100644 index 0000000..55d8ae5 --- /dev/null +++ b/charms/masakari-k8s/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing + +To make contributions to this charm, you'll need a working [development setup](https://juju.is/docs/sdk/dev-setup). + +You can create an environment for development with `tox`: + +```shell +tox devenv -e integration +source venv/bin/activate +``` + +## Testing + +This project uses `tox` for managing test environments. There are some pre-configured environments +that can be used for linting and formatting code when you're preparing contributions to the charm: + +```shell +tox run -e fmt # update your code according to linting rules +tox run -e pep8 # code style +tox run -e linters # static type checking +tox run -e py3 # unit tests +tox # runs 'format', 'lint', 'static', and 'unit' environments +``` + +## Build the charm + +Build the charm in this git repository using: + +```shell +tox run -e build masakari-k8s +``` \ No newline at end of file diff --git a/charms/masakari-k8s/LICENSE b/charms/masakari-k8s/LICENSE new file mode 100644 index 0000000..f37e372 --- /dev/null +++ b/charms/masakari-k8s/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 liam + + 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/charms/masakari-k8s/README.md b/charms/masakari-k8s/README.md new file mode 100644 index 0000000..45dc4eb --- /dev/null +++ b/charms/masakari-k8s/README.md @@ -0,0 +1,67 @@ +# masakari-k8s + +## Description + +masakari-k8s is an operator to manage the Masakari services masakari-api and +masakari-engine in a Kubernetes-based environment. + +## Usage + +### Deployment + +masakari k8s is deployed using below command: + + juju deploy masakari-k8s masakari --trust + +Now connect the masakari operator to existing database, keystone identity, +and rabbitmq operators: + + juju relate masakari-k8s:ingress-public traefik-k8s:ingress + juju relate masakari-k8s:identity-service keystone-k8s:identity-service + juju relate masakari-k8s:database mysql-k8s:database + juju relate masakari-k8s:amqp rabbitmq-k8s:amqp + +### Configuration + +This section covers common and/or important configuration options. See file +`config.yaml` for the full list of options, along with their descriptions and +default values. See the [Juju documentation][juju-docs-config-apps] for details +on configuring applications. + +### Actions + +This section covers Juju [actions][juju-docs-actions] supported by the charm. +Actions allow specific operations to be performed on a per-unit basis. To +display action descriptions run `juju actions masakari`. + +## Relations + +masakari-k8s requires the following relations: + +`database`: To connect to MySQL +`identity-service`: To register endpoints in Keystone +`ingress-public`: To expose service on public network +`amqp`: To connect to Rabbitmq + +## OCI Images + +The charm by default uses following images: + + ghcr.io/canonical/masakari-consolidated:2024.1 + +## Contributing + +Please see the [Juju SDK docs](https://juju.is/docs/sdk) for guidelines +on enhancements to this charm following best practice guidelines, and +[CONTRIBUTING.md](contributors-guide) for developer guidance. + +## Bugs + +Please report bugs on [Launchpad][lp-bugs-charm-masakari-k8s]. + + + +[contributors-guide]: https://opendev.org/openstack/charm-masakari-k8s/src/branch/main/CONTRIBUTING.md +[juju-docs-actions]: https://jaas.ai/docs/actions +[juju-docs-config-apps]: https://juju.is/docs/configuring-applications +[lp-bugs-charm-masakari-k8s]: https://bugs.launchpad.net/sunbeam-charms/+filebug \ No newline at end of file diff --git a/charms/masakari-k8s/charmcraft.yaml b/charms/masakari-k8s/charmcraft.yaml new file mode 100644 index 0000000..bc06ba0 --- /dev/null +++ b/charms/masakari-k8s/charmcraft.yaml @@ -0,0 +1,124 @@ +type: "charm" +bases: + - build-on: + - name: "ubuntu" + channel: "22.04" + run-on: + - name: "ubuntu" + channel: "22.04" +parts: + update-certificates: + plugin: nil + override-build: | + apt update + apt install -y ca-certificates + update-ca-certificates + charm: + after: [update-certificates] + build-packages: + - git + - libffi-dev + - libssl-dev + - rustc + - cargo + - pkg-config + charm-binary-python-packages: + - cryptography + - jsonschema + - pydantic + - jinja2 +name: masakari-k8s +title: OpenStack masakari service +summary: Masakari - Instances High Availability Service +description: | + Masakari provides Instances High Availability Service for OpenStack clouds by + automatically recovering failed Instances. Currently, Masakari can recover + KVM-based Virtual Machine(VM)s from failure events such as VM process down, + provisioning process down, and nova-compute host failure. Masakari also + provides an API service to manage and control the automated rescue mechanism. +links: + source: https://opendev.org/openstack/sunbeam-charms + issues: https://bugs.launchpad.net/sunbeam-charms +assumes: + - k8s-api + - juju >= 3.5 +containers: + masakari-api: + resource: masakari-image + masakari-engine: + resource: masakari-image + # Note(mylesjp): disabled until implemented + # host-monitor: + # resource: masakari-image +resources: + masakari-image: + description: OCI image for OpenStack Masakari services + type: oci-image + upstream-source: ghcr.io/canonical/masakari-consolidated:2024.1 +requires: + amqp: + interface: rabbitmq + limit: 1 + database: + interface: mysql_client + limit: 1 + identity-service: + interface: keystone + limit: 1 + ingress-internal: + interface: ingress + limit: 1 + optional: true + ingress-public: + interface: ingress + limit: 1 + logging: + interface: loki_push_api + limit: 1 + optional: true + receive-ca-cert: + interface: certificate_transfer + limit: 1 + optional: true + tracing: + interface: tracing + limit: 1 + optional: true + # Note(mylesjp): consul disabled until charm is published + # consul-management: + # interface: consul-client + # limit: 1 + # consul-tenant: # Name TBD + # interface: consul-client + # limit: 1 + # optional: true + # consul-storage: + # interface: consul-client + # limit: 1 + # optional: true +peers: + peers: + interface: masakari-peer +config: + options: + debug: + default: False + description: Enable debug logging. + type: boolean + evacuation-delay: + type: int + default: 60 + description: | + Number of seconds to wait before evacuation after a service is + enabled or disabled. + evacuate-all-instances: + type: boolean + default: True + description: | + Whether to restrict instance evacuation to instances with ha enabled + in their metadata + region: + type: string + default: RegionOne + description: Name of the OpenStack region +actions: {} diff --git a/charms/masakari-k8s/rebuild b/charms/masakari-k8s/rebuild new file mode 100644 index 0000000..5a402b9 --- /dev/null +++ b/charms/masakari-k8s/rebuild @@ -0,0 +1,3 @@ +# This file is used to trigger a build. +# Change uuid to trigger a new build. +c3b9c7c9-2bd4-4df1-a1df-89c729b34eb6 \ No newline at end of file diff --git a/charms/masakari-k8s/requirements.txt b/charms/masakari-k8s/requirements.txt new file mode 100644 index 0000000..3861393 --- /dev/null +++ b/charms/masakari-k8s/requirements.txt @@ -0,0 +1,15 @@ +# This file is managed centrally by release-tools and should not be modified +# within individual charm repos. See the 'global' dir contents for available +# choices of *requirements.txt files for OpenStack Charms: +# https://github.com/openstack-charmers/release-tools +# + +cryptography +jinja2 +jsonschema +pydantic +ops +pytest-interface-tester + +# From ops_sunbeam +tenacity \ No newline at end of file diff --git a/charms/masakari-k8s/src/charm.py b/charms/masakari-k8s/src/charm.py new file mode 100755 index 0000000..3a13ac1 --- /dev/null +++ b/charms/masakari-k8s/src/charm.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# Copyright 2024 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. +"""Masakari Operator Charm. + +This charm provide Masakari services as part of an OpenStack deployment +""" + +import logging + +import ops.framework +import ops.model +import ops.pebble +import ops_sunbeam.charm as sunbeam_charm +import ops_sunbeam.container_handlers as sunbeam_chandlers +import ops_sunbeam.core as sunbeam_core +import ops_sunbeam.tracing as sunbeam_tracing +from ops.main import ( + main, +) + +logger = logging.getLogger(__name__) + +MASAKARI_API_CONTAINER = "masakari-api" +MASAKARI_ENGINE_CONTAINER = "masakari-engine" + + +def exec(container: ops.model.Container, cmd: str): + """Execute a command in a container.""" + logging.debug(f"Executing command: {cmd!r}") + try: + process = container.exec(cmd.split(), timeout=5 * 60) + out, warnings = process.wait_output() + if warnings: + for line in warnings.splitlines(): + logger.warning(f"{cmd} warn: {line.strip()}") + logging.debug(f"Output from {cmd!r}: \n{out}") + except ops.pebble.ExecError: + logger.exception(f"Command {cmd!r} failed") + + +@sunbeam_tracing.trace_type +class MasakariWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler): + """Pebble handler for Masakari API container.""" + + charm: "MasakariOperatorCharm" + + def init_service(self, context: sunbeam_core.OPSCharmContexts): + """Enable and start WSGI service.""" + container = self.charm.unit.get_container(self.container_name) + exec(container, "a2dissite masakari-api") + super().init_service(context) + + +@sunbeam_tracing.trace_type +class MasakariEnginePebbleHandler(sunbeam_chandlers.ServicePebbleHandler): + """Pebble handler for Masakari Engine container.""" + + def get_layer(self): + """Pebble layer for Masakari Engine service. + + :returns: pebble service layer config for masakari engine service + :rtype: dict + """ + return { + "summary": "masakari engine layer", + "description": "pebble configuration for masakari engine service", + "services": { + "masakari-engine": { + "override": "replace", + "summary": "masakari engine", + "command": "masakari-engine", + "user": self.charm.service_user, + "group": self.charm.service_group, + } + }, + } + + +@sunbeam_tracing.trace_sunbeam_charm +class MasakariOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): + """Charm the service.""" + + mandatory_relations = { + "database", + "amqp", + "identity-service", + "ingress-public", + } + + wsgi_admin_script = "/usr/bin/masakari-wsgi" + wsgi_public_script = "/usr/bin/masakari-wsgi" + + db_sync_cmds = [ + [ + "masakari-manage", + "db", + "sync", + ] + ] + + @property + def container_configs(self) -> list[sunbeam_core.ContainerConfigFile]: + """Container configurations for handler.""" + _cconfigs = super().container_configs + _cconfigs.extend( + [ + sunbeam_core.ContainerConfigFile( + path="/usr/local/share/ca-certificates/ca-bundle.pem", + user="root", + group=self.service_group, + permissions=0o640, + ), + ] + ) + return _cconfigs + + def get_pebble_handlers(self): + """Pebble handlers for operator.""" + pebble_handlers = [] + pebble_handlers.extend( + [ + MasakariWSGIPebbleHandler( + self, + MASAKARI_API_CONTAINER, + self.service_name, + self.container_configs, + self.template_dir, + self.configure_charm, + f"wsgi-{self.service_name}", + ), + MasakariEnginePebbleHandler( + self, + MASAKARI_ENGINE_CONTAINER, + "masakari-engine", + self.container_configs, + self.template_dir, + self.configure_charm, + ), + ] + ) + return pebble_handlers + + @property + def service_name(self): + """Service name.""" + return "masakari-api" + + @property + def service_conf(self): + """Service default configuration file.""" + return "/etc/masakari/masakari.conf" + + @property + def service_user(self): + """Service user file and directory ownership.""" + return "masakari" + + @property + def service_group(self): + """Service group file and directory ownership.""" + return "masakari" + + @property + def service_endpoints(self): + """Return masakari service endpoints.""" + return [ + { + "service_name": "masakari", + "type": "instance-ha", + "description": "OpenStack Masakari API", + "internal_url": f"{self.internal_url}/v1/$(tenant_id)s", + "public_url": f"{self.public_url}/v1/$(tenant_id)s", + "admin_url": f"{self.admin_url}/v1/$(tenant_id)s", + } + ] + + @property + def default_public_ingress_port(self): + """Default port.""" + return 15868 + + +if __name__ == "__main__": + main(MasakariOperatorCharm) diff --git a/charms/masakari-k8s/src/templates/masakari.conf.j2 b/charms/masakari-k8s/src/templates/masakari.conf.j2 new file mode 100644 index 0000000..7b5d9fb --- /dev/null +++ b/charms/masakari-k8s/src/templates/masakari.conf.j2 @@ -0,0 +1,34 @@ +[DEFAULT] + +debug = {{ options.debug }} +auth_strategy = keystone + +{% if amqp.transport_url -%} +transport_url = {{ amqp.transport_url }} +{% endif -%} + +{% if receive_ca_cert and receive_ca_cert.ca_bundle -%} +nova_ca_certificates_file = /usr/local/share/ca-certificates/ca-bundle.pem +{% endif -%} + +graceful_shutdown_timeout = 5 +os_privileged_user_tenant = {{ identity_service.service_project_name }} +os_privileged_user_password = {{ identity_service.service_password }} +os_privileged_user_auth_url = {{ identity_service.admin_auth_url }} +os_privileged_user_name = {{ identity_service.admin_user_name }} +use_syslog = False +masakari_api_workers = 2 + +{% if options.evacuation_delay -%} +wait_period_after_service_update = {{ options.evacuation_delay }} +{% endif %} + +{% include "parts/section-database" %} + +{% include "parts/section-identity" %} + +[taskflow] +{% include "parts/database-connection" %} + +[host_failure] +evacuate_all_instances = {{ options.evacuate_all_instances }} diff --git a/charms/masakari-k8s/src/templates/wsgi-masakari-api.conf.j2 b/charms/masakari-k8s/src/templates/wsgi-masakari-api.conf.j2 new file mode 100644 index 0000000..ba44b53 --- /dev/null +++ b/charms/masakari-k8s/src/templates/wsgi-masakari-api.conf.j2 @@ -0,0 +1,31 @@ +Listen {{ wsgi_config.public_port }} + + + WSGIDaemonProcess {{ wsgi_config.name }} processes=4 threads=1 user={{ wsgi_config.user }} group={{ wsgi_config.group }} \ + display-name=%{GROUP} + WSGIProcessGroup {{ wsgi_config.name }} + {% if ingress_public and ingress_public.ingress_path -%} + WSGIScriptAlias {{ ingress_public.ingress_path }} {{ wsgi_config.wsgi_public_script }} + {% endif -%} + WSGIScriptAlias / {{ wsgi_config.wsgi_public_script }} + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + LimitRequestBody 114688 + + = 2.4> + ErrorLogFormat "%{cu}t %M" + + + ErrorLog /var/log/apache2/masakari_error.log + CustomLog /var/log/apache2/masakari_access.log combined + + + = 2.4> + Require all granted + + + Order allow,deny + Allow from all + + + diff --git a/charms/masakari-k8s/tests/unit/__init__.py b/charms/masakari-k8s/tests/unit/__init__.py new file mode 100644 index 0000000..1840f67 --- /dev/null +++ b/charms/masakari-k8s/tests/unit/__init__.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +# Copyright 2024 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. + +"""Unit tests for masakari operator.""" diff --git a/charms/masakari-k8s/tests/unit/test_charm.py b/charms/masakari-k8s/tests/unit/test_charm.py new file mode 100644 index 0000000..efd17ee --- /dev/null +++ b/charms/masakari-k8s/tests/unit/test_charm.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +# Copyright 2024 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. + +"""Tests for masakari-k8s charm.""" + +from pathlib import ( + Path, +) + +import charm +import ops_sunbeam.test_utils as test_utils +import yaml + +charmcraft = (Path(__file__).parents[2] / "charmcraft.yaml").read_text() +config = yaml.dump(yaml.safe_load(charmcraft)["config"]) +actions = yaml.dump(yaml.safe_load(charmcraft)["actions"]) + + +class _MasakariOperatorCharm(charm.MasakariOperatorCharm): + def __init__(self, framework): + self.seen_events = [] + super().__init__(framework) + + def _log_event(self, event): + self.seen_events.append(type(event).__name__) + + def configure_charm(self, event): + super().configure_charm(event) + self._log_event(event) + + @property + def public_ingress_address(self): + return "masakari.juju" + + +class TestMasakariOperatorCharm(test_utils.CharmTestCase): + """Class for testing masakari-k8s charm.""" + + PATCHES = [] + + def setUp(self): + """Run setup for unit tests.""" + super().setUp(charm, self.PATCHES) + self.harness = test_utils.get_harness( + _MasakariOperatorCharm, + container_calls=self.container_calls, + charm_metadata=charmcraft, + charm_config=config, + charm_actions=actions, + ) + + from charms.data_platform_libs.v0.data_interfaces import ( + DatabaseRequiresEvents, + ) + + for attr in ( + "database_database_created", + "database_endpoints_changed", + "database_read_only_endpoints_changed", + ): + try: + delattr(DatabaseRequiresEvents, attr) + except AttributeError: + pass + + self.addCleanup(self.harness.cleanup) + + def test_pebble_ready_handler(self): + """Test Pebble ready event is captured.""" + self.harness.begin() + self.assertEqual(self.harness.charm.seen_events, []) + test_utils.set_all_pebbles_ready(self.harness) + self.assertEqual( + self.harness.charm.seen_events, + ["PebbleReadyEvent", "PebbleReadyEvent"], + ) + + def test_all_relations(self): + """Test all the charm's relations for Masakari.""" + self.harness.begin_with_initial_hooks() + self.harness.set_leader() + test_utils.set_all_pebbles_ready(self.harness) + test_utils.add_api_relations(self.harness) + test_utils.add_complete_ingress_relation(self.harness) + + self.harness.charm.configure_charm(event=None) + + setup_cmds = [ + [ + "masakari-manage", + "db", + "sync", + ], + ] + + for cmd in setup_cmds: + self.assertIn(cmd, self.container_calls.execute["masakari-api"]) + + # Check for rendering of both configuration files in masakari-api + for f in [ + "/etc/apache2/sites-available/wsgi-masakari-api.conf", + "/etc/masakari/masakari.conf", + ]: + self.check_file("masakari-api", f) + + # Check for rendering of single configuration file in masakari-engine + self.check_file("masakari-engine", "/etc/masakari/masakari.conf") diff --git a/tests/misc/smoke.yaml.j2 b/tests/misc/smoke.yaml.j2 index cad07d1..9e3cc9d 100644 --- a/tests/misc/smoke.yaml.j2 +++ b/tests/misc/smoke.yaml.j2 @@ -102,6 +102,18 @@ applications: trust: true resources: horizon-image: ghcr.io/canonical/horizon:2024.1 + masakari: + {% if masakari_k8s is defined and masakari_k8s is sameas true -%} + charm: ../../../masakari-k8s.charm + {% else -%} + charm: ch:masakari-k8s + channel: 2024.1/edge + {% endif -%} + base: ubuntu@22.04 + scale: 1 + trust: true + resources: + masakari-image: ghcr.io/canonical/masakari-consolidated:2024.1 relations: - - mysql:database @@ -136,3 +148,12 @@ relations: - horizon:ingress-public - - keystone:send-ca-cert - horizon:receive-ca-cert + +- - mysql:database + - masakari:database +- - rabbitmq:amqp + - masakari:amqp +- - keystone:identity-service + - masakari:identity-service +- - traefik:ingress + - masakari:ingress-public \ No newline at end of file diff --git a/tests/misc/tests.yaml b/tests/misc/tests.yaml index 2bac5e4..f08c031 100644 --- a/tests/misc/tests.yaml +++ b/tests/misc/tests.yaml @@ -53,3 +53,6 @@ target_deploy_status: horizon: workload-status: active workload-status-message-regex: '^$' + masakari: + workload-status: active + workload-status-message-regex: '^$' diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml index 3a930a1..61eee14 100644 --- a/zuul.d/jobs.yaml +++ b/zuul.d/jobs.yaml @@ -323,6 +323,19 @@ vars: charm: watcher-k8s +- job: + name: charm-build-masakari-k8s + description: Build sunbeam masakari-k8s charm + run: playbooks/charm/build.yaml + timeout: 3600 + match-on-config-updates: false + files: + - ops-sunbeam/ops_sunbeam/* + - charms/masakari-k8s/* + - rebuild + vars: + charm: masakari-k8s + - job: name: func-test-core description: | @@ -485,7 +498,6 @@ soft: true - name: charm-build-ovn-central-k8s soft: true - files: - ops-sunbeam/ops_sunbeam/* - charms/heat-k8s/* @@ -525,6 +537,8 @@ soft: true - name: charm-build-horizon-k8s soft: true + - name: charm-build-masakari-k8s + soft: true files: - ops-sunbeam/ops_sunbeam/* @@ -533,6 +547,7 @@ - charms/keystone-ldap-k8s/* - charms/openstack-exporter-k8s/* - charms/horizon-k8s/* + - charms/masakari-k8s/* - rebuild vars: charm_jobs: @@ -542,6 +557,7 @@ - charm-build-openstack-exporter-k8s - charm-build-keystone-k8s - charm-build-horizon-k8s + - charm-build-masakari-k8s test_dir: tests/misc - job: name: func-test-machine @@ -918,3 +934,16 @@ secrets: - charmhub_token timeout: 3600 + +- job: + name: publish-charm-masakari-k8s + description: | + Publish masakari-k8s built in gate pipeline. + run: playbooks/charm/publish.yaml + files: + - ops-sunbeam/ops_sunbeam/* + - charms/masakari-k8s/* + - rebuild + secrets: + - charmhub_token + timeout: 3600 \ No newline at end of file diff --git a/zuul.d/project-templates.yaml b/zuul.d/project-templates.yaml index 9074130..55ba078 100644 --- a/zuul.d/project-templates.yaml +++ b/zuul.d/project-templates.yaml @@ -72,6 +72,8 @@ nodeset: ubuntu-jammy - charm-build-magnum-k8s: nodeset: ubuntu-jammy + - charm-build-masakari-k8s: + nodeset: ubuntu-jammy - charm-build-designate-k8s: nodeset: ubuntu-jammy - charm-build-designate-bind-k8s: @@ -129,6 +131,8 @@ nodeset: ubuntu-jammy - charm-build-magnum-k8s: nodeset: ubuntu-jammy + - charm-build-masakari-k8s: + nodeset: ubuntu-jammy - charm-build-designate-k8s: nodeset: ubuntu-jammy - charm-build-designate-bind-k8s: @@ -190,6 +194,8 @@ nodeset: ubuntu-jammy - publish-charm-magnum-k8s: nodeset: ubuntu-jammy + - publish-charm-masakari-k8s: + nodeset: ubuntu-jammy - publish-charm-designate-k8s: nodeset: ubuntu-jammy - publish-charm-designate-bind-k8s: diff --git a/zuul.d/secrets.yaml b/zuul.d/secrets.yaml index c1f7b12..4587875 100644 --- a/zuul.d/secrets.yaml +++ b/zuul.d/secrets.yaml @@ -1,75 +1,75 @@ - secret: name: charmhub_token data: - # Generated on 2024-09-05T09:40:42+00:00 with 90 days ttl + # Generated on 2024-10-07T15:03:09+00:00 with 90 days ttl value: !encrypted/pkcs1-oaep - - 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 diff --git a/zuul.d/zuul.yaml b/zuul.d/zuul.yaml index 467494b..6592743 100644 --- a/zuul.d/zuul.yaml +++ b/zuul.d/zuul.yaml @@ -45,6 +45,7 @@ designate-k8s: 2024.1/edge designate-bind-k8s: 9/edge magnum-k8s: 2024.1/edge + masakari-k8s: 2024.1/edge keystone-ldap-k8s: 2024.1/edge openstack-exporter-k8s: 2024.1/edge openstack-hypervisor: 2024.1/edge