From a708cb7fb0b992918cfc424516d090646c003038 Mon Sep 17 00:00:00 2001 From: Simon Aronsson Date: Sun, 24 Mar 2024 22:03:54 +0100 Subject: [PATCH] integrations for ceph and migrating to the new charmcraft.yaml (#309) * add anonymous access and embedding config options * merge yaml files. see https://juju.is/docs/sdk/charmcraft-yaml * fix integration and unit tests * fix lint --- actions.yaml | 5 - charmcraft.yaml | 181 ++++++++++++++++++ config.yaml | 63 ------ metadata.yaml | 96 ---------- src/charm.py | 4 + tests/integration/test_external_url.py | 4 +- tests/integration/test_get_password.py | 6 +- tests/integration/test_grafana_auth.py | 4 +- tests/integration/test_grafana_dashboard.py | 4 +- tests/integration/test_grafana_oauth.py | 4 +- tests/integration/test_grafana_source.py | 4 +- tests/integration/test_kubectl_delete.py | 8 +- tests/integration/test_multiple_units.py | 4 +- tests/integration/test_resource_limits.py | 4 +- .../test_selfmonitoring_dashboard.py | 4 +- tests/integration/test_tls_web.py | 6 +- .../integration/test_trusted_certificates.py | 6 +- tests/integration/test_upgrade_charm.py | 4 +- tests/unit/test_dashboard_consumer.py | 2 +- tests/unit/test_dashboard_transform.py | 2 +- tests/unit/test_source_consumer.py | 2 +- 21 files changed, 219 insertions(+), 198 deletions(-) delete mode 100644 actions.yaml delete mode 100644 config.yaml delete mode 100644 metadata.yaml diff --git a/actions.yaml b/actions.yaml deleted file mode 100644 index 283ed11b..00000000 --- a/actions.yaml +++ /dev/null @@ -1,5 +0,0 @@ -get-admin-password: - description: | - Get the dashboard url and initial admin password for the Grafana web interface. Initial - admin password is generated at charm deployment time. If the password has been changed, - a notice of that fact will be returned by this action instead. diff --git a/charmcraft.yaml b/charmcraft.yaml index a29c7bc6..6ac7a972 100644 --- a/charmcraft.yaml +++ b/charmcraft.yaml @@ -1,6 +1,103 @@ # Copyright 2021 Canonical Ltd. # See LICENSE file for licensing details. + type: charm +name: grafana-k8s +summary: Data visualization and observability with Grafana +description: | + Grafana provides dashboards for monitoring data and this + charm is written to allow for HA on Kubernetes and can take + multiple data sources (for example, Prometheus). + +links: + documentation: https://discourse.charmhub.io/t/grafana-operator-k8s-docs-index/5612 + website: + - https://charmhub.io/grafana-k8s + source: + - https://github.com/canonical/grafana-k8s-operator + issues: + - https://github.com/canonical/grafana-k8s-operator/issues + +assumes: + - k8s-api + - juju >= 3.0.3 # Juju 3.0.3+ needed for secrets and open-port + +containers: + grafana: + resource: grafana-image + mounts: + - storage: database + location: /var/lib/grafana + litestream: + resource: litestream-image + mounts: + - storage: database + location: /var/lib/grafana + +storage: + database: + type: filesystem + +requires: + grafana-source: + interface: grafana_datasource + grafana-dashboard: + interface: grafana_dashboard + grafana-auth: + interface: grafana_auth + database: + interface: db + limit: 1 + catalogue: + interface: catalogue + ingress: + interface: traefik_route + limit: 1 + description: Grafana needs "ingress per leader" (not per app and not per unit). + certificates: + interface: tls-certificates + limit: 1 + description: Certificate and key files for Grafana to use with TLS. + receive-ca-cert: + interface: certificate_transfer + description: | + Receive a CA cert for grafana to trust. + This relation can be used with a local CA to obtain the CA cert that was used to sign proxied + endpoints. + limit: 1 + oauth: + interface: oauth + limit: 1 + description: | + Receive oauth server's info and a set of client credentials. + This relation can be used to integrate grafana with an oAuth2/OIDC Provider. + tracing: + interface: tracing + limit: 1 + +provides: + metrics-endpoint: + interface: prometheus_scrape + description: Links to grafana's own `/metrics` endpoint. + +peers: + grafana: + interface: grafana_peers + replicas: + # Peer relation used as storage for TLS data + interface: grafana_replicas + +resources: + grafana-image: + type: oci-image + description: upstream docker image for Grafana + #upstream-source: ghcr.io/canonical/grafana:dev + upstream-source: docker.io/ubuntu/grafana:9-22.04 + litestream-image: + type: oci-image + description: upstream image for sqlite streaming + upstream-source: docker.io/litestream/litestream:0.4.0-beta.2 + bases: - build-on: - name: "ubuntu" @@ -8,6 +105,7 @@ bases: run-on: - name: "ubuntu" channel: "20.04" + parts: charm: # This is currently necessary because it cuts on packing times. @@ -31,3 +129,86 @@ parts: override-pull: | curl -L -O https://github.com/canonical/cos-tool/releases/latest/download/cos-tool-${CRAFT_TARGET_ARCH} chmod +x cos-tool-* + +actions: + get-admin-password: + description: | + Get the dashboard url and initial admin password for the Grafana web interface. Initial + admin password is generated at charm deployment time. If the password has been changed, + a notice of that fact will be returned by this action instead. + +config: + options: + allow_anonymous_access: + type: boolean + default: false + description: | + Whether Grafana should allow anonymous access to dashboards. Unless + you have some other authentication mechanism in front of your deployment, + you likely do not want to enable this. + allow_embedding: + type: boolean + default: false + description: | + Whether Grafana should allow embedding dashboards using iframes. Unless + you have a clear reason for doing so, you likely do not want to enable this. + log_level: + type: string + description: | + Logging level for Grafana. Options are “debug”, “info”, + “warn”, “error”, and “critical”. + default: info + admin_user: + description: The Grafana administrative user + type: string + default: admin + web_external_url: + description: | + DEPRECATED. This config option is no longer used, in favor of "skipPrefix". + + The URL under which Grafana is externally reachable (for example, + if Grafana is served via a reverse proxy). + + Used for generating relative and absolute links back to + Grafana itself. If the URL has a path portion, it will be used to + prefix all HTTP endpoints served by Grafana. + + If omitted, relevant URL components will be derived automatically. + + If provided, this should be a complete URI, including scheme, or a + fully qualified subpath starting with `/`. + + If Grafana is being served directly from the root of a fully-qualified + host or a bare A record, this may be omitted. + type: string + default: "" + enable_auto_assign_org: + description: | + Set to true to automatically add new users to the main organization (id 1). When set to + false, new users automatically cause a new organization to be created for that new user. + type: boolean + default: true + datasource_query_timeout: + description: | + The default timeout for querying a Grafana datasource. Each datasource can + also configure its own preferred timeout value through relation data. If the + value configured through relation data is larger than datasource_query_timeout + then that value is left unchanged. The value of this configuration option must + be a positive integer representing the maximum number of seconds Grafana will + wait for a datasource to respond to a query. + type: int + default: 300 + cpu: + description: | + K8s cpu resource limit, e.g. "1" or "500m". Default is unset (no limit). This value is used + for the "limits" portion of the resource requirements (the "requests" portion is + automatically deduced from it). + See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: string + memory: + description: | + K8s memory resource limit, e.g. "1Gi". Default is unset (no limit). This value is used + for the "limits" portion of the resource requirements (the "requests" portion is + automatically deduced from it). + See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: string \ No newline at end of file diff --git a/config.yaml b/config.yaml deleted file mode 100644 index 7de510d9..00000000 --- a/config.yaml +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2021 Canonical Ltd. -# See LICENSE file for licensing details. -options: - log_level: - type: string - description: | - Logging level for Grafana. Options are “debug”, “info”, - “warn”, “error”, and “critical”. - default: info - admin_user: - description: The Grafana administrative user - type: string - default: admin - web_external_url: - description: | - DEPRECATED. This config option is no longer used, in favor of "skipPrefix". - - The URL under which Grafana is externally reachable (for example, - if Grafana is served via a reverse proxy). - - Used for generating relative and absolute links back to - Grafana itself. If the URL has a path portion, it will be used to - prefix all HTTP endpoints served by Grafana. - - If omitted, relevant URL components will be derived automatically. - - If provided, this should be a complete URI, including scheme, or a - fully qualified subpath starting with `/`. - - If Grafana is being served directly from the root of a fully-qualified - host or a bare A record, this may be omitted. - type: string - default: "" - enable_auto_assign_org: - description: | - Set to true to automatically add new users to the main organization (id 1). When set to - false, new users automatically cause a new organization to be created for that new user. - type: boolean - default: true - datasource_query_timeout: - description: | - The default timeout for querying a Grafana datasource. Each datasource can - also configure its own preferred timeout value through relation data. If the - value configured through relation data is larger than datasource_query_timeout - then that value is left unchanged. The value of this configuration option must - be a positive integer representing the maximum number of seconds Grafana will - wait for a datasource to respond to a query. - type: int - default: 300 - cpu: - description: | - K8s cpu resource limit, e.g. "1" or "500m". Default is unset (no limit). This value is used - for the "limits" portion of the resource requirements (the "requests" portion is - automatically deduced from it). - See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: string - memory: - description: | - K8s memory resource limit, e.g. "1Gi". Default is unset (no limit). This value is used - for the "limits" portion of the resource requirements (the "requests" portion is - automatically deduced from it). - See https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - type: string diff --git a/metadata.yaml b/metadata.yaml deleted file mode 100644 index 5c97393a..00000000 --- a/metadata.yaml +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright 2021 Canonical Ltd. -# See LICENSE file for licensing details. -name: grafana-k8s - -assumes: - - k8s-api - - # Juju 3.0.3+ needed for secrets and open-port - - juju >= 3.0.3 - -summary: Data visualization and observability with Grafana - -website: https://charmhub.io/grafana-k8s -source: https://github.com/canonical/grafana-k8s-operator -issues: https://github.com/canonical/grafana-k8s-operator/issues -docs: https://discourse.charmhub.io/t/grafana-operator-k8s-docs-index/5612 - -description: | - Grafana provides dashboards for monitoring data and this - charm is written to allow for HA on Kubernetes and can take - multiple data sources (for example, Prometheus). - -containers: - grafana: - resource: grafana-image - mounts: - - storage: database - location: /var/lib/grafana - litestream: - resource: litestream-image - mounts: - - storage: database - location: /var/lib/grafana -storage: - database: - type: filesystem - -requires: - grafana-source: - interface: grafana_datasource - grafana-dashboard: - interface: grafana_dashboard - grafana-auth: - interface: grafana_auth - database: - interface: db - limit: 1 - catalogue: - interface: catalogue - ingress: - interface: traefik_route - limit: 1 - description: Grafana needs "ingress per leader" (not per app and not per unit). - certificates: - interface: tls-certificates - limit: 1 - description: Certificate and key files for Grafana to use with TLS. - receive-ca-cert: - interface: certificate_transfer - description: | - Receive a CA cert for grafana to trust. - This relation can be used with a local CA to obtain the CA cert that was used to sign proxied - endpoints. - limit: 1 - oauth: - interface: oauth - limit: 1 - description: | - Receive oauth server's info and a set of client credentials. - This relation can be used to integrate grafana with an oAuth2/OIDC Provider. - tracing: - interface: tracing - limit: 1 - -provides: - metrics-endpoint: - interface: prometheus_scrape - description: Links to grafana's own `/metrics` endpoint. - -peers: - grafana: - interface: grafana_peers - # Peer relation used as storage for TLS data - replicas: - interface: grafana_replicas - -resources: - grafana-image: - type: oci-image - description: upstream docker image for Grafana - #upstream-source: ghcr.io/canonical/grafana:dev - upstream-source: docker.io/ubuntu/grafana:9-22.04 - litestream-image: - type: oci-image - description: upstream image for sqlite streaming - upstream-source: docker.io/litestream/litestream:0.4.0-beta.2 diff --git a/src/charm.py b/src/charm.py index 3d5cd865..cfed7b83 100755 --- a/src/charm.py +++ b/src/charm.py @@ -997,8 +997,12 @@ def _build_layer(self) -> Layer: "GF_LOG_LEVEL": self.model.config["log_level"], "GF_PLUGINS_ENABLE_ALPHA": "true", "GF_PATHS_PROVISIONING": PROVISIONING_PATH, + "GF_SECURITY_ALLOW_EMBEDDING": self.model.config["allow_embedding"], "GF_SECURITY_ADMIN_USER": self.model.config["admin_user"], "GF_SECURITY_ADMIN_PASSWORD": self._get_admin_password(), + "GF_AUTH_ANONYMOUS_ENABLED": self.model.config[ + "allow_anonymous_access" + ], "GF_USERS_AUTO_ASSIGN_ORG": str( self.model.config["enable_auto_assign_org"] ), diff --git a/tests/integration/test_external_url.py b/tests/integration/test_external_url.py index 9c428bf0..a592a2cc 100644 --- a/tests/integration/test_external_url.py +++ b/tests/integration/test_external_url.py @@ -13,8 +13,8 @@ logger = logging.getLogger(__name__) grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } grafana_app_name = "grafana" diff --git a/tests/integration/test_get_password.py b/tests/integration/test_get_password.py index 35ed5ff0..4c96a575 100644 --- a/tests/integration/test_get_password.py +++ b/tests/integration/test_get_password.py @@ -11,11 +11,11 @@ logger = logging.getLogger(__name__) -METADATA = yaml.safe_load(Path("./metadata.yaml").read_text()) +METADATA = yaml.safe_load(Path("./charmcraft.yaml").read_text()) app_name = "grafana" grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } diff --git a/tests/integration/test_grafana_auth.py b/tests/integration/test_grafana_auth.py index fab8ec63..ee05b571 100644 --- a/tests/integration/test_grafana_auth.py +++ b/tests/integration/test_grafana_auth.py @@ -21,8 +21,8 @@ ) } grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } diff --git a/tests/integration/test_grafana_dashboard.py b/tests/integration/test_grafana_dashboard.py index b6780697..2489730a 100644 --- a/tests/integration/test_grafana_dashboard.py +++ b/tests/integration/test_grafana_dashboard.py @@ -21,8 +21,8 @@ ) } grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } grafana_app_name = "grafana" tester_app_name = "grafana-tester" diff --git a/tests/integration/test_grafana_oauth.py b/tests/integration/test_grafana_oauth.py index 4a0c4d77..ed289aee 100644 --- a/tests/integration/test_grafana_oauth.py +++ b/tests/integration/test_grafana_oauth.py @@ -38,8 +38,8 @@ ) } grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } diff --git a/tests/integration/test_grafana_source.py b/tests/integration/test_grafana_source.py index f3466ffe..bb0dec9b 100644 --- a/tests/integration/test_grafana_source.py +++ b/tests/integration/test_grafana_source.py @@ -21,8 +21,8 @@ ) } grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } diff --git a/tests/integration/test_kubectl_delete.py b/tests/integration/test_kubectl_delete.py index faf179cf..45bd8a21 100644 --- a/tests/integration/test_kubectl_delete.py +++ b/tests/integration/test_kubectl_delete.py @@ -21,15 +21,15 @@ grafana_app_name = "grafana" tester_app_name = "grafana-tester" config = {"log_level": "error", "datasource_query_timeout": "600"} -grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), -} tester_resources = { "grafana-tester-image": oci_image( "./tests/integration/grafana-tester/metadata.yaml", "grafana-tester-image" ) } +grafana_resources = { + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), +} @pytest.mark.abort_on_fail diff --git a/tests/integration/test_multiple_units.py b/tests/integration/test_multiple_units.py index b684c5a4..44f802c9 100644 --- a/tests/integration/test_multiple_units.py +++ b/tests/integration/test_multiple_units.py @@ -24,8 +24,8 @@ ) } grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } diff --git a/tests/integration/test_resource_limits.py b/tests/integration/test_resource_limits.py index 501a711c..752a9116 100644 --- a/tests/integration/test_resource_limits.py +++ b/tests/integration/test_resource_limits.py @@ -14,8 +14,8 @@ app_name = "grafana" grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } diff --git a/tests/integration/test_selfmonitoring_dashboard.py b/tests/integration/test_selfmonitoring_dashboard.py index 1bced86c..ea170af5 100644 --- a/tests/integration/test_selfmonitoring_dashboard.py +++ b/tests/integration/test_selfmonitoring_dashboard.py @@ -22,8 +22,8 @@ ) } grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } grafana_app_name = "grafana" prometheus_app_name = "prometheus" diff --git a/tests/integration/test_tls_web.py b/tests/integration/test_tls_web.py index 08f635d4..c469a0aa 100644 --- a/tests/integration/test_tls_web.py +++ b/tests/integration/test_tls_web.py @@ -14,11 +14,11 @@ logger = logging.getLogger(__name__) -METADATA = yaml.safe_load(Path("./metadata.yaml").read_text()) +METADATA = yaml.safe_load(Path("./charmcraft.yaml").read_text()) grafana = SimpleNamespace(name="grafana", scale=2) grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } diff --git a/tests/integration/test_trusted_certificates.py b/tests/integration/test_trusted_certificates.py index 6ec3fefd..a2d0b537 100644 --- a/tests/integration/test_trusted_certificates.py +++ b/tests/integration/test_trusted_certificates.py @@ -12,10 +12,10 @@ logger = logging.getLogger(__name__) -METADATA = yaml.safe_load(Path("./metadata.yaml").read_text()) +METADATA = yaml.safe_load(Path("./charmcraft.yaml").read_text()) grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } diff --git a/tests/integration/test_upgrade_charm.py b/tests/integration/test_upgrade_charm.py index 9e5174cb..a2718730 100644 --- a/tests/integration/test_upgrade_charm.py +++ b/tests/integration/test_upgrade_charm.py @@ -12,8 +12,8 @@ app_name = "grafana-k8s" grafana_resources = { - "grafana-image": oci_image("./metadata.yaml", "grafana-image"), - "litestream-image": oci_image("./metadata.yaml", "litestream-image"), + "grafana-image": oci_image("./charmcraft.yaml", "grafana-image"), + "litestream-image": oci_image("./charmcraft.yaml", "litestream-image"), } diff --git a/tests/unit/test_dashboard_consumer.py b/tests/unit/test_dashboard_consumer.py index 802ff92d..a3e11114 100644 --- a/tests/unit/test_dashboard_consumer.py +++ b/tests/unit/test_dashboard_consumer.py @@ -440,7 +440,7 @@ def peers(self): @patch.object(base64, "b64decode", new=lambda x: x) class TestDashboardConsumer(unittest.TestCase): def setUp(self): - meta = open("metadata.yaml") + meta = open("charmcraft.yaml") self.harness = Harness(ConsumerCharm, meta=meta) self.harness.set_model_info(name=MODEL_INFO["name"], uuid=MODEL_INFO["uuid"]) self.addCleanup(self.harness.cleanup) diff --git a/tests/unit/test_dashboard_transform.py b/tests/unit/test_dashboard_transform.py index 21ce2ace..2943ef88 100644 --- a/tests/unit/test_dashboard_transform.py +++ b/tests/unit/test_dashboard_transform.py @@ -311,7 +311,7 @@ def peers(self): @patch.object(base64, "b64decode", new=lambda x: x) class TestDashboardLabelInjector(unittest.TestCase): def setUp(self): - meta = open("metadata.yaml") + meta = open("charmcraft.yaml") self.harness = Harness(ConsumerCharm, meta=meta) self.harness.set_model_info(name=MODEL_INFO["name"], uuid=MODEL_INFO["uuid"]) self.addCleanup(self.harness.cleanup) diff --git a/tests/unit/test_source_consumer.py b/tests/unit/test_source_consumer.py index 00c45670..3cc2e3d0 100644 --- a/tests/unit/test_source_consumer.py +++ b/tests/unit/test_source_consumer.py @@ -72,7 +72,7 @@ def inject_fixtures(self, caplog): self._caplog = caplog def setUp(self): - meta = open("metadata.yaml") + meta = open("charmcraft.yaml") self.harness = Harness(GrafanaCharm, meta=meta) self.addCleanup(self.harness.cleanup) self.harness.set_leader(True)