# Execute tests for charms
CHARM_PATH="./installers/charm"
NEW_CHARMS_NAMES="osm-keystone osm-lcm osm-mon osm-nbi osm-ng-ui osm-pol osm-ro vca-integrator-operator"
-OLD_CHARMS_NAMES="prometheus grafana"
for charm in $NEW_CHARMS_NAMES; do
if [ $(git diff --name-only "origin/${GERRIT_BRANCH}" -- "installers/charm/${charm}" | wc -l) -ne 0 ]; then
echo "Running tox for ${charm}"
cd "${CURRENT_DIR}"
fi
done
-for charm in $OLD_CHARMS_NAMES; do
- if [ $(git diff --name-only "origin/${GERRIT_BRANCH}" -- "installers/charm/${charm}" | wc -l) -ne 0 ]; then
- echo "Running tox for ${charm}"
- cd "${CHARM_PATH}/${charm}"
- TOX_PARALLEL_NO_SPINNER=1 tox --parallel=auto
- cd "${CURRENT_DIR}"
- fi
-done
# Execute linting test for OSM helm chart
helm lint installers/helm/osm
+++ /dev/null
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-venv
-.vscode
-build
-*.charm
-.coverage
-coverage.xml
-.stestr
-cover
-release
\ No newline at end of file
+++ /dev/null
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-venv
-.vscode
-build
-*.charm
-.coverage
-coverage.xml
-.gitignore
-.stestr
-cover
-release
-tests/
-requirements*
-tox.ini
+++ /dev/null
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
----
-extends: default
-
-yaml-files:
- - "*.yaml"
- - "*.yml"
- - ".yamllint"
-ignore: |
- .tox
- cover/
- build/
- venv
- release/
+++ /dev/null
-<!-- Copyright 2021 Canonical Ltd.
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may
-not use this file except in compliance with the License. You may obtain
-a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-License for the specific language governing permissions and limitations
-under the License.
-
-For those usages not covered by the Apache License, Version 2.0 please
-contact: legal@canonical.com
-
-To get in touch with the maintainers, please contact:
-osm-charmers@lists.launchpad.net -->
-
-# Prometheus operator Charm for Kubernetes
-
-## Requirements
+++ /dev/null
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-backup:
- description: "Do a mongodb backup"
+++ /dev/null
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-type: charm
-bases:
- - build-on:
- - name: ubuntu
- channel: "20.04"
- architectures: ["amd64"]
- run-on:
- - name: ubuntu
- channel: "20.04"
- architectures:
- - amd64
- - aarch64
- - arm64
-parts:
- charm:
- build-packages:
- - cargo
- - git
- - libffi-dev
- - rustc
+++ /dev/null
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-options:
- web-subpath:
- description: Subpath for accessing Prometheus
- type: string
- default: /
- default-target:
- description: Default target to be added in Prometheus
- type: string
- default: ""
- max_file_size:
- type: int
- description: |
- The maximum file size, in megabytes. If there is a reverse proxy in front
- of Keystone, it may need to be configured to handle the requested size.
- Note: if set to 0, there is no limit.
- default: 0
- ingress_class:
- type: string
- description: |
- Ingress class name. This is useful for selecting the ingress to be used
- in case there are multiple ingresses in the underlying k8s clusters.
- ingress_whitelist_source_range:
- type: string
- description: |
- A comma-separated list of CIDRs to store in the
- ingress.kubernetes.io/whitelist-source-range annotation.
-
- This can be used to lock down access to
- Keystone based on source IP address.
- default: ""
- tls_secret_name:
- type: string
- description: TLS Secret name
- default: ""
- site_url:
- type: string
- description: Ingress URL
- default: ""
- cluster_issuer:
- type: string
- description: Name of the cluster issuer for TLS certificates
- default: ""
- enable_web_admin_api:
- type: boolean
- description: Boolean to enable the web admin api
- default: false
- image_pull_policy:
- type: string
- description: |
- ImagePullPolicy configuration for the pod.
- Possible values: always, ifnotpresent, never
- default: always
- security_context:
- description: Enables the security context of the pods
- type: boolean
- default: false
- web_config_username:
- type: string
- default: admin
- description: Username to access the Prometheus Web Interface
- web_config_password:
- type: string
- default: admin
- description: Password to access the Prometheus Web Interface
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- version="1.1"
- id="Layer_1"
- x="0px"
- y="0px"
- width="115.333px"
- height="114px"
- viewBox="0 0 115.333 114"
- enable-background="new 0 0 115.333 114"
- xml:space="preserve"
- sodipodi:docname="prometheus_logo_orange.svg"
- inkscape:version="0.92.1 r15371"><metadata
- id="metadata4495"><rdf:RDF><cc:Work
- rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
- id="defs4493" /><sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1484"
- inkscape:window-height="886"
- id="namedview4491"
- showgrid="false"
- inkscape:zoom="5.2784901"
- inkscape:cx="60.603667"
- inkscape:cy="60.329656"
- inkscape:window-x="54"
- inkscape:window-y="7"
- inkscape:window-maximized="0"
- inkscape:current-layer="Layer_1" /><g
- id="Layer_2" /><path
- style="fill:#e6522c;fill-opacity:1"
- inkscape:connector-curvature="0"
- id="path4486"
- d="M 56.667,0.667 C 25.372,0.667 0,26.036 0,57.332 c 0,31.295 25.372,56.666 56.667,56.666 31.295,0 56.666,-25.371 56.666,-56.666 0,-31.296 -25.372,-56.665 -56.666,-56.665 z m 0,106.055 c -8.904,0 -16.123,-5.948 -16.123,-13.283 H 72.79 c 0,7.334 -7.219,13.283 -16.123,13.283 z M 83.297,89.04 H 30.034 V 79.382 H 83.298 V 89.04 Z M 83.106,74.411 H 30.186 C 30.01,74.208 29.83,74.008 29.66,73.802 24.208,67.182 22.924,63.726 21.677,60.204 c -0.021,-0.116 6.611,1.355 11.314,2.413 0,0 2.42,0.56 5.958,1.205 -3.397,-3.982 -5.414,-9.044 -5.414,-14.218 0,-11.359 8.712,-21.285 5.569,-29.308 3.059,0.249 6.331,6.456 6.552,16.161 3.252,-4.494 4.613,-12.701 4.613,-17.733 0,-5.21 3.433,-11.262 6.867,-11.469 -3.061,5.045 0.793,9.37 4.219,20.099 1.285,4.03 1.121,10.812 2.113,15.113 C 63.797,33.534 65.333,20.5 71,16 c -2.5,5.667 0.37,12.758 2.333,16.167 3.167,5.5 5.087,9.667 5.087,17.548 0,5.284 -1.951,10.259 -5.242,14.148 3.742,-0.702 6.326,-1.335 6.326,-1.335 l 12.152,-2.371 c 10e-4,-10e-4 -1.765,7.261 -8.55,14.254 z" /></svg>
\ No newline at end of file
+++ /dev/null
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-name: osm-prometheus
-summary: OSM Prometheus
-description: |
- A CAAS charm to deploy OSM's Prometheus.
-series:
- - kubernetes
-tags:
- - kubernetes
- - osm
- - prometheus
-min-juju-version: 2.8.0
-deployment:
- type: stateful
- service: cluster
-resources:
- backup-image:
- type: oci-image
- description: Container image to run backup actions
- upstream-source: "ed1000/prometheus-backup:latest"
- image:
- type: oci-image
- description: OSM docker image for Prometheus
- upstream-source: "ubuntu/prometheus:latest"
-provides:
- prometheus:
- interface: prometheus
-storage:
- data:
- type: filesystem
- location: /prometheus
+++ /dev/null
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-mock==4.0.3
+++ /dev/null
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
-requests
-urllib3>1.25.9
-bcrypt
+++ /dev/null
-#!/usr/bin/env python3
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-# pylint: disable=E0213
-
-import base64
-from ipaddress import ip_network
-import logging
-from typing import NoReturn, Optional
-from urllib.parse import urlparse
-
-import bcrypt
-from oci_image import OCIImageResource
-from ops.framework import EventBase
-from ops.main import main
-from opslib.osm.charm import CharmedOsmBase
-from opslib.osm.interfaces.prometheus import PrometheusServer
-from opslib.osm.pod import (
- ContainerV3Builder,
- FilesV3Builder,
- IngressResourceV3Builder,
- PodSpecV3Builder,
-)
-from opslib.osm.validator import (
- ModelValidator,
- validator,
-)
-import requests
-
-
-logger = logging.getLogger(__name__)
-
-PORT = 9090
-
-
-class ConfigModel(ModelValidator):
- web_subpath: str
- default_target: str
- max_file_size: int
- site_url: Optional[str]
- cluster_issuer: Optional[str]
- ingress_class: Optional[str]
- ingress_whitelist_source_range: Optional[str]
- tls_secret_name: Optional[str]
- enable_web_admin_api: bool
- image_pull_policy: str
- security_context: bool
- web_config_username: str
- web_config_password: str
-
- @validator("web_subpath")
- def validate_web_subpath(cls, v):
- if len(v) < 1:
- raise ValueError("web-subpath must be a non-empty string")
- return v
-
- @validator("max_file_size")
- def validate_max_file_size(cls, v):
- if v < 0:
- raise ValueError("value must be equal or greater than 0")
- return v
-
- @validator("site_url")
- def validate_site_url(cls, v):
- if v:
- parsed = urlparse(v)
- if not parsed.scheme.startswith("http"):
- raise ValueError("value must start with http")
- return v
-
- @validator("ingress_whitelist_source_range")
- def validate_ingress_whitelist_source_range(cls, v):
- if v:
- ip_network(v)
- return v
-
- @validator("image_pull_policy")
- def validate_image_pull_policy(cls, v):
- values = {
- "always": "Always",
- "ifnotpresent": "IfNotPresent",
- "never": "Never",
- }
- v = v.lower()
- if v not in values.keys():
- raise ValueError("value must be always, ifnotpresent or never")
- return values[v]
-
-
-class PrometheusCharm(CharmedOsmBase):
-
- """Prometheus Charm."""
-
- def __init__(self, *args) -> NoReturn:
- """Prometheus Charm constructor."""
- super().__init__(*args, oci_image="image")
-
- # Registering provided relation events
- self.prometheus = PrometheusServer(self, "prometheus")
- self.framework.observe(
- self.on.prometheus_relation_joined, # pylint: disable=E1101
- self._publish_prometheus_info,
- )
-
- # Registering actions
- self.framework.observe(
- self.on.backup_action, # pylint: disable=E1101
- self._on_backup_action,
- )
-
- def _publish_prometheus_info(self, event: EventBase) -> NoReturn:
- config = ConfigModel(**dict(self.config))
- self.prometheus.publish_info(
- self.app.name,
- PORT,
- user=config.web_config_username,
- password=config.web_config_password,
- )
-
- def _on_backup_action(self, event: EventBase) -> NoReturn:
- url = f"http://{self.model.app.name}:{PORT}/api/v1/admin/tsdb/snapshot"
- result = requests.post(url)
-
- if result.status_code == 200:
- event.set_results({"backup-name": result.json()["name"]})
- else:
- event.fail(f"status-code: {result.status_code}")
-
- def _build_config_file(self, config: ConfigModel):
- files_builder = FilesV3Builder()
- files_builder.add_file(
- "prometheus.yml",
- (
- "global:\n"
- " scrape_interval: 15s\n"
- " evaluation_interval: 15s\n"
- "alerting:\n"
- " alertmanagers:\n"
- " - static_configs:\n"
- " - targets:\n"
- "rule_files:\n"
- "scrape_configs:\n"
- " - job_name: 'prometheus'\n"
- " static_configs:\n"
- f" - targets: [{config.default_target}]\n"
- ),
- )
- return files_builder.build()
-
- def _build_webconfig_file(self):
- files_builder = FilesV3Builder()
- files_builder.add_file("web.yml", "web-config-file", secret=True)
- return files_builder.build()
-
- def build_pod_spec(self, image_info):
- # Validate config
- config = ConfigModel(**dict(self.config))
- # Create Builder for the PodSpec
- pod_spec_builder = PodSpecV3Builder(
- enable_security_context=config.security_context
- )
-
- # Build Backup Container
- backup_image = OCIImageResource(self, "backup-image")
- backup_image_info = backup_image.fetch()
- backup_container_builder = ContainerV3Builder("prom-backup", backup_image_info)
- backup_container = backup_container_builder.build()
-
- # Add backup container to pod spec
- pod_spec_builder.add_container(backup_container)
-
- # Add pod secrets
- prometheus_secret_name = f"{self.app.name}-secret"
- pod_spec_builder.add_secret(
- prometheus_secret_name,
- {
- "web-config-file": (
- "basic_auth_users:\n"
- f" {config.web_config_username}: {self._hash_password(config.web_config_password)}\n"
- )
- },
- )
-
- # Build Container
- container_builder = ContainerV3Builder(
- self.app.name,
- image_info,
- config.image_pull_policy,
- run_as_non_root=config.security_context,
- )
- container_builder.add_port(name=self.app.name, port=PORT)
- token = self._base64_encode(
- f"{config.web_config_username}:{config.web_config_password}"
- )
- container_builder.add_http_readiness_probe(
- "/-/ready",
- PORT,
- initial_delay_seconds=10,
- timeout_seconds=30,
- http_headers=[("Authorization", f"Basic {token}")],
- )
- container_builder.add_http_liveness_probe(
- "/-/healthy",
- PORT,
- initial_delay_seconds=30,
- period_seconds=30,
- http_headers=[("Authorization", f"Basic {token}")],
- )
- command = [
- "/bin/prometheus",
- "--config.file=/etc/prometheus/prometheus.yml",
- "--web.config.file=/etc/prometheus/web-config/web.yml",
- "--storage.tsdb.path=/prometheus",
- "--web.console.libraries=/usr/share/prometheus/console_libraries",
- "--web.console.templates=/usr/share/prometheus/consoles",
- f"--web.route-prefix={config.web_subpath}",
- f"--web.external-url=http://localhost:{PORT}{config.web_subpath}",
- ]
- if config.enable_web_admin_api:
- command.append("--web.enable-admin-api")
- container_builder.add_command(command)
- container_builder.add_volume_config(
- "config", "/etc/prometheus", self._build_config_file(config)
- )
- container_builder.add_volume_config(
- "web-config",
- "/etc/prometheus/web-config",
- self._build_webconfig_file(),
- secret_name=prometheus_secret_name,
- )
- container = container_builder.build()
- # Add container to pod spec
- pod_spec_builder.add_container(container)
- # Add ingress resources to pod spec if site url exists
- if config.site_url:
- parsed = urlparse(config.site_url)
- annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size": "{}".format(
- str(config.max_file_size) + "m"
- if config.max_file_size > 0
- else config.max_file_size
- )
- }
- if config.ingress_class:
- annotations["kubernetes.io/ingress.class"] = config.ingress_class
- ingress_resource_builder = IngressResourceV3Builder(
- f"{self.app.name}-ingress", annotations
- )
-
- if config.ingress_whitelist_source_range:
- annotations[
- "nginx.ingress.kubernetes.io/whitelist-source-range"
- ] = config.ingress_whitelist_source_range
-
- if config.cluster_issuer:
- annotations["cert-manager.io/cluster-issuer"] = config.cluster_issuer
-
- if parsed.scheme == "https":
- ingress_resource_builder.add_tls(
- [parsed.hostname], config.tls_secret_name
- )
- else:
- annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false"
-
- ingress_resource_builder.add_rule(parsed.hostname, self.app.name, PORT)
- ingress_resource = ingress_resource_builder.build()
- pod_spec_builder.add_ingress_resource(ingress_resource)
- return pod_spec_builder.build()
-
- def _hash_password(self, password):
- hashed_password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt())
- return hashed_password.decode()
-
- def _base64_encode(self, phrase: str) -> str:
- return base64.b64encode(phrase.encode("utf-8")).decode("utf-8")
-
-
-if __name__ == "__main__":
- main(PrometheusCharm)
+++ /dev/null
-#!/usr/bin/env python3
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-from ipaddress import ip_network
-import logging
-from typing import Any, Dict, List
-from urllib.parse import urlparse
-
-logger = logging.getLogger(__name__)
-
-
-def _validate_max_file_size(max_file_size: int, site_url: str) -> bool:
- """Validate max_file_size.
-
- Args:
- max_file_size (int): maximum file size allowed.
- site_url (str): endpoint url.
-
- Returns:
- bool: True if valid, false otherwise.
- """
- if not site_url:
- return True
-
- parsed = urlparse(site_url)
-
- if not parsed.scheme.startswith("http"):
- return True
-
- if max_file_size is None:
- return False
-
- return max_file_size >= 0
-
-
-def _validate_ip_network(network: str) -> bool:
- """Validate IP network.
-
- Args:
- network (str): IP network range.
-
- Returns:
- bool: True if valid, false otherwise.
- """
- if not network:
- return True
-
- try:
- ip_network(network)
- except ValueError:
- return False
-
- return True
-
-
-def _validate_data(config_data: Dict[str, Any], relation_data: Dict[str, Any]) -> bool:
- """Validates passed information.
-
- Args:
- config_data (Dict[str, Any]): configuration information.
- relation_data (Dict[str, Any]): relation information
-
- Raises:
- ValueError: when config and/or relation data is not valid.
- """
- config_validators = {
- "web_subpath": lambda value, _: isinstance(value, str) and len(value) > 0,
- "default_target": lambda value, _: isinstance(value, str),
- "site_url": lambda value, _: isinstance(value, str)
- if value is not None
- else True,
- "max_file_size": lambda value, values: _validate_max_file_size(
- value, values.get("site_url")
- ),
- "ingress_whitelist_source_range": lambda value, _: _validate_ip_network(value),
- "tls_secret_name": lambda value, _: isinstance(value, str)
- if value is not None
- else True,
- "enable_web_admin_api": lambda value, _: isinstance(value, bool),
- }
- relation_validators = {}
- problems = []
-
- for key, validator in config_validators.items():
- valid = validator(config_data.get(key), config_data)
-
- if not valid:
- problems.append(key)
-
- for key, validator in relation_validators.items():
- valid = validator(relation_data.get(key), relation_data)
-
- if not valid:
- problems.append(key)
-
- if len(problems) > 0:
- raise ValueError("Errors found in: {}".format(", ".join(problems)))
-
- return True
-
-
-def _make_pod_ports(port: int) -> List[Dict[str, Any]]:
- """Generate pod ports details.
-
- Args:
- port (int): port to expose.
-
- Returns:
- List[Dict[str, Any]]: pod port details.
- """
- return [{"name": "prometheus", "containerPort": port, "protocol": "TCP"}]
-
-
-def _make_pod_envconfig(
- config: Dict[str, Any], relation_state: Dict[str, Any]
-) -> Dict[str, Any]:
- """Generate pod environment configuration.
-
- Args:
- config (Dict[str, Any]): configuration information.
- relation_state (Dict[str, Any]): relation state information.
-
- Returns:
- Dict[str, Any]: pod environment configuration.
- """
- envconfig = {}
-
- return envconfig
-
-
-def _make_pod_ingress_resources(
- config: Dict[str, Any], app_name: str, port: int
-) -> List[Dict[str, Any]]:
- """Generate pod ingress resources.
-
- Args:
- config (Dict[str, Any]): configuration information.
- app_name (str): application name.
- port (int): port to expose.
-
- Returns:
- List[Dict[str, Any]]: pod ingress resources.
- """
- site_url = config.get("site_url")
-
- if not site_url:
- return
-
- parsed = urlparse(site_url)
-
- if not parsed.scheme.startswith("http"):
- return
-
- max_file_size = config["max_file_size"]
- ingress_whitelist_source_range = config["ingress_whitelist_source_range"]
-
- annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size": "{}".format(
- str(max_file_size) + "m" if max_file_size > 0 else max_file_size
- ),
- }
-
- if ingress_whitelist_source_range:
- annotations[
- "nginx.ingress.kubernetes.io/whitelist-source-range"
- ] = ingress_whitelist_source_range
-
- ingress_spec_tls = None
-
- if parsed.scheme == "https":
- ingress_spec_tls = [{"hosts": [parsed.hostname]}]
- tls_secret_name = config["tls_secret_name"]
- if tls_secret_name:
- ingress_spec_tls[0]["secretName"] = tls_secret_name
- else:
- annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false"
-
- ingress = {
- "name": "{}-ingress".format(app_name),
- "annotations": annotations,
- "spec": {
- "rules": [
- {
- "host": parsed.hostname,
- "http": {
- "paths": [
- {
- "path": "/",
- "backend": {
- "serviceName": app_name,
- "servicePort": port,
- },
- }
- ]
- },
- }
- ]
- },
- }
- if ingress_spec_tls:
- ingress["spec"]["tls"] = ingress_spec_tls
-
- return [ingress]
-
-
-def _make_pod_files(config: Dict[str, Any]) -> List[Dict[str, Any]]:
- """Generating ConfigMap information
-
- Args:
- config (Dict[str, Any]): configuration information.
-
- Returns:
- List[Dict[str, Any]]: ConfigMap information.
- """
- files = [
- {
- "name": "config",
- "mountPath": "/etc/prometheus",
- "files": [
- {
- "path": "prometheus.yml",
- "content": (
- "global:\n"
- " scrape_interval: 15s\n"
- " evaluation_interval: 15s\n"
- "alerting:\n"
- " alertmanagers:\n"
- " - static_configs:\n"
- " - targets:\n"
- "rule_files:\n"
- "scrape_configs:\n"
- " - job_name: 'prometheus'\n"
- " static_configs:\n"
- " - targets: [{}]\n".format(config["default_target"])
- ),
- }
- ],
- }
- ]
-
- return files
-
-
-def _make_readiness_probe(port: int) -> Dict[str, Any]:
- """Generate readiness probe.
-
- Args:
- port (int): service port.
-
- Returns:
- Dict[str, Any]: readiness probe.
- """
- return {
- "httpGet": {
- "path": "/-/ready",
- "port": port,
- },
- "initialDelaySeconds": 10,
- "timeoutSeconds": 30,
- }
-
-
-def _make_liveness_probe(port: int) -> Dict[str, Any]:
- """Generate liveness probe.
-
- Args:
- port (int): service port.
-
- Returns:
- Dict[str, Any]: liveness probe.
- """
- return {
- "httpGet": {
- "path": "/-/healthy",
- "port": port,
- },
- "initialDelaySeconds": 30,
- "periodSeconds": 30,
- }
-
-
-def _make_pod_command(config: Dict[str, Any], port: int) -> List[str]:
- """Generate the startup command.
-
- Args:
- config (Dict[str, Any]): Configuration information.
- port (int): port.
-
- Returns:
- List[str]: command to startup the process.
- """
- command = [
- "/bin/prometheus",
- "--config.file=/etc/prometheus/prometheus.yml",
- "--storage.tsdb.path=/prometheus",
- "--web.console.libraries=/usr/share/prometheus/console_libraries",
- "--web.console.templates=/usr/share/prometheus/consoles",
- "--web.route-prefix={}".format(config.get("web_subpath")),
- "--web.external-url=http://localhost:{}{}".format(
- port, config.get("web_subpath")
- ),
- ]
- if config.get("enable_web_admin_api"):
- command.append("--web.enable-admin-api")
- return command
-
-
-def make_pod_spec(
- image_info: Dict[str, str],
- config: Dict[str, Any],
- relation_state: Dict[str, Any],
- app_name: str = "prometheus",
- port: int = 9090,
-) -> Dict[str, Any]:
- """Generate the pod spec information.
-
- Args:
- image_info (Dict[str, str]): Object provided by
- OCIImageResource("image").fetch().
- config (Dict[str, Any]): Configuration information.
- relation_state (Dict[str, Any]): Relation state information.
- app_name (str, optional): Application name. Defaults to "ro".
- port (int, optional): Port for the container. Defaults to 9090.
-
- Returns:
- Dict[str, Any]: Pod spec dictionary for the charm.
- """
- if not image_info:
- return None
-
- _validate_data(config, relation_state)
-
- ports = _make_pod_ports(port)
- env_config = _make_pod_envconfig(config, relation_state)
- files = _make_pod_files(config)
- readiness_probe = _make_readiness_probe(port)
- liveness_probe = _make_liveness_probe(port)
- ingress_resources = _make_pod_ingress_resources(config, app_name, port)
- command = _make_pod_command(config, port)
-
- return {
- "version": 3,
- "containers": [
- {
- "name": app_name,
- "imageDetails": image_info,
- "imagePullPolicy": "Always",
- "ports": ports,
- "envConfig": env_config,
- "volumeConfig": files,
- "command": command,
- "kubernetes": {
- "readinessProbe": readiness_probe,
- "livenessProbe": liveness_probe,
- },
- }
- ],
- "kubernetesResources": {
- "ingressResources": ingress_resources or [],
- },
- }
+++ /dev/null
-#!/usr/bin/env python3
-# Copyright 2020 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-"""Init mocking for unit tests."""
-
-import sys
-
-
-import mock
-
-
-class OCIImageResourceErrorMock(Exception):
- pass
-
-
-sys.path.append("src")
-
-oci_image = mock.MagicMock()
-oci_image.OCIImageResourceError = OCIImageResourceErrorMock
-sys.modules["oci_image"] = oci_image
-sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
+++ /dev/null
-#!/usr/bin/env python3
-# Copyright 2020 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-import sys
-from typing import NoReturn
-import unittest
-
-from charm import PrometheusCharm
-from ops.model import ActiveStatus
-from ops.testing import Harness
-
-
-class TestCharm(unittest.TestCase):
- """Prometheus Charm unit tests."""
-
- def setUp(self) -> NoReturn:
- """Test setup"""
- self.image_info = sys.modules["oci_image"].OCIImageResource().fetch()
- self.harness = Harness(PrometheusCharm)
- self.harness.set_leader(is_leader=True)
- self.harness.begin()
- self.config = {
- "web-subpath": "/",
- "default-target": "",
- "max_file_size": 0,
- "ingress_whitelist_source_range": "",
- "tls_secret_name": "",
- "site_url": "https://prometheus.192.168.100.100.nip.io",
- "cluster_issuer": "vault-issuer",
- "enable_web_admin_api": False,
- "web_config_username": "admin",
- "web_config_password": "1234",
- }
- self.harness.update_config(self.config)
-
- def test_config_changed(
- self,
- ) -> NoReturn:
- """Test ingress resources without HTTP."""
-
- self.harness.charm.on.config_changed.emit()
-
- # Assertions
- self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus)
-
- def test_config_changed_non_leader(
- self,
- ) -> NoReturn:
- """Test ingress resources without HTTP."""
- self.harness.set_leader(is_leader=False)
- self.harness.charm.on.config_changed.emit()
-
- # Assertions
- self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus)
-
- def test_publish_prometheus_info(
- self,
- ) -> NoReturn:
- """Test to see if prometheus relation is updated."""
- expected_result = {
- "hostname": self.harness.charm.app.name,
- "port": "9090",
- "user": "admin",
- "password": "1234",
- }
-
- relation_id = self.harness.add_relation("prometheus", "mon")
- self.harness.add_relation_unit(relation_id, "mon/0")
- relation_data = self.harness.get_relation_data(
- relation_id, self.harness.charm.app.name
- )
-
- self.assertDictEqual(expected_result, relation_data)
-
- def test_publish_prometheus_info_non_leader(
- self,
- ) -> NoReturn:
- """Test to see if prometheus relation is updated."""
- expected_result = {}
-
- self.harness.set_leader(is_leader=False)
- relation_id = self.harness.add_relation("prometheus", "mon")
- self.harness.add_relation_unit(relation_id, "mon/0")
- relation_data = self.harness.get_relation_data(
- relation_id, self.harness.charm.app.name
- )
-
- self.assertDictEqual(expected_result, relation_data)
-
-
-if __name__ == "__main__":
- unittest.main()
+++ /dev/null
-#!/usr/bin/env python3
-# Copyright 2020 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-from typing import NoReturn
-import unittest
-
-import pod_spec
-
-
-class TestPodSpec(unittest.TestCase):
- """Pod spec unit tests."""
-
- def test_make_pod_ports(self) -> NoReturn:
- """Testing make pod ports."""
- port = 9090
-
- expected_result = [
- {
- "name": "prometheus",
- "containerPort": port,
- "protocol": "TCP",
- }
- ]
-
- pod_ports = pod_spec._make_pod_ports(port)
-
- self.assertListEqual(expected_result, pod_ports)
-
- def test_make_pod_envconfig(self) -> NoReturn:
- """Testing make pod envconfig."""
- config = {}
- relation_state = {}
-
- expected_result = {}
-
- pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state)
-
- self.assertDictEqual(expected_result, pod_envconfig)
-
- def test_make_pod_ingress_resources_without_site_url(self) -> NoReturn:
- """Testing make pod ingress resources without site_url."""
- config = {"site_url": ""}
- app_name = "prometheus"
- port = 9090
-
- pod_ingress_resources = pod_spec._make_pod_ingress_resources(
- config, app_name, port
- )
-
- self.assertIsNone(pod_ingress_resources)
-
- def test_make_pod_ingress_resources(self) -> NoReturn:
- """Testing make pod ingress resources."""
- config = {
- "site_url": "http://prometheus",
- "max_file_size": 0,
- "ingress_whitelist_source_range": "",
- }
- app_name = "prometheus"
- port = 9090
-
- expected_result = [
- {
- "name": f"{app_name}-ingress",
- "annotations": {
- "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
- "nginx.ingress.kubernetes.io/ssl-redirect": "false",
- },
- "spec": {
- "rules": [
- {
- "host": app_name,
- "http": {
- "paths": [
- {
- "path": "/",
- "backend": {
- "serviceName": app_name,
- "servicePort": port,
- },
- }
- ]
- },
- }
- ]
- },
- }
- ]
-
- pod_ingress_resources = pod_spec._make_pod_ingress_resources(
- config, app_name, port
- )
-
- self.assertListEqual(expected_result, pod_ingress_resources)
-
- def test_make_pod_ingress_resources_with_whitelist_source_range(self) -> NoReturn:
- """Testing make pod ingress resources with whitelist_source_range."""
- config = {
- "site_url": "http://prometheus",
- "max_file_size": 0,
- "ingress_whitelist_source_range": "0.0.0.0/0",
- }
- app_name = "prometheus"
- port = 9090
-
- expected_result = [
- {
- "name": f"{app_name}-ingress",
- "annotations": {
- "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
- "nginx.ingress.kubernetes.io/ssl-redirect": "false",
- "nginx.ingress.kubernetes.io/whitelist-source-range": config[
- "ingress_whitelist_source_range"
- ],
- },
- "spec": {
- "rules": [
- {
- "host": app_name,
- "http": {
- "paths": [
- {
- "path": "/",
- "backend": {
- "serviceName": app_name,
- "servicePort": port,
- },
- }
- ]
- },
- }
- ]
- },
- }
- ]
-
- pod_ingress_resources = pod_spec._make_pod_ingress_resources(
- config, app_name, port
- )
-
- self.assertListEqual(expected_result, pod_ingress_resources)
-
- def test_make_pod_ingress_resources_with_https(self) -> NoReturn:
- """Testing make pod ingress resources with HTTPs."""
- config = {
- "site_url": "https://prometheus",
- "max_file_size": 0,
- "ingress_whitelist_source_range": "",
- "tls_secret_name": "",
- }
- app_name = "prometheus"
- port = 9090
-
- expected_result = [
- {
- "name": f"{app_name}-ingress",
- "annotations": {
- "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
- },
- "spec": {
- "rules": [
- {
- "host": app_name,
- "http": {
- "paths": [
- {
- "path": "/",
- "backend": {
- "serviceName": app_name,
- "servicePort": port,
- },
- }
- ]
- },
- }
- ],
- "tls": [{"hosts": [app_name]}],
- },
- }
- ]
-
- pod_ingress_resources = pod_spec._make_pod_ingress_resources(
- config, app_name, port
- )
-
- self.assertListEqual(expected_result, pod_ingress_resources)
-
- def test_make_pod_ingress_resources_with_https_tls_secret_name(self) -> NoReturn:
- """Testing make pod ingress resources with HTTPs and TLS secret name."""
- config = {
- "site_url": "https://prometheus",
- "max_file_size": 0,
- "ingress_whitelist_source_range": "",
- "tls_secret_name": "secret_name",
- }
- app_name = "prometheus"
- port = 9090
-
- expected_result = [
- {
- "name": f"{app_name}-ingress",
- "annotations": {
- "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
- },
- "spec": {
- "rules": [
- {
- "host": app_name,
- "http": {
- "paths": [
- {
- "path": "/",
- "backend": {
- "serviceName": app_name,
- "servicePort": port,
- },
- }
- ]
- },
- }
- ],
- "tls": [
- {"hosts": [app_name], "secretName": config["tls_secret_name"]}
- ],
- },
- }
- ]
-
- pod_ingress_resources = pod_spec._make_pod_ingress_resources(
- config, app_name, port
- )
-
- self.assertListEqual(expected_result, pod_ingress_resources)
-
- def test_make_pod_files(self) -> NoReturn:
- """Testing make pod files."""
- config = {
- "web_subpath": "/",
- "default_target": "",
- "site_url": "",
- }
-
- expected_result = [
- {
- "name": "config",
- "mountPath": "/etc/prometheus",
- "files": [
- {
- "path": "prometheus.yml",
- "content": (
- "global:\n"
- " scrape_interval: 15s\n"
- " evaluation_interval: 15s\n"
- "alerting:\n"
- " alertmanagers:\n"
- " - static_configs:\n"
- " - targets:\n"
- "rule_files:\n"
- "scrape_configs:\n"
- " - job_name: 'prometheus'\n"
- " static_configs:\n"
- " - targets: [{}]\n".format(config["default_target"])
- ),
- }
- ],
- }
- ]
-
- pod_envconfig = pod_spec._make_pod_files(config)
- self.assertListEqual(expected_result, pod_envconfig)
-
- def test_make_readiness_probe(self) -> NoReturn:
- """Testing make readiness probe."""
- port = 9090
-
- expected_result = {
- "httpGet": {
- "path": "/-/ready",
- "port": port,
- },
- "initialDelaySeconds": 10,
- "timeoutSeconds": 30,
- }
-
- readiness_probe = pod_spec._make_readiness_probe(port)
-
- self.assertDictEqual(expected_result, readiness_probe)
-
- def test_make_liveness_probe(self) -> NoReturn:
- """Testing make liveness probe."""
- port = 9090
-
- expected_result = {
- "httpGet": {
- "path": "/-/healthy",
- "port": port,
- },
- "initialDelaySeconds": 30,
- "periodSeconds": 30,
- }
-
- liveness_probe = pod_spec._make_liveness_probe(port)
-
- self.assertDictEqual(expected_result, liveness_probe)
-
- def test_make_pod_command(self) -> NoReturn:
- """Testing make pod command."""
- port = 9090
- config = {
- "web_subpath": "/",
- "default_target": "",
- "site_url": "",
- }
-
- expected_result = [
- "/bin/prometheus",
- "--config.file=/etc/prometheus/prometheus.yml",
- "--storage.tsdb.path=/prometheus",
- "--web.console.libraries=/usr/share/prometheus/console_libraries",
- "--web.console.templates=/usr/share/prometheus/consoles",
- "--web.route-prefix={}".format(config.get("web_subpath")),
- "--web.external-url=http://localhost:{}{}".format(
- port, config.get("web_subpath")
- ),
- ]
-
- pod_envconfig = pod_spec._make_pod_command(config, port)
-
- self.assertListEqual(expected_result, pod_envconfig)
-
- def test_make_pod_command_with_web_admin_api_enabled(self) -> NoReturn:
- """Testing make pod command."""
- port = 9090
- config = {
- "web_subpath": "/",
- "default_target": "",
- "site_url": "",
- "enable_web_admin_api": True,
- }
-
- expected_result = [
- "/bin/prometheus",
- "--config.file=/etc/prometheus/prometheus.yml",
- "--storage.tsdb.path=/prometheus",
- "--web.console.libraries=/usr/share/prometheus/console_libraries",
- "--web.console.templates=/usr/share/prometheus/consoles",
- "--web.route-prefix={}".format(config.get("web_subpath")),
- "--web.external-url=http://localhost:{}{}".format(
- port, config.get("web_subpath")
- ),
- "--web.enable-admin-api",
- ]
-
- pod_envconfig = pod_spec._make_pod_command(config, port)
-
- self.assertListEqual(expected_result, pod_envconfig)
-
- def test_make_pod_spec(self) -> NoReturn:
- """Testing make pod spec."""
- image_info = {"upstream-source": "ubuntu/prometheus:latest"}
- config = {
- "web_subpath": "/",
- "default_target": "",
- "site_url": "",
- "enable_web_admin_api": False,
- }
- relation_state = {}
- app_name = "prometheus"
- port = 9090
-
- expected_result = {
- "version": 3,
- "containers": [
- {
- "name": app_name,
- "imageDetails": image_info,
- "imagePullPolicy": "Always",
- "ports": [
- {
- "name": app_name,
- "containerPort": port,
- "protocol": "TCP",
- }
- ],
- "envConfig": {},
- "volumeConfig": [
- {
- "name": "config",
- "mountPath": "/etc/prometheus",
- "files": [
- {
- "path": "prometheus.yml",
- "content": (
- "global:\n"
- " scrape_interval: 15s\n"
- " evaluation_interval: 15s\n"
- "alerting:\n"
- " alertmanagers:\n"
- " - static_configs:\n"
- " - targets:\n"
- "rule_files:\n"
- "scrape_configs:\n"
- " - job_name: 'prometheus'\n"
- " static_configs:\n"
- " - targets: [{}]\n".format(
- config.get("default_target")
- )
- ),
- }
- ],
- }
- ],
- "command": [
- "/bin/prometheus",
- "--config.file=/etc/prometheus/prometheus.yml",
- "--storage.tsdb.path=/prometheus",
- "--web.console.libraries=/usr/share/prometheus/console_libraries",
- "--web.console.templates=/usr/share/prometheus/consoles",
- "--web.route-prefix={}".format(config.get("web_subpath")),
- "--web.external-url=http://localhost:{}{}".format(
- port, config.get("web_subpath")
- ),
- ],
- "kubernetes": {
- "readinessProbe": {
- "httpGet": {
- "path": "/-/ready",
- "port": port,
- },
- "initialDelaySeconds": 10,
- "timeoutSeconds": 30,
- },
- "livenessProbe": {
- "httpGet": {
- "path": "/-/healthy",
- "port": port,
- },
- "initialDelaySeconds": 30,
- "periodSeconds": 30,
- },
- },
- }
- ],
- "kubernetesResources": {"ingressResources": []},
- }
-
- spec = pod_spec.make_pod_spec(
- image_info, config, relation_state, app_name, port
- )
-
- self.assertDictEqual(expected_result, spec)
-
- def test_make_pod_spec_with_ingress(self) -> NoReturn:
- """Testing make pod spec."""
- image_info = {"upstream-source": "ubuntu/prometheus:latest"}
- config = {
- "web_subpath": "/",
- "default_target": "",
- "site_url": "https://prometheus",
- "tls_secret_name": "prometheus",
- "max_file_size": 0,
- "ingress_whitelist_source_range": "0.0.0.0/0",
- "enable_web_admin_api": False,
- }
- relation_state = {}
- app_name = "prometheus"
- port = 9090
-
- expected_result = {
- "version": 3,
- "containers": [
- {
- "name": app_name,
- "imageDetails": image_info,
- "imagePullPolicy": "Always",
- "ports": [
- {
- "name": app_name,
- "containerPort": port,
- "protocol": "TCP",
- }
- ],
- "envConfig": {},
- "volumeConfig": [
- {
- "name": "config",
- "mountPath": "/etc/prometheus",
- "files": [
- {
- "path": "prometheus.yml",
- "content": (
- "global:\n"
- " scrape_interval: 15s\n"
- " evaluation_interval: 15s\n"
- "alerting:\n"
- " alertmanagers:\n"
- " - static_configs:\n"
- " - targets:\n"
- "rule_files:\n"
- "scrape_configs:\n"
- " - job_name: 'prometheus'\n"
- " static_configs:\n"
- " - targets: [{}]\n".format(
- config.get("default_target")
- )
- ),
- }
- ],
- }
- ],
- "command": [
- "/bin/prometheus",
- "--config.file=/etc/prometheus/prometheus.yml",
- "--storage.tsdb.path=/prometheus",
- "--web.console.libraries=/usr/share/prometheus/console_libraries",
- "--web.console.templates=/usr/share/prometheus/consoles",
- "--web.route-prefix={}".format(config.get("web_subpath")),
- "--web.external-url=http://localhost:{}{}".format(
- port, config.get("web_subpath")
- ),
- ],
- "kubernetes": {
- "readinessProbe": {
- "httpGet": {
- "path": "/-/ready",
- "port": port,
- },
- "initialDelaySeconds": 10,
- "timeoutSeconds": 30,
- },
- "livenessProbe": {
- "httpGet": {
- "path": "/-/healthy",
- "port": port,
- },
- "initialDelaySeconds": 30,
- "periodSeconds": 30,
- },
- },
- }
- ],
- "kubernetesResources": {
- "ingressResources": [
- {
- "name": "{}-ingress".format(app_name),
- "annotations": {
- "nginx.ingress.kubernetes.io/proxy-body-size": str(
- config.get("max_file_size")
- ),
- "nginx.ingress.kubernetes.io/whitelist-source-range": config.get(
- "ingress_whitelist_source_range"
- ),
- },
- "spec": {
- "rules": [
- {
- "host": app_name,
- "http": {
- "paths": [
- {
- "path": "/",
- "backend": {
- "serviceName": app_name,
- "servicePort": port,
- },
- }
- ]
- },
- }
- ],
- "tls": [
- {
- "hosts": [app_name],
- "secretName": config.get("tls_secret_name"),
- }
- ],
- },
- }
- ],
- },
- }
-
- spec = pod_spec.make_pod_spec(
- image_info, config, relation_state, app_name, port
- )
-
- self.assertDictEqual(expected_result, spec)
-
- def test_make_pod_spec_without_image_info(self) -> NoReturn:
- """Testing make pod spec without image_info."""
- image_info = None
- config = {
- "web_subpath": "/",
- "default_target": "",
- "site_url": "",
- "enable_web_admin_api": False,
- }
- relation_state = {}
- app_name = "prometheus"
- port = 9090
-
- spec = pod_spec.make_pod_spec(
- image_info, config, relation_state, app_name, port
- )
-
- self.assertIsNone(spec)
-
- def test_make_pod_spec_without_config(self) -> NoReturn:
- """Testing make pod spec without config."""
- image_info = {"upstream-source": "ubuntu/prometheus:latest"}
- config = {}
- relation_state = {}
- app_name = "prometheus"
- port = 9090
-
- with self.assertRaises(ValueError):
- pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port)
-
-
-if __name__ == "__main__":
- unittest.main()
+++ /dev/null
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-#######################################################################################
-
-[tox]
-envlist = black, cover, flake8, pylint, yamllint, safety
-skipsdist = true
-
-[tox:jenkins]
-toxworkdir = /tmp/.tox
-
-[testenv]
-basepython = python3.8
-setenv = VIRTUAL_ENV={envdir}
- PYTHONDONTWRITEBYTECODE = 1
-deps = -r{toxinidir}/requirements.txt
-
-
-#######################################################################################
-[testenv:black]
-deps = black
-commands =
- black --check --diff src/ tests/
-
-
-#######################################################################################
-[testenv:cover]
-deps = {[testenv]deps}
- -r{toxinidir}/requirements-test.txt
- coverage
- nose2
-commands =
- sh -c 'rm -f nosetests.xml'
- coverage erase
- nose2 -C --coverage src
- coverage report --omit='*tests*'
- coverage html -d ./cover --omit='*tests*'
- coverage xml -o coverage.xml --omit=*tests*
-whitelist_externals = sh
-
-
-#######################################################################################
-[testenv:flake8]
-deps = flake8
- flake8-import-order
-commands =
- flake8 src/ tests/
-
-
-#######################################################################################
-[testenv:pylint]
-deps = {[testenv]deps}
- -r{toxinidir}/requirements-test.txt
- pylint==2.10.2
-commands =
- pylint -E src/ tests/
-
-
-#######################################################################################
-[testenv:safety]
-setenv =
- LC_ALL=C.UTF-8
- LANG=C.UTF-8
-deps = {[testenv]deps}
- safety
-commands =
- - safety check --full-report
-
-
-#######################################################################################
-[testenv:yamllint]
-deps = {[testenv]deps}
- -r{toxinidir}/requirements-test.txt
- yamllint
-commands = yamllint .
-
-#######################################################################################
-[testenv:build]
-passenv=HTTP_PROXY HTTPS_PROXY NO_PROXY
-whitelist_externals =
- charmcraft
- sh
-commands =
- charmcraft pack
- sh -c 'ubuntu_version=20.04; \
- architectures="amd64-aarch64-arm64"; \
- charm_name=`cat metadata.yaml | grep -E "^name: " | cut -f 2 -d " "`; \
- mv $charm_name"_ubuntu-"$ubuntu_version-$architectures.charm $charm_name.charm'
-
-#######################################################################################
-[flake8]
-ignore =
- W291,
- W293,
- W503,
- E123,
- E125,
- E226,
- E241,
-exclude =
- .git,
- __pycache__,
- .tox,
-max-line-length = 120
-show-source = True
-builtins = _
-max-complexity = 10
-import-order-style = google