Initial charm
Initial charm generated by running - git init - charmcraft init - cookie cutter code from charm-ops-sunbeam
This commit is contained in:
commit
df41decd82
9
charms/gnocchi-k8s/.gitignore
vendored
Normal file
9
charms/gnocchi-k8s/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
venv/
|
||||||
|
build/
|
||||||
|
*.charm
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
.idea
|
||||||
|
.vscode/
|
34
charms/gnocchi-k8s/CONTRIBUTING.md
Normal file
34
charms/gnocchi-k8s/CONTRIBUTING.md
Normal file
@ -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>
|
202
charms/gnocchi-k8s/LICENSE
Normal file
202
charms/gnocchi-k8s/LICENSE
Normal file
@ -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.
|
26
charms/gnocchi-k8s/README.md
Normal file
26
charms/gnocchi-k8s/README.md
Normal file
@ -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.
|
2
charms/gnocchi-k8s/actions.yaml
Normal file
2
charms/gnocchi-k8s/actions.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# NOTE: no actions yet!
|
||||||
|
{ }
|
19
charms/gnocchi-k8s/charmcraft.yaml
Normal file
19
charms/gnocchi-k8s/charmcraft.yaml
Normal file
@ -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
|
27
charms/gnocchi-k8s/config.yaml
Normal file
27
charms/gnocchi-k8s/config.yaml
Normal file
@ -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
|
47
charms/gnocchi-k8s/metadata.yaml
Normal file
47
charms/gnocchi-k8s/metadata.yaml
Normal file
@ -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
|
46
charms/gnocchi-k8s/pyproject.toml
Normal file
46
charms/gnocchi-k8s/pyproject.toml
Normal file
@ -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"]
|
||||||
|
|
8
charms/gnocchi-k8s/requirements.txt
Normal file
8
charms/gnocchi-k8s/requirements.txt
Normal file
@ -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
|
61
charms/gnocchi-k8s/src/charm.py
Executable file
61
charms/gnocchi-k8s/src/charm.py
Executable file
@ -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)
|
22
charms/gnocchi-k8s/src/templates/ceph.conf.j2
Normal file
22
charms/gnocchi-k8s/src/templates/ceph.conf.j2
Normal file
@ -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 %}
|
@ -0,0 +1,3 @@
|
|||||||
|
{% if database.connection -%}
|
||||||
|
connection = {{ database.connection }}
|
||||||
|
{% endif -%}
|
10
charms/gnocchi-k8s/src/templates/parts/identity-data
Normal file
10
charms/gnocchi-k8s/src/templates/parts/identity-data
Normal file
@ -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 -%}
|
3
charms/gnocchi-k8s/src/templates/parts/section-database
Normal file
3
charms/gnocchi-k8s/src/templates/parts/section-database
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[database]
|
||||||
|
{% include "parts/database-connection" %}
|
||||||
|
connection_recycle_time = 200
|
10
charms/gnocchi-k8s/src/templates/parts/section-federation
Normal file
10
charms/gnocchi-k8s/src/templates/parts/section-federation
Normal file
@ -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 -%}
|
2
charms/gnocchi-k8s/src/templates/parts/section-identity
Normal file
2
charms/gnocchi-k8s/src/templates/parts/section-identity
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[keystone_authtoken]
|
||||||
|
{% include "parts/identity-data" %}
|
@ -0,0 +1,6 @@
|
|||||||
|
{% for section in sections -%}
|
||||||
|
[{{section}}]
|
||||||
|
{% for key, value in sections[section].items() -%}
|
||||||
|
{{ key }} = {{ value }}
|
||||||
|
{% endfor %}
|
||||||
|
{%- endfor %}
|
15
charms/gnocchi-k8s/src/templates/parts/section-signing
Normal file
15
charms/gnocchi-k8s/src/templates/parts/section-signing
Normal file
@ -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 -%}
|
27
charms/gnocchi-k8s/src/templates/wsgi-gnocchi-api.conf
Normal file
27
charms/gnocchi-k8s/src/templates/wsgi-gnocchi-api.conf
Normal file
@ -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>
|
27
charms/gnocchi-k8s/src/templates/wsgi-template.conf.j2
Normal file
27
charms/gnocchi-k8s/src/templates/wsgi-template.conf.j2
Normal file
@ -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>
|
17
charms/gnocchi-k8s/test-requirements.txt
Normal file
17
charms/gnocchi-k8s/test-requirements.txt
Normal file
@ -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?)
|
35
charms/gnocchi-k8s/tests/integration/test_charm.py
Normal file
35
charms/gnocchi-k8s/tests/integration/test_charm.py
Normal file
@ -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
|
||||||
|
),
|
||||||
|
)
|
68
charms/gnocchi-k8s/tests/unit/test_charm.py
Normal file
68
charms/gnocchi-k8s/tests/unit/test_charm.py
Normal file
@ -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)
|
134
charms/gnocchi-k8s/tox.ini
Normal file
134
charms/gnocchi-k8s/tox.ini
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user