From df41decd82ea911f1e46655c8d342f76b6972ead Mon Sep 17 00:00:00 2001 From: Hemanth Nakkina <hemanth.nakkina@canonical.com> Date: Fri, 4 Aug 2023 03:39:54 +0530 Subject: [PATCH] Initial charm Initial charm generated by running - git init - charmcraft init - cookie cutter code from charm-ops-sunbeam --- charms/gnocchi-k8s/.gitignore | 9 + charms/gnocchi-k8s/CONTRIBUTING.md | 34 +++ charms/gnocchi-k8s/LICENSE | 202 ++++++++++++++++++ charms/gnocchi-k8s/README.md | 26 +++ charms/gnocchi-k8s/actions.yaml | 2 + charms/gnocchi-k8s/charmcraft.yaml | 19 ++ charms/gnocchi-k8s/config.yaml | 27 +++ charms/gnocchi-k8s/metadata.yaml | 47 ++++ charms/gnocchi-k8s/pyproject.toml | 46 ++++ charms/gnocchi-k8s/requirements.txt | 8 + charms/gnocchi-k8s/src/charm.py | 61 ++++++ charms/gnocchi-k8s/src/templates/ceph.conf.j2 | 22 ++ .../src/templates/parts/database-connection | 3 + .../src/templates/parts/identity-data | 10 + .../src/templates/parts/section-database | 3 + .../src/templates/parts/section-federation | 10 + .../src/templates/parts/section-identity | 2 + .../src/templates/parts/section-middleware | 6 + .../src/templates/parts/section-signing | 15 ++ .../src/templates/wsgi-gnocchi-api.conf | 27 +++ .../src/templates/wsgi-template.conf.j2 | 27 +++ charms/gnocchi-k8s/test-requirements.txt | 17 ++ .../tests/integration/test_charm.py | 35 +++ charms/gnocchi-k8s/tests/unit/test_charm.py | 68 ++++++ charms/gnocchi-k8s/tox.ini | 134 ++++++++++++ 25 files changed, 860 insertions(+) create mode 100644 charms/gnocchi-k8s/.gitignore create mode 100644 charms/gnocchi-k8s/CONTRIBUTING.md create mode 100644 charms/gnocchi-k8s/LICENSE create mode 100644 charms/gnocchi-k8s/README.md create mode 100644 charms/gnocchi-k8s/actions.yaml create mode 100644 charms/gnocchi-k8s/charmcraft.yaml create mode 100644 charms/gnocchi-k8s/config.yaml create mode 100644 charms/gnocchi-k8s/metadata.yaml create mode 100644 charms/gnocchi-k8s/pyproject.toml create mode 100644 charms/gnocchi-k8s/requirements.txt create mode 100755 charms/gnocchi-k8s/src/charm.py create mode 100644 charms/gnocchi-k8s/src/templates/ceph.conf.j2 create mode 100644 charms/gnocchi-k8s/src/templates/parts/database-connection create mode 100644 charms/gnocchi-k8s/src/templates/parts/identity-data create mode 100644 charms/gnocchi-k8s/src/templates/parts/section-database create mode 100644 charms/gnocchi-k8s/src/templates/parts/section-federation create mode 100644 charms/gnocchi-k8s/src/templates/parts/section-identity create mode 100644 charms/gnocchi-k8s/src/templates/parts/section-middleware create mode 100644 charms/gnocchi-k8s/src/templates/parts/section-signing create mode 100644 charms/gnocchi-k8s/src/templates/wsgi-gnocchi-api.conf create mode 100644 charms/gnocchi-k8s/src/templates/wsgi-template.conf.j2 create mode 100644 charms/gnocchi-k8s/test-requirements.txt create mode 100644 charms/gnocchi-k8s/tests/integration/test_charm.py create mode 100644 charms/gnocchi-k8s/tests/unit/test_charm.py create mode 100644 charms/gnocchi-k8s/tox.ini diff --git a/charms/gnocchi-k8s/.gitignore b/charms/gnocchi-k8s/.gitignore new file mode 100644 index 00000000..a26d707f --- /dev/null +++ b/charms/gnocchi-k8s/.gitignore @@ -0,0 +1,9 @@ +venv/ +build/ +*.charm +.tox/ +.coverage +__pycache__/ +*.py[cod] +.idea +.vscode/ diff --git a/charms/gnocchi-k8s/CONTRIBUTING.md b/charms/gnocchi-k8s/CONTRIBUTING.md new file mode 100644 index 00000000..20e88bcc --- /dev/null +++ b/charms/gnocchi-k8s/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# 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 format # update your code according to linting rules +tox run -e lint # code style +tox run -e static # static type checking +tox run -e unit # unit tests +tox run -e integration # integration tests +tox # runs 'format', 'lint', 'static', and 'unit' environments +``` + +## Build the charm + +Build the charm in this git repository using: + +```shell +charmcraft pack +``` + +<!-- You may want to include any contribution/style guidelines in this document> diff --git a/charms/gnocchi-k8s/LICENSE b/charms/gnocchi-k8s/LICENSE new file mode 100644 index 00000000..8d771e18 --- /dev/null +++ b/charms/gnocchi-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 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. diff --git a/charms/gnocchi-k8s/README.md b/charms/gnocchi-k8s/README.md new file mode 100644 index 00000000..104b9d5e --- /dev/null +++ b/charms/gnocchi-k8s/README.md @@ -0,0 +1,26 @@ +<!-- +Avoid using this README file for information that is maintained or published elsewhere, e.g.: + +* metadata.yaml > published on Charmhub +* documentation > published on (or linked to from) Charmhub +* detailed contribution guide > documentation or CONTRIBUTING.md + +Use links instead. +--> + +# gnocchi-k8s + +Charmhub package name: operator-template +More information: https://charmhub.io/gnocchi-k8s + +Describe your charm in one or two sentences. + +## Other resources + +<!-- If your charm is documented somewhere else other than Charmhub, provide a link separately. --> + +- [Read more](https://example.com) + +- [Contributing](CONTRIBUTING.md) <!-- or link to other contribution documentation --> + +- See the [Juju SDK documentation](https://juju.is/docs/sdk) for more information about developing and improving charms. diff --git a/charms/gnocchi-k8s/actions.yaml b/charms/gnocchi-k8s/actions.yaml new file mode 100644 index 00000000..88e6195d --- /dev/null +++ b/charms/gnocchi-k8s/actions.yaml @@ -0,0 +1,2 @@ +# NOTE: no actions yet! +{ } diff --git a/charms/gnocchi-k8s/charmcraft.yaml b/charms/gnocchi-k8s/charmcraft.yaml new file mode 100644 index 00000000..ab6239a8 --- /dev/null +++ b/charms/gnocchi-k8s/charmcraft.yaml @@ -0,0 +1,19 @@ +type: "charm" +bases: + - build-on: + - name: "ubuntu" + channel: "22.04" + run-on: + - name: "ubuntu" + channel: "22.04" +parts: + charm: + build-packages: + - git + - libffi-dev + - libssl-dev + charm-binary-python-packages: + - cryptography + - jsonschema + - jinja2 + - git+https://opendev.org/openstack/charm-ops-sunbeam#egg=ops_sunbeam diff --git a/charms/gnocchi-k8s/config.yaml b/charms/gnocchi-k8s/config.yaml new file mode 100644 index 00000000..606b5357 --- /dev/null +++ b/charms/gnocchi-k8s/config.yaml @@ -0,0 +1,27 @@ +options: + debug: + default: False + description: Enable debug logging. + type: boolean + os-admin-hostname: + default: glance.juju + description: | + The hostname or address of the admin endpoints that should be advertised + in the glance image provider. + type: string + os-internal-hostname: + default: glance.juju + description: | + The hostname or address of the internal endpoints that should be advertised + in the glance image provider. + type: string + os-public-hostname: + default: glance.juju + description: | + The hostname or address of the internal endpoints that should be advertised + in the glance image provider. + type: string + region: + default: RegionOne + description: Space delimited list of OpenStack regions + type: string diff --git a/charms/gnocchi-k8s/metadata.yaml b/charms/gnocchi-k8s/metadata.yaml new file mode 100644 index 00000000..34b5cd74 --- /dev/null +++ b/charms/gnocchi-k8s/metadata.yaml @@ -0,0 +1,47 @@ +name: gnocchi-k8s +summary: OpenStack gnocchi service +maintainer: OpenStack Charmers <openstack-charmers@lists.ubuntu.com> +description: | + OpenStack gnocchi provides an HTTP service for managing, selecting, + and claiming providers of classes of inventory representing available + resources in a cloud. + . +version: 3 +bases: + - name: ubuntu + channel: 20.04/stable +tags: +- openstack + +containers: + gnocchi-api: + resource: gnocchi-api-image + +resources: + gnocchi-api-image: + type: oci-image + description: OCI image for OpenStack gnocchi + +requires: + database: + interface: mysql_client + limit: 1 + identity-service: + interface: keystone + ingress-internal: + interface: ingress + optional: true + limit: 1 + ingress-public: + interface: ingress + limit: 1 + amqp: + interface: rabbitmq + +provides: + gnocchi: + interface: gnocchi + +peers: + peers: + interface: gnocchi-peer diff --git a/charms/gnocchi-k8s/pyproject.toml b/charms/gnocchi-k8s/pyproject.toml new file mode 100644 index 00000000..e10531c7 --- /dev/null +++ b/charms/gnocchi-k8s/pyproject.toml @@ -0,0 +1,46 @@ +# Testing tools configuration +[tool.coverage.run] +branch = true + +[tool.coverage.report] +show_missing = true + +[tool.pytest.ini_options] +minversion = "6.0" +log_cli_level = "INFO" + +# Formatting tools configuration +[tool.black] +line-length = 99 +target-version = ["py38"] + +# Linting tools configuration +[tool.ruff] +line-length = 99 +select = ["E", "W", "F", "C", "N", "D", "I001"] +extend-ignore = [ + "D203", + "D204", + "D213", + "D215", + "D400", + "D404", + "D406", + "D407", + "D408", + "D409", + "D413", +] +ignore = ["E501", "D107"] +extend-exclude = ["__pycache__", "*.egg_info"] +per-file-ignores = {"tests/*" = ["D100","D101","D102","D103","D104"]} + +[tool.ruff.mccabe] +max-complexity = 10 + +[tool.codespell] +skip = "build,lib,venv,icon.svg,.tox,.git,.mypy_cache,.ruff_cache,.coverage" + +[tool.pyright] +include = ["src/**.py"] + diff --git a/charms/gnocchi-k8s/requirements.txt b/charms/gnocchi-k8s/requirements.txt new file mode 100644 index 00000000..99f36564 --- /dev/null +++ b/charms/gnocchi-k8s/requirements.txt @@ -0,0 +1,8 @@ +ops +jinja2 +git+https://github.com/openstack/charm-ops-sunbeam#egg=ops_sunbeam +lightkube +# These are only needeed if the charm relates to ceph +git+https://github.com/openstack/charm-ops-interface-ceph-client#egg=interface_ceph_client +# Charmhelpers is only present as interface_ceph_client uses it. +git+https://github.com/juju/charm-helpers.git#egg=charmhelpers diff --git a/charms/gnocchi-k8s/src/charm.py b/charms/gnocchi-k8s/src/charm.py new file mode 100755 index 00000000..a6f5b8a4 --- /dev/null +++ b/charms/gnocchi-k8s/src/charm.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +"""Gnocchi Operator Charm. + +This charm provide Gnocchi services as part of an OpenStack deployment +""" + +import logging + +from ops.framework import StoredState +from ops.main import main + +import ops_sunbeam.charm as sunbeam_charm + +logger = logging.getLogger(__name__) + + +class GnocchiOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): + """Charm the service.""" + + _state = StoredState() + service_name = "gnocchi-api" + wsgi_admin_script = '/usr/bin/gnocchi-api-wsgi' + wsgi_public_script = '/usr/bin/gnocchi-api-wsgi' + + db_sync_cmds = [ + ['/snap/bin/gnocchi.upgrade', '--log-file=/var/snap/gnocchi/common/log/gnocchi-upgrade.log'] + ] + + @property + def service_conf(self) -> str: + """Service default configuration file.""" + return f"/etc/gnocchi/gnocchi.conf" + + @property + def service_user(self) -> str: + """Service user file and directory ownership.""" + return 'gnocchi' + + @property + def service_group(self) -> str: + """Service group file and directory ownership.""" + return 'gnocchi' + + @property + def service_endpoints(self): + return [ + { + 'service_name': 'gnocchi', + 'type': 'gnocchi', + 'description': "OpenStack Gnocchi API", + 'internal_url': f'{self.internal_url}', + 'public_url': f'{self.public_url}', + 'admin_url': f'{self.admin_url}'}] + + @property + def default_public_ingress_port(self): + return 8041 + + +if __name__ == "__main__": + main(GnocchiOperatorCharm) diff --git a/charms/gnocchi-k8s/src/templates/ceph.conf.j2 b/charms/gnocchi-k8s/src/templates/ceph.conf.j2 new file mode 100644 index 00000000..c293ae90 --- /dev/null +++ b/charms/gnocchi-k8s/src/templates/ceph.conf.j2 @@ -0,0 +1,22 @@ +############################################################################### +# [ WARNING ] +# ceph configuration file maintained in aso +# local changes may be overwritten. +############################################################################### +[global] +{% if ceph.auth -%} +auth_supported = {{ ceph.auth }} +mon host = {{ ceph.mon_hosts }} +{% endif -%} +keyring = /etc/ceph/$cluster.$name.keyring +log to syslog = false +err to syslog = false +clog to syslog = false +{% if ceph.rbd_features %} +rbd default features = {{ ceph.rbd_features }} +{% endif %} + +[client] +{% if ceph_config.rbd_default_data_pool -%} +rbd default data pool = {{ ceph_config.rbd_default_data_pool }} +{% endif %} diff --git a/charms/gnocchi-k8s/src/templates/parts/database-connection b/charms/gnocchi-k8s/src/templates/parts/database-connection new file mode 100644 index 00000000..1fd70ce2 --- /dev/null +++ b/charms/gnocchi-k8s/src/templates/parts/database-connection @@ -0,0 +1,3 @@ +{% if database.connection -%} +connection = {{ database.connection }} +{% endif -%} diff --git a/charms/gnocchi-k8s/src/templates/parts/identity-data b/charms/gnocchi-k8s/src/templates/parts/identity-data new file mode 100644 index 00000000..4b4af021 --- /dev/null +++ b/charms/gnocchi-k8s/src/templates/parts/identity-data @@ -0,0 +1,10 @@ +{% if identity_service.internal_host -%} +www_authenticate_uri = {{ identity_service.internal_protocol }}://{{ identity_service.internal_host }}:{{ identity_service.internal_port }} +auth_url = {{ identity_service.internal_protocol }}://{{ identity_service.internal_host }}:{{ identity_service.internal_port }} +auth_type = password +project_domain_name = {{ identity_service.service_domain_name }} +user_domain_name = {{ identity_service.service_domain_name }} +project_name = {{ identity_service.service_project_name }} +username = {{ identity_service.service_user_name }} +password = {{ identity_service.service_password }} +{% endif -%} diff --git a/charms/gnocchi-k8s/src/templates/parts/section-database b/charms/gnocchi-k8s/src/templates/parts/section-database new file mode 100644 index 00000000..986d9b10 --- /dev/null +++ b/charms/gnocchi-k8s/src/templates/parts/section-database @@ -0,0 +1,3 @@ +[database] +{% include "parts/database-connection" %} +connection_recycle_time = 200 diff --git a/charms/gnocchi-k8s/src/templates/parts/section-federation b/charms/gnocchi-k8s/src/templates/parts/section-federation new file mode 100644 index 00000000..65ee99ed --- /dev/null +++ b/charms/gnocchi-k8s/src/templates/parts/section-federation @@ -0,0 +1,10 @@ +{% if trusted_dashboards %} +[federation] +{% for dashboard_url in trusted_dashboards -%} +trusted_dashboard = {{ dashboard_url }} +{% endfor -%} +{% endif %} +{% for sp in fid_sps -%} +[{{ sp['protocol-name'] }}] +remote_id_attribute = {{ sp['remote-id-attribute'] }} +{% endfor -%} diff --git a/charms/gnocchi-k8s/src/templates/parts/section-identity b/charms/gnocchi-k8s/src/templates/parts/section-identity new file mode 100644 index 00000000..7568a9a4 --- /dev/null +++ b/charms/gnocchi-k8s/src/templates/parts/section-identity @@ -0,0 +1,2 @@ +[keystone_authtoken] +{% include "parts/identity-data" %} diff --git a/charms/gnocchi-k8s/src/templates/parts/section-middleware b/charms/gnocchi-k8s/src/templates/parts/section-middleware new file mode 100644 index 00000000..e65f1d98 --- /dev/null +++ b/charms/gnocchi-k8s/src/templates/parts/section-middleware @@ -0,0 +1,6 @@ +{% for section in sections -%} +[{{section}}] +{% for key, value in sections[section].items() -%} +{{ key }} = {{ value }} +{% endfor %} +{%- endfor %} diff --git a/charms/gnocchi-k8s/src/templates/parts/section-signing b/charms/gnocchi-k8s/src/templates/parts/section-signing new file mode 100644 index 00000000..cb7d69ae --- /dev/null +++ b/charms/gnocchi-k8s/src/templates/parts/section-signing @@ -0,0 +1,15 @@ +{% if enable_signing -%} +[signing] +{% if certfile -%} +certfile = {{ certfile }} +{% endif -%} +{% if keyfile -%} +keyfile = {{ keyfile }} +{% endif -%} +{% if ca_certs -%} +ca_certs = {{ ca_certs }} +{% endif -%} +{% if ca_key -%} +ca_key = {{ ca_key }} +{% endif -%} +{% endif -%} \ No newline at end of file diff --git a/charms/gnocchi-k8s/src/templates/wsgi-gnocchi-api.conf b/charms/gnocchi-k8s/src/templates/wsgi-gnocchi-api.conf new file mode 100644 index 00000000..b34c076e --- /dev/null +++ b/charms/gnocchi-k8s/src/templates/wsgi-gnocchi-api.conf @@ -0,0 +1,27 @@ +Listen {{ wsgi_config.public_port }} +<VirtualHost *:{{ wsgi_config.public_port }}> + WSGIDaemonProcess {{ wsgi_config.group }} processes=3 threads=1 user={{ wsgi_config.user }} group={{ wsgi_config.group }} \ + display-name=%{GROUP} + WSGIProcessGroup {{ wsgi_config.group }} + {% if ingress_internal.ingress_path -%} + WSGIScriptAlias {{ ingress_internal.ingress_path }} {{ wsgi_config.wsgi_public_script }} + {% endif -%} + WSGIScriptAlias / {{ wsgi_config.wsgi_public_script }} + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + <IfVersion >= 2.4> + ErrorLogFormat "%{cu}t %M" + </IfVersion> + ErrorLog {{ wsgi_config.error_log }} + CustomLog {{ wsgi_config.custom_log }} combined + + <Directory /usr/bin> + <IfVersion >= 2.4> + Require all granted + </IfVersion> + <IfVersion < 2.4> + Order allow,deny + Allow from all + </IfVersion> + </Directory> +</VirtualHost> diff --git a/charms/gnocchi-k8s/src/templates/wsgi-template.conf.j2 b/charms/gnocchi-k8s/src/templates/wsgi-template.conf.j2 new file mode 100644 index 00000000..b34c076e --- /dev/null +++ b/charms/gnocchi-k8s/src/templates/wsgi-template.conf.j2 @@ -0,0 +1,27 @@ +Listen {{ wsgi_config.public_port }} +<VirtualHost *:{{ wsgi_config.public_port }}> + WSGIDaemonProcess {{ wsgi_config.group }} processes=3 threads=1 user={{ wsgi_config.user }} group={{ wsgi_config.group }} \ + display-name=%{GROUP} + WSGIProcessGroup {{ wsgi_config.group }} + {% if ingress_internal.ingress_path -%} + WSGIScriptAlias {{ ingress_internal.ingress_path }} {{ wsgi_config.wsgi_public_script }} + {% endif -%} + WSGIScriptAlias / {{ wsgi_config.wsgi_public_script }} + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + <IfVersion >= 2.4> + ErrorLogFormat "%{cu}t %M" + </IfVersion> + ErrorLog {{ wsgi_config.error_log }} + CustomLog {{ wsgi_config.custom_log }} combined + + <Directory /usr/bin> + <IfVersion >= 2.4> + Require all granted + </IfVersion> + <IfVersion < 2.4> + Order allow,deny + Allow from all + </IfVersion> + </Directory> +</VirtualHost> diff --git a/charms/gnocchi-k8s/test-requirements.txt b/charms/gnocchi-k8s/test-requirements.txt new file mode 100644 index 00000000..8057d2c6 --- /dev/null +++ b/charms/gnocchi-k8s/test-requirements.txt @@ -0,0 +1,17 @@ +# This file is managed centrally. If you find the need to modify this as a +# one-off, please don't. Intead, consult #openstack-charms and ask about +# requirements management in charms via bot-control. Thank you. +charm-tools>=2.4.4 +coverage>=3.6 +mock>=1.2 +flake8>=2.2.4,<=2.4.1 +pyflakes==2.1.1 +stestr>=2.2.0 +requests>=2.18.4 +psutil +# oslo.i18n dropped py35 support +oslo.i18n<4.0.0 +git+https://github.com/openstack-charmers/zaza.git#egg=zaza +git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack +pytz # workaround for 14.04 pip/tox +pyudev # for ceph-* charm unit tests (not mocked?) diff --git a/charms/gnocchi-k8s/tests/integration/test_charm.py b/charms/gnocchi-k8s/tests/integration/test_charm.py new file mode 100644 index 00000000..6823bbd9 --- /dev/null +++ b/charms/gnocchi-k8s/tests/integration/test_charm.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# Copyright 2023 Canonical Ltd. +# See LICENSE file for licensing details. + +import asyncio +import logging +from pathlib import Path + +import pytest +import yaml +from pytest_operator.plugin import OpsTest + +logger = logging.getLogger(__name__) + +METADATA = yaml.safe_load(Path("./metadata.yaml").read_text()) +APP_NAME = METADATA["name"] + + +@pytest.mark.abort_on_fail +async def test_build_and_deploy(ops_test: OpsTest): + """Build the charm-under-test and deploy it together with related charms. + + Assert on the unit status before any relations/configurations take place. + """ + # Build and deploy charm from local source folder + charm = await ops_test.build_charm(".") + resources = {"httpbin-image": METADATA["resources"]["httpbin-image"]["upstream-source"]} + + # Deploy the charm and wait for active/idle status + await asyncio.gather( + ops_test.model.deploy(charm, resources=resources, application_name=APP_NAME), + ops_test.model.wait_for_idle( + apps=[APP_NAME], status="active", raise_on_blocked=True, timeout=1000 + ), + ) diff --git a/charms/gnocchi-k8s/tests/unit/test_charm.py b/charms/gnocchi-k8s/tests/unit/test_charm.py new file mode 100644 index 00000000..ba8a0efd --- /dev/null +++ b/charms/gnocchi-k8s/tests/unit/test_charm.py @@ -0,0 +1,68 @@ +# Copyright 2023 Canonical Ltd. +# See LICENSE file for licensing details. +# +# Learn more about testing at: https://juju.is/docs/sdk/testing + +import unittest + +import ops +import ops.testing +from charm import GnocchiK8SCharm + + +class TestCharm(unittest.TestCase): + def setUp(self): + self.harness = ops.testing.Harness(GnocchiK8SCharm) + self.addCleanup(self.harness.cleanup) + self.harness.begin() + + def test_httpbin_pebble_ready(self): + # Expected plan after Pebble ready with default config + expected_plan = { + "services": { + "httpbin": { + "override": "replace", + "summary": "httpbin", + "command": "gunicorn -b 0.0.0.0:80 httpbin:app -k gevent", + "startup": "enabled", + "environment": {"GUNICORN_CMD_ARGS": "--log-level info"}, + } + }, + } + # Simulate the container coming up and emission of pebble-ready event + self.harness.container_pebble_ready("httpbin") + # Get the plan now we've run PebbleReady + updated_plan = self.harness.get_container_pebble_plan("httpbin").to_dict() + # Check we've got the plan we expected + self.assertEqual(expected_plan, updated_plan) + # Check the service was started + service = self.harness.model.unit.get_container("httpbin").get_service("httpbin") + self.assertTrue(service.is_running()) + # Ensure we set an ActiveStatus with no message + self.assertEqual(self.harness.model.unit.status, ops.ActiveStatus()) + + def test_config_changed_valid_can_connect(self): + # Ensure the simulated Pebble API is reachable + self.harness.set_can_connect("httpbin", True) + # Trigger a config-changed event with an updated value + self.harness.update_config({"log-level": "debug"}) + # Get the plan now we've run PebbleReady + updated_plan = self.harness.get_container_pebble_plan("httpbin").to_dict() + updated_env = updated_plan["services"]["httpbin"]["environment"] + # Check the config change was effective + self.assertEqual(updated_env, {"GUNICORN_CMD_ARGS": "--log-level debug"}) + self.assertEqual(self.harness.model.unit.status, ops.ActiveStatus()) + + def test_config_changed_valid_cannot_connect(self): + # Trigger a config-changed event with an updated value + self.harness.update_config({"log-level": "debug"}) + # Check the charm is in WaitingStatus + self.assertIsInstance(self.harness.model.unit.status, ops.WaitingStatus) + + def test_config_changed_invalid(self): + # Ensure the simulated Pebble API is reachable + self.harness.set_can_connect("httpbin", True) + # Trigger a config-changed event with an updated value + self.harness.update_config({"log-level": "foobar"}) + # Check the charm is in BlockedStatus + self.assertIsInstance(self.harness.model.unit.status, ops.BlockedStatus) diff --git a/charms/gnocchi-k8s/tox.ini b/charms/gnocchi-k8s/tox.ini new file mode 100644 index 00000000..31301b80 --- /dev/null +++ b/charms/gnocchi-k8s/tox.ini @@ -0,0 +1,134 @@ +# Operator charm (with zaza): tox.ini + +[tox] +envlist = pep8,py3 +skipsdist = True +# NOTE: Avoid build/test env pollution by not enabling sitepackages. +sitepackages = False +# NOTE: Avoid false positives by not skipping missing interpreters. +skip_missing_interpreters = False +# NOTES: +# * We avoid the new dependency resolver by pinning pip < 20.3, see +# https://github.com/pypa/pip/issues/9187 +# * Pinning dependencies requires tox >= 3.2.0, see +# https://tox.readthedocs.io/en/latest/config.html#conf-requires +# * It is also necessary to pin virtualenv as a newer virtualenv would still +# lead to fetching the latest pip in the func* tox targets, see +# https://stackoverflow.com/a/38133283 +requires = pip < 20.3 + virtualenv < 20.0 +# NOTE: https://wiki.canonical.com/engineering/OpenStack/InstallLatestToxOnOsci +minversion = 3.2.0 + +[testenv] +setenv = VIRTUAL_ENV={envdir} + PYTHONHASHSEED=0 + CHARM_DIR={envdir} +install_command = + pip install {opts} {packages} +commands = stestr run --slowest {posargs} +whitelist_externals = + git + add-to-archive.py + bash + charmcraft +passenv = HOME TERM CS_* OS_* TEST_* +deps = -r{toxinidir}/test-requirements.txt + +[testenv:py35] +basepython = python3.5 +# python3.5 is irrelevant on a focal+ charm. +commands = /bin/true + +[testenv:py36] +basepython = python3.6 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:py37] +basepython = python3.7 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:py38] +basepython = python3.8 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:py3] +basepython = python3 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt + +[testenv:pep8] +basepython = python3 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +commands = flake8 {posargs} src unit_tests tests + +[testenv:cover] +# Technique based heavily upon +# https://github.com/openstack/nova/blob/master/tox.ini +basepython = python3 +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +setenv = + {[testenv]setenv} + PYTHON=coverage run +commands = + coverage erase + stestr run --slowest {posargs} + coverage combine + coverage html -d cover + coverage xml -o cover/coverage.xml + coverage report + +[coverage:run] +branch = True +concurrency = multiprocessing +parallel = True +source = + . +omit = + .tox/* + */charmhelpers/* + unit_tests/* + +[testenv:venv] +basepython = python3 +commands = {posargs} + +[testenv:build] +basepython = python3 +deps = -r{toxinidir}/build-requirements.txt +commands = + charmcraft build + +[testenv:func-noop] +basepython = python3 +commands = + functest-run-suite --help + +[testenv:func] +basepython = python3 +commands = + functest-run-suite --keep-model + +[testenv:func-smoke] +basepython = python3 +commands = + functest-run-suite --keep-model --smoke + +[testenv:func-dev] +basepython = python3 +commands = + functest-run-suite --keep-model --dev + +[testenv:func-target] +basepython = python3 +commands = + functest-run-suite --keep-model --bundle {posargs} + +[flake8] +# Ignore E902 because the unit_tests directory is missing in the built charm. +ignore = E402,E226,E902