diff --git a/.github/prompts/review-docs.prompt.md b/.github/prompts/review-docs.prompt.md index be37ec32..daa908c2 100644 --- a/.github/prompts/review-docs.prompt.md +++ b/.github/prompts/review-docs.prompt.md @@ -13,8 +13,10 @@ Review the documentation for clarity, completeness, and accuracy. - H1 titles under `docs/how-to` should start with "How to". - Section headers and index entries should all use sentence case (not title case). - Known product names should be capitalized consistently throughout the documentation. +- Spelling according to US English conventions. - Changes in docs should be reflected in the glossary, if the changes are related to terms/concepts that are unique to COS or charmed observability (don't redefine all/general terms). +- DO NOT use prompt marks (e.g. $ or #) in code samples. ## Context diff --git a/docs/.custom_wordlist.txt b/docs/.custom_wordlist.txt index fc4de209..288174c3 100644 --- a/docs/.custom_wordlist.txt +++ b/docs/.custom_wordlist.txt @@ -172,6 +172,7 @@ SHA sitemapindex SLAs Snapcraft +snap_microk8s SNMP Sphinx Spread diff --git a/docs/assets/telemetry-flow-1.jpeg b/docs/assets/telemetry-flow-1.jpeg index dcf06088..9089216b 100644 Binary files a/docs/assets/telemetry-flow-1.jpeg and b/docs/assets/telemetry-flow-1.jpeg differ diff --git a/docs/explanation/telemetry/logging-architecture.md b/docs/explanation/telemetry/logging-architecture.md index f6f990f8..79bda5be 100644 --- a/docs/explanation/telemetry/logging-architecture.md +++ b/docs/explanation/telemetry/logging-architecture.md @@ -89,7 +89,7 @@ relations: this results in an auto-render Promtail config file with three scrape jobs, one for each "filename": ```bash -$ juju ssh --container postgresql pgsql/0 cat /etc/promtail/promtail_config.yaml +juju ssh --container postgresql pgsql/0 cat /etc/promtail/promtail_config.yaml ``` ```yaml @@ -163,7 +163,7 @@ relations: This results in an auto-generated `/etc/otelcol/config.d/otelcol_0.yaml` config file with juju topology labels and the default scrape jobs for `/var/log/**/*log` and `journalctl`: ```bash -$ juju ssh otelcol/0 cat /etc/otelcol/config.d/otelcol_0.yaml +juju ssh otelcol/0 cat /etc/otelcol/config.d/otelcol_0.yaml ``` ```yaml diff --git a/docs/explanation/telemetry/telemetry-labels.md b/docs/explanation/telemetry/telemetry-labels.md index ab7822da..da32d273 100644 --- a/docs/explanation/telemetry/telemetry-labels.md +++ b/docs/explanation/telemetry/telemetry-labels.md @@ -22,9 +22,9 @@ By convention, applications expose labeled metrics under a [`/metrics` endpoint] For example, you can run the prometheus application and curl its `:9090/metrics` endpoint to obtain the metrics exposed by the process. ```bash -$ sudo snap install prometheus +sudo snap install prometheus -$ curl localhost:9090/metrics +curl localhost:9090/metrics # -- snip -- @@ -69,7 +69,7 @@ scrape_configs: Labels that are specified under a `static_configs` entry are automatically attached to all metrics scraped from the targets: ```bash -$ curl -s --data-urlencode 'match[]={__name__="prometheus_http_requests_total"}' localhost:9090/api/v1/series | jq '.data' +curl -s --data-urlencode 'match[]={__name__="prometheus_http_requests_total"}' localhost:9090/api/v1/series | jq '.data' [ { "__name__": "prometheus_http_requests_total", diff --git a/docs/how-to/configure-and-tune/disable-charmed-rules.md b/docs/how-to/configure-and-tune/disable-charmed-rules.md index 88d3222c..9ff51bcb 100644 --- a/docs/how-to/configure-and-tune/disable-charmed-rules.md +++ b/docs/how-to/configure-and-tune/disable-charmed-rules.md @@ -24,7 +24,7 @@ using a boolean configuration option, called `forward_alert_rules`: For example, to disable forwarding of all alert rules from opentelemetry collector, ``` -$ juju config opentelemetry-collector forward_alert_rules=false +juju config opentelemetry-collector forward_alert_rules=false ``` ## Silence charmed rules using alertmanager configuration diff --git a/docs/how-to/configure-and-tune/evaluate-telemetry-volume.md b/docs/how-to/configure-and-tune/evaluate-telemetry-volume.md index cb657f97..3e322b4c 100644 --- a/docs/how-to/configure-and-tune/evaluate-telemetry-volume.md +++ b/docs/how-to/configure-and-tune/evaluate-telemetry-volume.md @@ -11,12 +11,27 @@ In order to correctly size the VM(s) needed for COS, you need to know how much t ## Metrics rate + +### Manual evaluation +Find out the metrics endpoint manifest for each observed workload. If it is not documented, +you will need to manually count the number of non-comment lines served on the metrics endpoint, +for example: + +```bash +curl -sf localhost:8080/metrics | grep -v "^# " | wc -l +``` + +This will give you the number of time series that will be created for the workload, per unit. + +Another option is to deploy a temporary pilot Prometheus charm. + +### With charmed Prometheus Have your deployment sending all metrics to Prometheus (or Mimir) and inspect the 48hr plot for `count({__name__=~".+"})`. The raw data can also be obtained by querying the Prometheus `query` endpoint directly: ```bash curl -s --data-urlencode 'query=count({__name__=~".+"})' \ - ${PROM_UNIT_IP}:9090/api/v1/query" + http://${PROM_UNIT_IP}:9090/api/v1/query ``` Compare the output to the number of metrics exposed by individual applications. @@ -36,8 +51,13 @@ load[load generator] ---|db| postgresql postgresql ---|metrics-endpoint| prometheus ``` - ## Logs rate +### Manual evaluation +The most reliable way to evaluate the logging rate of a workload is with load tests. + +Another option is to deploy temporary pilot Loki and Prometheus charms. + +### With charmed Loki and Prometheus Have your deployment sending all logs to Loki, and inspect the 48hr plot for `loki_distributor_*_received_total`: ``` diff --git a/docs/how-to/configure-and-tune/selectively-drop-telemetry-scrape-config.md b/docs/how-to/configure-and-tune/selectively-drop-telemetry-scrape-config.md index f87d00e0..94704e8a 100644 --- a/docs/how-to/configure-and-tune/selectively-drop-telemetry-scrape-config.md +++ b/docs/how-to/configure-and-tune/selectively-drop-telemetry-scrape-config.md @@ -52,7 +52,7 @@ graph LR We can specify the `drop` action via a config option for the [scrape-config charm](https://charmhub.io/prometheus-scrape-config-k8s): ```shell -$ juju config sc metric_relabel_configs="$(cat < +Install ``` ## Secure access @@ -34,3 +30,14 @@ Protect and expose COS endpoints for production traffic. Configure TLS encryption Configure ingress ``` + +## Upgrades + +Move between COS revisions with confidence. + +```{toctree} +:maxdepth: 1 + +Cross-track upgrade instructions +``` + diff --git a/docs/how-to/deploy-and-manage/install.md b/docs/how-to/deploy-and-manage/install.md new file mode 100644 index 00000000..dd03d75b --- /dev/null +++ b/docs/how-to/deploy-and-manage/install.md @@ -0,0 +1,106 @@ +--- +myst: + html_meta: + description: "Install the Canonical Observability Stack: preparation checklist covering sizing, networking, storage, and deployment options." +--- + +# How to install COS + +This guide walks you through the prerequisites and deployment planning needed to install the Canonical Observability Stack (COS). Use it to prepare your environment, create a Terraform plan, and deploy supporting components in the recommended order. + +## Preparation + +Before deploying COS or COS Lite, work through the items below. + +### COS flavor + +The [flavor of COS](/explanation/overview/what-is-cos) to install depends on your use case. +If you want to install on edge devices, want to rely on local storage, or do not need high availability, then COS Lite is likely the right choice; otherwise +you should probably go with "full" COS. + +```{mermaid} +graph LR + +subgraph env["Monitored environment"] +opentelemetry-collector +end + +subgraph k8s["K8s cluster"] +COS +end + +subgraph pc["Public cloud"] +cos-alerter["COS Alerter"] +end + +subgraph storage["Storage cluster"] +S3 +end + +opentelemetry-collector ---|telemetry| COS +COS --- S3 +COS --- cos-alerter +``` + +### Kubernetes cluster + +Deploy COS on a high-availability Kubernetes cluster with at least 3 control plane nodes. + +### Sizing + +Use the [sizing guide](/reference/system-requirements) to determine the minimum hardware for your deployment. +If you don't yet know how much telemetry your workloads generate, start with [How to evaluate telemetry volume](/how-to/configure-and-tune/evaluate-telemetry-volume). + +Follow the [storage best practices](/reference/storage) to set up a distributed storage backend with a replication factor of 3. +Do **not** use `hostPath` storage in production. + +### Configure networking + +Review the [networking best practices](/reference/networking) and ensure: + +- A load balancer (for example, MetalLB) is available to give Traefik a stable IP. +- Egress is open for Charmhub, the Juju OCI registry, and Snapcraft. + +### Plan for TLS + +Production deployments should use TLS. +See [How to configure TLS encryption](/how-to/deploy-and-manage/configure-tls-encryption) for the available modes and what you need to prepare (for example, an external certificates provider). + +### Authentication and authorization +Only the Grafana and Traefik charms support authentication. +To expose Grafana publicly, deploy two Traefik charms: one for internal connections and another for external access to provide ingress. + +### Dedicated Juju controller and model + +You should bootstrap a dedicated Juju controller and model just for COS. + +## Terraform plan + +Create a `main.tf` file like this one: + +```hcl +resource "juju_model" "cos" { + name = "cos" +} + +module "cos" { + source = "git::https://github.com/canonical/observability-stack//terraform/cos?ref=tf-cos-3.0.n" + risk = "stable" + model_uuid = juju_model.cos.uuid + s3_endpoint = "http://IP_ADDRESS:PORT" + s3_secret_key = "REPLACE_ME" + s3_access_key = "REPLACE_ME" +} +``` + +### Revision pins +Revision pinning is optional. With revision pins, subsequent `terraform apply` invocation will not refresh charms. Without revision pins, each `terraform apply` would refresh to the latest revision in track, if a new one released. +where `.n` in `tf-cos-3.0.n` is the latest available patch version in the [COS tags](https://github.com/canonical/observability-stack/tags) list. + +### Revision pins + +Deploying COS without revision pins, per component, will deploy the latest charms revisions in-track. Any subsequent Terraform plans will experience the same behaviour i.e., keeping COS up-to-date. However, if you require more stability, it is advised to pin the charm revisions of all components. + +## Deploy COS Alerter + +COS Alerter is a watchdog service for COS. Deploy it on dedicated infrastructure that is separate from your COS or COS Lite deployment. For more information, including deployment details, see the [COS Alerter repository](https://github.com/canonical/cos-alerter). \ No newline at end of file diff --git a/docs/how-to/integrate/add-tracing-to-cos-lite.md b/docs/how-to/integrate/add-tracing-to-cos-lite.md index 0ef209c9..9eb9ceae 100644 --- a/docs/how-to/integrate/add-tracing-to-cos-lite.md +++ b/docs/how-to/integrate/add-tracing-to-cos-lite.md @@ -15,7 +15,7 @@ In the same Juju model as you have COS Lite deployed, deploy the ``tempo-coordin using the following command: ```bash -$ juju deploy tempo-coordinator-k8s tempo \ +juju deploy tempo-coordinator-k8s tempo \ --channel edge \ --trust ``` @@ -28,7 +28,7 @@ with the worker nodes directly. ## Deploy the Tempo Worker ```bash -$ juju deploy tempo-worker-k8s tempo-worker \ +juju deploy tempo-worker-k8s tempo-worker \ --channel edge \ --trust ``` @@ -47,7 +47,7 @@ If you don't have an s3 bucket ready at hand, follow [this guide](https://discou Once you're done deploying ``minio`` and ``s3``, you can run: ```bash -$ juju integrate tempo s3 +juju integrate tempo s3 ``` And wait for the `tempo` application to go to `active/idle`. @@ -56,7 +56,7 @@ And wait for the `tempo` application to go to `active/idle`. ## Integrate coordinator and workers ```bash -$ juju integrate tempo tempo-worker +juju integrate tempo tempo-worker ``` At this point your `juju status` should look like this: @@ -73,29 +73,29 @@ Coordinator is reporting 'degraded' because not all roles are assigned in the re You can enable self-monitoring for ``tempo`` by integrating it with the other COS Lite components. ```bash -$ juju integrate loki:logging tempo:logging -$ juju integrate s3:s3-credentials tempo:s3 -$ juju integrate tempo:grafana-dashboard grafana:grafana-dashboard -$ juju integrate tempo:grafana-source grafana:grafana-source -$ juju integrate tempo:metrics-endpoint prometheus:metrics-endpoint -$ juju integrate tempo:tempo-cluster tempo-worker:tempo-cluster -$ juju integrate traefik:traefik-route tempo:ingress +juju integrate loki:logging tempo:logging +juju integrate s3:s3-credentials tempo:s3 +juju integrate tempo:grafana-dashboard grafana:grafana-dashboard +juju integrate tempo:grafana-source grafana:grafana-source +juju integrate tempo:metrics-endpoint prometheus:metrics-endpoint +juju integrate tempo:tempo-cluster tempo-worker:tempo-cluster +juju integrate traefik:traefik-route tempo:ingress ``` Similarly, you can enable tracing in COS Lite by integrating the COS Lite charms that support it to `tempo` over the `tracing` relation: ```bash -$ juju integrate tempo:tracing alertmanager:tracing -$ juju integrate tempo:tracing catalogue:tracing -$ juju integrate tempo:tracing traefik:charm-tracing -$ juju integrate tempo:tracing traefik:workload-tracing -$ juju integrate tempo:tracing loki:charm-tracing -$ juju integrate tempo:tracing loki:workload-tracing -$ juju integrate tempo:tracing grafana:charm-tracing -$ juju integrate tempo:tracing grafana:workload-tracing -$ juju integrate tempo:tracing prometheus:charm-tracing -$ juju integrate tempo:tracing prometheus:workload-tracing +juju integrate tempo:tracing alertmanager:tracing +juju integrate tempo:tracing catalogue:tracing +juju integrate tempo:tracing traefik:charm-tracing +juju integrate tempo:tracing traefik:workload-tracing +juju integrate tempo:tracing loki:charm-tracing +juju integrate tempo:tracing loki:workload-tracing +juju integrate tempo:tracing grafana:charm-tracing +juju integrate tempo:tracing grafana:workload-tracing +juju integrate tempo:tracing prometheus:charm-tracing +juju integrate tempo:tracing prometheus:workload-tracing ``` ```{note} @@ -107,7 +107,7 @@ You can also achieve the same by running ``jhack imatrix fill``. If you have a charm offering a `certificates` endpoint such as [`self-signed-certificates`](https://charmhub.io/self-signed-certificates), you can integrate it with `tempo`: ```bash -$ juju integrate tempo:certificates ca:certificates +juju integrate tempo:certificates ca:certificates ``` to enable traces to be sent to `tempo` over HTTPS (or gRPCs). diff --git a/docs/how-to/integrate/configure-scrape-jobs.md b/docs/how-to/integrate/configure-scrape-jobs.md index 7c10554d..d7a22f04 100644 --- a/docs/how-to/integrate/configure-scrape-jobs.md +++ b/docs/how-to/integrate/configure-scrape-jobs.md @@ -22,15 +22,15 @@ Deploying the [Prometheus Scrape Config charm](https://charmhub.io/prometheus-sc deploy it from the edge channel: ```bash -$ juju deploy prometheus-scrape-config-k8s --channel latest/edge +juju deploy prometheus-scrape-config-k8s --channel latest/edge ``` Then relate it to the application you want to scrape, in this case [the Zinc charm](https://charmhub.io/zinc-k8s), as well as to Prometheus itself: ```bash -$ juju relate prometheus-scrape-config-k8s zinc-k8s -$ juju relate prometheus-scrape-config-k8s prometheus-k8s +juju relate prometheus-scrape-config-k8s zinc-k8s +juju relate prometheus-scrape-config-k8s prometheus-k8s ``` With that done, you'll now be able to tweak the configuration of the scrape job. @@ -40,7 +40,7 @@ With that done, you'll now be able to tweak the configuration of the scrape job. For a list of configuration options and explanations about what they do as well as what their defaults are, run the `juju config` command without any configuration option: ``` -$ juju config prometheus-scrape-config-k8s +juju config prometheus-scrape-config-k8s ``` ## Changing the Configuration @@ -49,7 +49,7 @@ Let's go ahead and have a look at how our scrape job currently looks: ```bash -$ juju show-unit prometheus-k8s/0 +juju show-unit prometheus-k8s/0 ... @@ -85,14 +85,13 @@ $ juju show-unit prometheus-k8s/0 Then, we will set the `scrape_interval` in the prometheus-scrape-config-k8s charm: ```bash -$ juju config prometheus-scrape-config-k8s scrape_interval=2m +juju config prometheus-scrape-config-k8s scrape_interval=2m ``` Let's have a look again: ```bash - -$ juju show-unit prometheus-k8s/0 +juju show-unit prometheus-k8s/0 ... diff --git a/docs/how-to/integrate/deploy-s3-integrator-and-minio.md b/docs/how-to/integrate/deploy-s3-integrator-and-minio.md index 04b3a52a..0391e24b 100644 --- a/docs/how-to/integrate/deploy-s3-integrator-and-minio.md +++ b/docs/how-to/integrate/deploy-s3-integrator-and-minio.md @@ -18,12 +18,12 @@ single-node configuration, it is suitable for providing an S3 storage backend fo This is [a small python script](https://raw.githubusercontent.com/canonical/tempo-coordinator-k8s-operator/main/scripts/deploy_minio.py) that deploys `minio`, `s3-integrator`, configures them and provisions a bucket for you to use. ```bash -$ juju switch cos # select the model where you have COS-lite deployed -$ sudo snap install astral-uv --classic # this is how we recommend to run the script, but you're free to do it your way -$ curl https://raw.githubusercontent.com/canonical/tempo-coordinator-k8s-operator/main/scripts/deploy_minio.py -o deploy_minio.py +juju switch cos # select the model where you have COS-lite deployed +sudo snap install astral-uv --classic # this is how we recommend to run the script, but you're free to do it your way +curl https://raw.githubusercontent.com/canonical/tempo-coordinator-k8s-operator/main/scripts/deploy_minio.py -o deploy_minio.py # review the script prior to executing it, then: -$ MINIO_BUCKET="tempo" uv run --with minio deploy_minio.py +MINIO_BUCKET="tempo" uv run --with minio deploy_minio.py ``` Once the command exits zero, your storage is ready and you can integrate with the `s3` app. @@ -39,7 +39,7 @@ The `secret-key` must be at least 8 characters long. If not, Minio will crash. ``` ```bash -$ juju deploy minio \ +juju deploy minio \ --channel edge \ --trust \ --config access-key= \ @@ -51,7 +51,7 @@ And wait for it to go to `active/idle`. ### 2. Deploy the S3 Integrator ```bash -$ juju deploy s3-integrator s3 \ +juju deploy s3-integrator s3 \ --channel edge \ --trust ``` @@ -60,7 +60,7 @@ Wait for the `s3` app to go to `blocked/idle`. The `s3` app will go into `blocked` status until you run the `sync-s3-credentials` action to give it access to `minio`. ```bash -$ juju run s3/leader sync-s3-credentials \ +juju run s3/leader sync-s3-credentials \ access-key= \ secret-key= ``` @@ -83,7 +83,7 @@ From there you should be able to create a bucket with a few clicks. See [this gu Alternatively, you can use the Minio Python SDK. ```bash -$ pip install minio +pip install minio ``` Then execute this script: @@ -111,7 +111,7 @@ if not found: Now give the `s3` app access to the bucket. ``` -$ juju config s3 \ +juju config s3 \ endpoint=minio-0.minio-endpoints..svc.cluster.local:9000 \ bucket= ``` diff --git a/docs/how-to/integrate/exposing-a-metrics-endpoint.md b/docs/how-to/integrate/exposing-a-metrics-endpoint.md index 07b8a9ad..2a424210 100644 --- a/docs/how-to/integrate/exposing-a-metrics-endpoint.md +++ b/docs/how-to/integrate/exposing-a-metrics-endpoint.md @@ -19,7 +19,7 @@ amount of code, enable your charm to get scraped by a charm like [prometheus-k8s Fetch the `prometheus_scrape` library using the `charmcraft` command: ```bash -$ charmcraft fetch-lib charms.prometheus_k8s.v0.prometheus_scrape +charmcraft fetch-lib charms.prometheus_k8s.v0.prometheus_scrape ``` ## Import the Library diff --git a/docs/how-to/integrate/integrating-cos-lite-with-uncharmed-applications.md b/docs/how-to/integrate/integrating-cos-lite-with-uncharmed-applications.md index 6ee81a43..71080e00 100644 --- a/docs/how-to/integrate/integrating-cos-lite-with-uncharmed-applications.md +++ b/docs/how-to/integrate/integrating-cos-lite-with-uncharmed-applications.md @@ -27,7 +27,7 @@ We recommend to host Opentelemetry Collector as close as possible to the workloa We recommend to install Opentelemetry Collector via a handy snap we maintain: ```bash -$ sudo snap install opentelemetry-collector +sudo snap install opentelemetry-collector ``` ```{note} @@ -53,7 +53,7 @@ In other words, Traefik's own URL needs to be stable. In the Juju model where COS Lite is deployed, run the command below to find out the URL to the proxied endpoint. ``` -$ juju run traefik/0 show-proxied-endpoints +juju run traefik/0 show-proxied-endpoints ``` Assuming you have [configured the Traefik charm](https://github.com/canonical/traefik-k8s-operator#configurations) to use an external host name, for example `"traefik.url"`, you will see something like: @@ -75,7 +75,7 @@ At this point you will need to follow [the documentation on how to configure Ope Once you've written your finished configuration to `/etc/otelcol/config.d/otelcol_0.yaml `, you'll be able to restart the snap using the following command: ```bash -$ sudo snap restart opentelemetry-collector +sudo snap restart opentelemetry-collector ``` And with that, you are done! Good job, you got this! diff --git a/docs/how-to/validate-and-troubleshoot/troubleshooting.md b/docs/how-to/validate-and-troubleshoot/troubleshooting.md index 410360eb..8163eefa 100644 --- a/docs/how-to/validate-and-troubleshoot/troubleshooting.md +++ b/docs/how-to/validate-and-troubleshoot/troubleshooting.md @@ -40,14 +40,14 @@ apply, although you will need to tailor the exact steps and commands to your set Check with: ```bash -$ microk8s status -a metallb +microk8s status -a metallb ``` If it is disabled, you can enable it with: ```bash -$ IPADDR=$(ip -4 -j route get 2.2.2.2 | jq -r '.[] | .prefsrc') -$ microk8s enable metallb:$IPADDR-$IPADDR +IPADDR=$(ip -4 -j route get 2.2.2.2 | jq -r '.[] | .prefsrc') +microk8s enable metallb:$IPADDR-$IPADDR ``` This command will fetch the IPv4 address assigned to your host, and hand it to MetalLB @@ -60,8 +60,8 @@ for instance `IPADDR=10.0.0.1-10.0.0.100`. Does the Traefik service have an external IP assigned to it? Check with: ```bash -$ JUJU_APP_NAME="traefik" -$ kubectl get svc -A -o wide | grep -E "^NAMESPACE|$JUJU_APP_NAME" +JUJU_APP_NAME="traefik" +kubectl get svc -A -o wide | grep -E "^NAMESPACE|$JUJU_APP_NAME" ``` #### No available IP in address pool @@ -77,14 +77,14 @@ This can happen when: Check with: ```bash -$ kubectl get ipaddresspool -n metallb-system -o yaml && kubectl get all -n metallb-system +kubectl get ipaddresspool -n metallb-system -o yaml && kubectl get all -n metallb-system ``` You could add more IPs to the range: ```bash -$ FROM_IP="..." TO_IP="..." -$ microk8s enable metallb:$FROM_IP-$TO_IP +FROM_IP="..." TO_IP="..." +microk8s enable metallb:$FROM_IP-$TO_IP ``` #### The Load Balancer service type reverted to `ClusterIP` @@ -95,7 +95,7 @@ Juju controller cycling may cause the type to revert from `LoadBalancer` back to Check with: ```bash -$ kubectl get svc -A -o wide | grep -E "^NAMESPACE|LoadBalancer" +kubectl get svc -A -o wide | grep -E "^NAMESPACE|LoadBalancer" ``` If Traefik isn't listed (it's not `LoadBalancer`), then recreate the pod to have it @@ -114,7 +114,7 @@ Verify that the Traefik Kubernetes service now has been assigned an external IP: ``` -$ microk8s.kubectl get services -A +microk8s.kubectl get services -A NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) cos traefik LoadBalancer 10.152.183.130 10.70.43.245 80:32343/TCP,443:30698/TCP 4d3h @@ -127,7 +127,7 @@ If you have COS Lite deployed, you may check that if works as expected using the ```bash # curl http:///-catalogue/ # for example... -$ curl http://10.70.43.245/cos-catalogue/ +curl http://10.70.43.245/cos-catalogue/ ``` This command should return a long HTML code block if everything works as expected. @@ -177,9 +177,9 @@ You can use: Make sure your alerts manifest matches the output of: ```bash -$ juju ssh prometheus/0 curl localhost:9090/api/v1/rules | jq -r '.data.groups | .[] | .rules | .[] | .name' +juju ssh prometheus/0 curl localhost:9090/api/v1/rules | jq -r '.data.groups | .[] | .rules | .[] | .name' # and... -$ juju ssh loki/0 curl localhost:3100/loki/api/v1/rules +juju ssh loki/0 curl localhost:3100/loki/api/v1/rules ``` #### Integration tests @@ -224,7 +224,7 @@ When related to Loki, make sure your logging sources are listed in: Make sure the dashboards manifest you have in the charm matches: ```bash -$ juju ssh grafana/0 curl http://admin:password@localhost:3000/api/search +juju ssh grafana/0 curl http://admin:password@localhost:3000/api/search ``` ### Data Duplication @@ -235,7 +235,7 @@ Charms should use `limit: 1` for the cos-agent relation (see example [here](http but this cannot be enforced by opentelemetry-collector itself. You can confirm this is the case with `jq`: ```bash -$ juju export-bundle | yq -o json '.' | jq -r ' +juju export-bundle | yq -o json '.' | jq -r ' .applications as $apps | .relations as $relations | $apps @@ -313,7 +313,7 @@ for agent, principals in agents.items(): Then run it using: ```bash -$ juju status --format=yaml | ./is_multi_agent.py +juju status --format=yaml | ./is_multi_agent.py ``` If there is a problem, you would see output such as: @@ -454,7 +454,7 @@ deployment itself. For MicroK8s, this would be done by increasing the limits in ### 1. Juju SSH into the machine ```bash -$ juju ssh uk8s/1 +juju ssh uk8s/1 ``` Substitute `uk8s/1` with the name of your MicroK8s unit. If you have more than @@ -465,7 +465,7 @@ one unit, you will need to repeat this for each of them. You can use whatever editor you prefer for this. In this how-to, we'll use ``vim``. ```bash -$ vim /var/snap/microk8s/current/args/containerd-env +vim /var/snap/microk8s/current/args/containerd-env ``` ### 3. Increase the `ulimit` @@ -490,7 +490,7 @@ $ vim /var/snap/microk8s/current/args/containerd-env Restart the machine the MicroK8s unit is deployed on and then wait for it to come back up. ```bash -$ sudo reboot +sudo reboot ``` ### 5. Validate @@ -499,7 +499,7 @@ Validate that the change made it through and had the desired effect once the mac back up and running. ```bash -$ juju ssh uk8s/1 cat /var/snap/microk8s/current/args/containerd-env +juju ssh uk8s/1 cat /var/snap/microk8s/current/args/containerd-env [...] diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md index feac61d0..6bb60036 100644 --- a/docs/reference/glossary.md +++ b/docs/reference/glossary.md @@ -35,7 +35,7 @@ alerting pipeline has failed. See [Topology](/reference/topology). ## COS Configuration -A [peripheral charm](#peripheral-charm) that clones a git repository (via a `git-sync` +A peripheral charm that clones a git repository (via a `git-sync` workload) and provisions alert rules, dashboards, and scrape targets from it into COS. Enables git-ops–style configuration independently of any charmed operator. See [COS components](/reference/cos-components). @@ -90,10 +90,9 @@ per-charm host-health alerts. See [Generic alert rule groups](/explanation/alert ## Git-ops alert rules -Alert rules (and dashboards or scrape targets) loaded from an external git repository via -the [COS Configuration](#cos-configuration) charm. Allows version-controlled, -operator-defined rules outside of any charmed operator. -See [Alert rules](/explanation/alerting/alerting-overview). +Alert rules (and dashboards or scrape targets) loaded from an external git repository via the COS +Configuration charm. Allows version-controlled, operator-defined rules outside of any charmed +operator. See [Alert rules](/explanation/alerting/alerting-overview). ## Juju topology labels diff --git a/docs/tutorial/cos-canonical-k8s-sandbox.conf b/docs/tutorial/cos-canonical-k8s-sandbox.conf index 2e919987..b71b9612 100644 --- a/docs/tutorial/cos-canonical-k8s-sandbox.conf +++ b/docs/tutorial/cos-canonical-k8s-sandbox.conf @@ -15,6 +15,12 @@ snap: - snap install k8s --classic --channel=1.33-classic/stable - snap install terraform --classic +write_files: +- source: + uri: https://github.com/canonical/observability-stack/raw/refs/heads/main/tests/integration/cos/tls_internal/track-dev.tf + path: /var/run/cos/cos-demo.tf + permissions: '0755' + runcmd: - | # [docs:setup-s3] @@ -55,45 +61,17 @@ runcmd: # Set up terraform plan for COS - # [docs:create-terraform-module] - sudo -u ubuntu mkdir ~ubuntu/cos - sudo -u ubuntu tee ~ubuntu/cos/cos-demo.tf << EOF - terraform { - required_version = ">= 1.5" - required_providers { - juju = { - source = "juju/juju" - version = "~> 1.0" - } - } - } - - resource "juju_model" "cos" { - name = "cos" - config = { logging-config = "=WARNING; unit=DEBUG" } - } - - module "cos" { - source = "git::https://github.com/canonical/observability-stack//terraform/cos?ref=main" - model_uuid = juju_model.cos.uuid - risk = "edge" - anti_affinity = false - internal_tls = false - external_certificates_offer_url = null - s3_endpoint = "http://$IPADDR:8080" - s3_secret_key = "secret-key" - s3_access_key = "access-key" - } - EOF - # [docs:create-terraform-module-end] - echo "Deploying COS..." + sudo -u ubuntu juju add-model cos + sudo -u ubuntu mkdir -p ~ubuntu/cos + sudo -u ubuntu cp /var/run/cos/cos-demo.tf ~ubuntu/cos + sudo -u ubuntu terraform -chdir=$(echo ~ubuntu/cos) init - sudo -u ubuntu terraform -chdir=$(echo ~ubuntu/cos) apply -auto-approve + sudo -u ubuntu terraform -chdir=$(echo ~ubuntu/cos) apply -auto-approve -var="model=cos" -var="s3_endpoint=http://$IPADDR:8080" -var="s3_secret_key=secret-key" -var="s3_access_key=access-key" # The first "terraform apply" might fail due to https://github.com/juju/terraform-provider-juju/issues/767. # Running apply again resolves this. - sudo -u ubuntu terraform -chdir=$(echo ~ubuntu/cos) apply -auto-approve + sudo -u ubuntu terraform -chdir=$(echo ~ubuntu/cos) apply -auto-approve -var="model=cos" -var="s3_endpoint=http://$IPADDR:8080" -var="s3_secret_key=secret-key" -var="s3_access_key=access-key" echo "Waiting for model to settle..." sudo -u ubuntu juju wait-for model cos --query='forEach(units, unit => unit.agent-status == "idle") && forEach(applications, app => app.status == "active")' --timeout=10m -final_message: "The COS Lite appliance is ready, after $UPTIME seconds" +final_message: "The COS appliance is ready, after $UPTIME seconds" \ No newline at end of file diff --git a/docs/tutorial/cos-canonical-k8s-sandbox.md b/docs/tutorial/cos-canonical-k8s-sandbox.md index e688bd28..95175c25 100644 --- a/docs/tutorial/cos-canonical-k8s-sandbox.md +++ b/docs/tutorial/cos-canonical-k8s-sandbox.md @@ -36,13 +36,10 @@ and configure RadosGW to listen on port 8080 ([doc](https://canonical-microceph. ## Deploy COS using Terraform -Assuming you are using the username `ubuntu`, create a `cos-demo.tf` file as follows: +Create a `cos-demo.tf` file as follows: -```{literalinclude} /tutorial/cos-canonical-k8s-sandbox.conf - :language: bash - :start-after: [docs:create-terraform-module] - :end-before: [docs:create-terraform-module-end] - :dedent: 4 +```{literalinclude} /../tests/integration/cos/tls_internal/track-dev.tf + :language: hcl ``` **Note**: You can customize further the number of units of each distributed charm and other aspects of COS: have a look at the [`variables.tf`](../../terraform/cos/variables.tf) file of the COS Terraform module for the complete documentation. @@ -50,11 +47,14 @@ Assuming you are using the username `ubuntu`, create a `cos-demo.tf` file as fol To deploy COS in a new model named `cos`, run: ```bash -$ terraform init -$ terraform apply +terraform init +terraform apply -var="model=cos" \ + -var="s3_endpoint=$IPADDR" \ + -var="s3_secret_key=secret-key" \ + -var="s3_access_key=access-key" ``` -You can watch the model as it settles with: +You can watch the model as it settles: ``` juju status --model cos --relations --watch=5s @@ -66,91 +66,163 @@ The output of `juju status --relations` for your deployment should eventually be ``` Model Controller Cloud/Region Version SLA Timestamp -cos ck8s ck8s 3.6.7 unsupported 10:55:15-00:00 - -App Version Status Scale Charm Channel Rev Address Exposed Message -alertmanager 0.27.0 active 1 alertmanager-k8s 2/edge 171 10.152.183.180 no -catalogue active 1 catalogue-k8s 2/edge 94 10.152.183.145 no -grafana 9.5.3 active 1 grafana-k8s 2/edge 155 10.152.183.144 no -grafana-agent 0.40.4 active 1 grafana-agent-k8s 2/edge 148 10.152.183.251 no grafana-dashboards-provider: off -loki active 3 loki-coordinator-k8s 2/edge 35 10.152.183.128 no -loki-backend 3.0.0 active 3 loki-worker-k8s 2/edge 48 10.152.183.22 no backend ready. -loki-read 3.0.0 active 3 loki-worker-k8s 2/edge 48 10.152.183.161 no read ready. -loki-s3-integrator active 1 s3-integrator 2/edge 157 10.152.183.159 no -loki-write 3.0.0 active 3 loki-worker-k8s 2/edge 48 10.152.183.188 no write ready. -mimir active 3 mimir-coordinator-k8s 2/edge 57 10.152.183.187 no -mimir-backend 2.13.0 active 3 mimir-worker-k8s 2/edge 55 10.152.183.168 no backend ready. -mimir-read 2.13.0 active 3 mimir-worker-k8s 2/edge 55 10.152.183.129 no read ready. -mimir-s3-integrator active 1 s3-integrator 2/edge 157 10.152.183.184 no -mimir-write 2.13.0 active 3 mimir-worker-k8s 2/edge 55 10.152.183.225 no write ready. -tempo active 3 tempo-coordinator-k8s 2/edge 91 10.152.183.123 no -tempo-compactor 2.7.1 active 3 tempo-worker-k8s 2/edge 66 10.152.183.138 no compactor ready. -tempo-distributor 2.7.1 active 3 tempo-worker-k8s 2/edge 66 10.152.183.27 no distributor ready. -tempo-ingester 2.7.1 active 3 tempo-worker-k8s 2/edge 66 10.152.183.91 no ingester ready. -tempo-metrics-generator 2.7.1 active 3 tempo-worker-k8s 2/edge 66 10.152.183.200 no metrics-generator ready. -tempo-querier 2.7.1 active 3 tempo-worker-k8s 2/edge 66 10.152.183.59 no querier ready. -tempo-query-frontend 2.7.1 active 3 tempo-worker-k8s 2/edge 66 10.152.183.109 no query-frontend ready. -tempo-s3-integrator active 1 s3-integrator 2/edge 157 10.152.183.205 no -traefik 2.11.0 active 1 traefik-k8s latest/stable 236 10.152.183.197 no Serving at 10.63.93.138 - -Unit Workload Agent Address Ports Message -alertmanager/0* active idle 10.1.0.38 -catalogue/0* active idle 10.1.0.2 -grafana-agent/0* active idle 10.1.0.242 grafana-dashboards-provider: off -grafana/0* active idle 10.1.0.108 -loki-backend/0 active idle 10.1.0.154 backend ready. -loki-backend/1 active idle 10.1.0.162 backend ready. -loki-backend/2* active idle 10.1.0.140 backend ready. -loki-read/0* active idle 10.1.0.187 read ready. -loki-read/1 active idle 10.1.0.169 read ready. -loki-read/2 active idle 10.1.0.235 read ready. -loki-s3-integrator/0* active idle 10.1.0.72 -loki-write/0* active idle 10.1.0.115 write ready. -loki-write/1 active idle 10.1.0.239 write ready. -loki-write/2 active idle 10.1.0.182 write ready. -loki/0 active idle 10.1.0.35 -loki/1* active idle 10.1.0.90 -loki/2 active idle 10.1.0.10 -mimir-backend/0* active idle 10.1.0.3 backend ready. -mimir-backend/1 active idle 10.1.0.24 backend ready. -mimir-backend/2 active idle 10.1.0.147 backend ready. -mimir-read/0* active idle 10.1.0.103 read ready. -mimir-read/1 active idle 10.1.0.112 read ready. -mimir-read/2 active idle 10.1.0.158 read ready. -mimir-s3-integrator/0* active idle 10.1.0.171 -mimir-write/0* active idle 10.1.0.160 write ready. -mimir-write/1 active idle 10.1.0.155 write ready. -mimir-write/2 active idle 10.1.0.96 write ready. -mimir/0 active idle 10.1.0.117 -mimir/1 active idle 10.1.0.222 -mimir/2* active idle 10.1.0.134 -tempo-compactor/0* active idle 10.1.0.204 compactor ready. -tempo-compactor/1 active idle 10.1.0.191 compactor ready. -tempo-compactor/2 active idle 10.1.0.181 compactor ready. -tempo-distributor/0 active idle 10.1.0.53 distributor ready. -tempo-distributor/1 active idle 10.1.0.176 distributor ready. -tempo-distributor/2* active idle 10.1.0.184 distributor ready. -tempo-ingester/0 active idle 10.1.0.221 ingester ready. -tempo-ingester/1 active idle 10.1.0.78 ingester ready. -tempo-ingester/2* active idle 10.1.0.109 ingester ready. -tempo-metrics-generator/0 active idle 10.1.0.13 metrics-generator ready. -tempo-metrics-generator/1* active idle 10.1.0.40 metrics-generator ready. -tempo-metrics-generator/2 active idle 10.1.0.201 metrics-generator ready. -tempo-querier/0* active idle 10.1.0.118 querier ready. -tempo-querier/1 active idle 10.1.0.195 querier ready. -tempo-querier/2 active idle 10.1.0.197 querier ready. -tempo-query-frontend/0* active idle 10.1.0.17 query-frontend ready. -tempo-query-frontend/1 active idle 10.1.0.180 query-frontend ready. -tempo-query-frontend/2 active idle 10.1.0.12 query-frontend ready. -tempo-s3-integrator/0* active idle 10.1.0.247 -tempo/0* active idle 10.1.0.217 -tempo/1 active idle 10.1.0.84 -tempo/2 active idle 10.1.0.249 -traefik/0* active idle 10.1.0.127 Serving at 10.63.93.138 - -Offer Application Charm Rev Connected Endpoint Interface Role -alertmanager-karma-dashboard alertmanager alertmanager-k8s 171 0/0 karma-dashboard karma_dashboard provider -grafana-dashboards grafana grafana-k8s 155 0/0 grafana-dashboard grafana_dashboard requirer -loki-logging loki loki-coordinator-k8s 35 0/0 logging loki_push_api provider -mimir-receive-remote-write mimir mimir-coordinator-k8s 57 0/0 receive-remote-write prometheus_remote_write provider +cos ck8s ck8s 3.6.21 unsupported 12:14:38+02:00 + +App Version Status Scale Charm Channel Rev Address Exposed Message +alertmanager 0.31.0 active 1 alertmanager-k8s dev/edge 206 10.152.183.104 no +ca active 1 self-signed-certificates 1/edge 637 10.152.183.157 no +catalogue active 1 catalogue-k8s dev/edge 125 10.152.183.162 no +grafana 12.4.2 active 1 grafana-k8s dev/edge 187 10.152.183.83 no +loki active 1 loki-coordinator-k8s dev/edge 61 10.152.183.88 no Degraded. +loki-backend 3.7.1 active 1 loki-worker-k8s dev/edge 68 10.152.183.90 no backend ready. +loki-read 3.7.1 active 1 loki-worker-k8s dev/edge 68 10.152.183.211 no read ready. +loki-s3-integrator active 1 s3-integrator 2/edge 550 10.152.183.129 no +loki-write 3.7.1 active 1 loki-worker-k8s dev/edge 68 10.152.183.217 no write ready. +mimir active 1 mimir-coordinator-k8s dev/edge 81 10.152.183.220 no +mimir-backend 2.17.10 active 1 mimir-worker-k8s dev/edge 71 10.152.183.212 no backend ready. +mimir-read 2.17.10 active 1 mimir-worker-k8s dev/edge 71 10.152.183.227 no read ready. +mimir-s3-integrator active 1 s3-integrator 2/edge 550 10.152.183.100 no +mimir-write 2.17.10 active 1 mimir-worker-k8s dev/edge 71 10.152.183.193 no write ready. +otelcol 0.130.1 active 1 opentelemetry-collector-k8s dev/edge 179 10.152.183.115 no +tempo active 1 tempo-coordinator-k8s dev/edge 149 10.152.183.117 no +tempo-compactor 2.10.1 active 1 tempo-worker-k8s dev/edge 102 10.152.183.200 no compactor ready. +tempo-distributor 2.10.1 active 1 tempo-worker-k8s dev/edge 102 10.152.183.228 no distributor ready. +tempo-ingester 2.10.1 active 1 tempo-worker-k8s dev/edge 102 10.152.183.92 no ingester ready. +tempo-metrics-generator 2.10.1 active 1 tempo-worker-k8s dev/edge 102 10.152.183.183 no metrics-generator ready. +tempo-querier 2.10.1 active 1 tempo-worker-k8s dev/edge 102 10.152.183.137 no querier ready. +tempo-query-frontend 2.10.1 active 1 tempo-worker-k8s dev/edge 102 10.152.183.20 no query-frontend ready. +tempo-s3-integrator active 1 s3-integrator 2/edge 550 10.152.183.216 no +traefik 2.11.0 active 1 traefik-k8s latest/edge 294 10.152.183.182 no Serving at http://10.249.85.241 + +Unit Workload Agent Address Ports Message +alertmanager/0* active idle 10.1.0.112 +ca/0* active idle 10.1.0.180 +catalogue/0* active idle 10.1.0.186 +grafana/0* active idle 10.1.0.169 +loki-backend/0* active idle 10.1.0.142 backend ready. +loki-read/0* active idle 10.1.0.225 read ready. +loki-s3-integrator/0* active idle 10.1.0.96 +loki-write/0* active idle 10.1.0.229 write ready. +loki/0* active idle 10.1.0.69 Degraded. +mimir-backend/0* active idle 10.1.0.218 backend ready. +mimir-read/0* active idle 10.1.0.88 read ready. +mimir-s3-integrator/0* active idle 10.1.0.64 +mimir-write/0* active idle 10.1.0.43 write ready. +mimir/0* active idle 10.1.0.57 +otelcol/0* active idle 10.1.0.121 +tempo-compactor/0* active idle 10.1.0.109 compactor ready. +tempo-distributor/0* active idle 10.1.0.30 distributor ready. +tempo-ingester/0* active idle 10.1.0.155 ingester ready. +tempo-metrics-generator/0* active idle 10.1.0.124 metrics-generator ready. +tempo-querier/0* active idle 10.1.0.106 querier ready. +tempo-query-frontend/0* active idle 10.1.0.114 query-frontend ready. +tempo-s3-integrator/0* active idle 10.1.0.81 +tempo/0* active idle 10.1.0.107 +traefik/0* active idle 10.1.0.135 Serving at http://10.249.85.241 + +Offer Application Charm Rev Connected Endpoint Interface Role +alertmanager-karma-dashboard alertmanager alertmanager-k8s 206 0/0 karma-dashboard karma_dashboard provider +certificates ca self-signed-certificates 637 0/0 certificates tls-certificates provider +grafana-dashboards grafana grafana-k8s 187 0/0 grafana-dashboard grafana_dashboard requirer +loki-logging loki loki-coordinator-k8s 61 0/0 logging loki_push_api provider +mimir-receive-remote-write mimir mimir-coordinator-k8s 81 0/0 receive-remote-write prometheus_remote_write provider +send-ca-cert ca self-signed-certificates 637 0/0 send-ca-cert certificate_transfer provider + +Integration provider Requirer Interface Type Message +alertmanager:alerting loki:alertmanager alertmanager_dispatch regular +alertmanager:alerting mimir:alertmanager alertmanager_dispatch regular +alertmanager:grafana-dashboard grafana:grafana-dashboard grafana_dashboard regular +alertmanager:grafana-source grafana:grafana-source grafana_datasource regular +alertmanager:replicas alertmanager:replicas alertmanager_replica peer +alertmanager:self-metrics-endpoint otelcol:metrics-endpoint prometheus_scrape regular +ca:certificates alertmanager:certificates tls-certificates regular +ca:certificates catalogue:certificates tls-certificates regular +ca:certificates grafana:certificates tls-certificates regular +ca:certificates loki:certificates tls-certificates regular +ca:certificates mimir:certificates tls-certificates regular +ca:certificates otelcol:receive-server-cert tls-certificates regular +ca:certificates tempo:certificates tls-certificates regular +ca:send-ca-cert traefik:receive-ca-cert certificate_transfer regular +catalogue:catalogue alertmanager:catalogue catalogue regular +catalogue:catalogue grafana:catalogue catalogue regular +catalogue:catalogue mimir:catalogue catalogue regular +catalogue:catalogue tempo:catalogue catalogue regular +catalogue:replicas catalogue:replicas catalogue_replica peer +grafana:grafana grafana:grafana grafana_peers peer +grafana:replicas grafana:replicas grafana_replicas peer +loki-s3-integrator:s3-credentials loki:s3 s3 regular +loki-s3-integrator:status-peers loki-s3-integrator:status-peers status_peers peer +loki:grafana-dashboards-provider grafana:grafana-dashboard grafana_dashboard regular +loki:grafana-source grafana:grafana-source grafana_datasource regular +loki:logging otelcol:send-loki-logs loki_push_api regular +loki:loki-cluster loki-backend:loki-cluster loki_cluster regular +loki:loki-cluster loki-read:loki-cluster loki_cluster regular +loki:loki-cluster loki-write:loki-cluster loki_cluster regular +loki:loki-peers loki:loki-peers loki_peers peer +loki:self-metrics-endpoint otelcol:metrics-endpoint prometheus_scrape regular +loki:send-datasource tempo:receive-datasource grafana_datasource_exchange regular +mimir-s3-integrator:s3-credentials mimir:s3 s3 regular +mimir-s3-integrator:status-peers mimir-s3-integrator:status-peers status_peers peer +mimir:grafana-dashboards-provider grafana:grafana-dashboard grafana_dashboard regular +mimir:grafana-source grafana:grafana-source grafana_datasource regular +mimir:mimir-cluster mimir-backend:mimir-cluster mimir_cluster regular +mimir:mimir-cluster mimir-read:mimir-cluster mimir_cluster regular +mimir:mimir-cluster mimir-write:mimir-cluster mimir_cluster regular +mimir:mimir-peers mimir:mimir-peers mimir_peers peer +mimir:receive-remote-write otelcol:send-remote-write prometheus_remote_write regular +mimir:receive-remote-write tempo:send-remote-write prometheus_remote_write regular +mimir:self-metrics-endpoint otelcol:metrics-endpoint prometheus_scrape regular +mimir:send-datasource tempo:receive-datasource grafana_datasource_exchange regular +otelcol:grafana-dashboards-provider grafana:grafana-dashboard grafana_dashboard regular +otelcol:peers otelcol:peers otelcol_replica peer +otelcol:receive-loki-logs alertmanager:logging loki_push_api regular +otelcol:receive-loki-logs grafana:logging loki_push_api regular +otelcol:receive-loki-logs loki:logging-consumer loki_push_api regular +otelcol:receive-loki-logs mimir:logging-consumer loki_push_api regular +otelcol:receive-loki-logs tempo:logging loki_push_api regular +otelcol:receive-traces grafana:charm-tracing tracing regular +otelcol:receive-traces loki:charm-tracing tracing regular +otelcol:receive-traces mimir:charm-tracing tracing regular +tempo-s3-integrator:s3-credentials tempo:s3 s3 regular +tempo-s3-integrator:status-peers tempo-s3-integrator:status-peers status_peers peer +tempo:grafana-dashboard grafana:grafana-dashboard grafana_dashboard regular +tempo:grafana-source grafana:grafana-source grafana_datasource regular +tempo:metrics-endpoint otelcol:metrics-endpoint prometheus_scrape regular +tempo:peers tempo:peers tempo_peers peer +tempo:tempo-cluster tempo-compactor:tempo-cluster tempo_cluster regular +tempo:tempo-cluster tempo-distributor:tempo-cluster tempo_cluster regular +tempo:tempo-cluster tempo-ingester:tempo-cluster tempo_cluster regular +tempo:tempo-cluster tempo-metrics-generator:tempo-cluster tempo_cluster regular +tempo:tempo-cluster tempo-querier:tempo-cluster tempo_cluster regular +tempo:tempo-cluster tempo-query-frontend:tempo-cluster tempo_cluster regular +tempo:tracing otelcol:send-traces tracing regular +traefik:ingress alertmanager:ingress ingress regular +traefik:ingress catalogue:ingress ingress regular +traefik:ingress grafana:ingress ingress regular +traefik:ingress loki:ingress ingress regular +traefik:ingress mimir:ingress ingress regular +traefik:peers traefik:peers traefik_peers peer +traefik:traefik-route otelcol:ingress traefik_route regular +traefik:traefik-route tempo:ingress traefik_route regular + +Storage Unit Storage ID Type Pool Mountpoint Size Status Message +alertmanager/0 data/1 filesystem kubernetes /var/lib/juju/storage/data/0 1.0 GiB attached Successfully provisioned volume pvc-306b0833-5c2a-4270-ba20-fac620b8b5be +grafana/0 database/3 filesystem kubernetes /var/lib/juju/storage/database/0 1.0 GiB attached Successfully provisioned volume pvc-b37d7f49-0b5a-409f-8e5b-56a57d212598 +loki-backend/0 loki-persisted/12 filesystem kubernetes /var/lib/juju/storage/loki-persisted/0 1.0 GiB attached Successfully provisioned volume pvc-9b92a901-ac30-4e26-a9c0-81692aecef8c +loki-read/0 loki-persisted/6 filesystem kubernetes /var/lib/juju/storage/loki-persisted/0 1.0 GiB attached Successfully provisioned volume pvc-d24b4519-f3f6-4cc7-b595-7d4c3bb19d75 +loki-write/0 loki-persisted/13 filesystem kubernetes /var/lib/juju/storage/loki-persisted/0 1.0 GiB attached Successfully provisioned volume pvc-4d8a0605-9cad-415a-b55d-f96bc561bb4f +mimir-backend/0 data/7 filesystem kubernetes /var/lib/juju/storage/data/0 1.0 GiB attached Successfully provisioned volume pvc-c2335bbf-0b4b-4509-be45-e6915af0cb87 +mimir-backend/0 recovery-data/8 filesystem kubernetes /var/lib/juju/storage/recovery-data/0 1.0 GiB attached Successfully provisioned volume pvc-ca30d59d-210c-466a-9b0e-956f0b0fc875 +mimir-read/0 data/10 filesystem kubernetes /var/lib/juju/storage/data/0 1.0 GiB attached Successfully provisioned volume pvc-bf7e55a0-f530-47a6-8db8-82f67353bf60 +mimir-read/0 recovery-data/11 filesystem kubernetes /var/lib/juju/storage/recovery-data/0 1.0 GiB attached Successfully provisioned volume pvc-f05111ec-90d4-4d34-b46c-11665f25cbd6 +mimir-write/0 data/4 filesystem kubernetes /var/lib/juju/storage/data/0 1.0 GiB attached Successfully provisioned volume pvc-f15365ba-cc3c-46ac-b770-5baa060091c7 +mimir-write/0 recovery-data/5 filesystem kubernetes /var/lib/juju/storage/recovery-data/0 1.0 GiB attached Successfully provisioned volume pvc-4d0af7f9-ae4c-4396-b047-d5d26bf1de7c +otelcol/0 persisted/2 filesystem kubernetes /var/lib/juju/storage/persisted/0 1.0 GiB attached Successfully provisioned volume pvc-64e14ebe-2e64-484a-9fc1-486d42e5c57c +tempo-compactor/0 wal/18 filesystem kubernetes /var/lib/juju/storage/wal/0 1.0 GiB attached Successfully provisioned volume pvc-d51e4cf4-5ffe-4ffa-b291-d6973b7a1f3f +tempo-distributor/0 wal/9 filesystem kubernetes /var/lib/juju/storage/wal/0 1.0 GiB attached Successfully provisioned volume pvc-493ddbc3-6db7-4212-8eac-24f113c34417 +tempo-ingester/0 wal/16 filesystem kubernetes /var/lib/juju/storage/wal/0 1.0 GiB attached Successfully provisioned volume pvc-02b1e04d-af1e-4103-af93-350186f93a1c +tempo-metrics-generator/0 wal/14 filesystem kubernetes /var/lib/juju/storage/wal/0 1.0 GiB attached Successfully provisioned volume pvc-008f0c76-fbd1-4862-b116-f2a116c6f68f +tempo-querier/0 wal/15 filesystem kubernetes /var/lib/juju/storage/wal/0 1.0 GiB attached Successfully provisioned volume pvc-5b12acbb-af86-471c-8875-2196276cbcd5 +tempo-query-frontend/0 wal/17 filesystem kubernetes /var/lib/juju/storage/wal/0 1.0 GiB attached Successfully provisioned volume pvc-e5cde942-0ada-4dc8-9ade-fe6989d1f7a0 +traefik/0 configurations/0 filesystem kubernetes /var/lib/juju/storage/configurations/0 1.0 GiB attached Successfully provisioned volume pvc-5930d32c-7909-4d81-b039-ab45c3461200 ``` diff --git a/docs/tutorial/cos-lite-canonical-k8s-sandbox.conf b/docs/tutorial/cos-lite-canonical-k8s-sandbox.conf deleted file mode 100644 index 2856bc86..00000000 --- a/docs/tutorial/cos-lite-canonical-k8s-sandbox.conf +++ /dev/null @@ -1,34 +0,0 @@ -#cloud-config - -# Usage example: -# multipass launch noble --cloud-init ./cos-lite-canonical-k8s-sandbox.conf --name cos --memory 8G --cpus 4 --disk 40G -# multipass exec cos -- tail -f /var/log/cloud-init-output.log - -snap: - commands: - - snap install juju --channel=3.6/stable - - snap install k8s --classic --channel=1.33-classic/stable - -runcmd: - - | - echo "Setting up K8s..." - k8s bootstrap - k8s status --wait-ready - k8s enable local-storage - k8s enable load-balancer - k8s set load-balancer.l2-mode=true load-balancer.cidrs="$(ip -4 -j route get 2.2.2.2 | jq -r '.[] | .prefsrc')/32" - k8s status --wait-ready - - echo "Adding K8s cloud to Juju..." - k8s kubectl config view --raw | sudo -u ubuntu /snap/juju/current/bin/juju add-k8s ck8s --client - echo "Bootstrapping K8s controller..." - sudo -u ubuntu juju bootstrap ck8s - - echo "Deploying COS Lite..." - sudo -u ubuntu juju add-model cos - sudo -u ubuntu curl -o ~ubuntu/offers-overlay.yaml -fL https://raw.githubusercontent.com/canonical/cos-lite-bundle/main/overlays/offers-overlay.yaml - sudo -u ubuntu juju deploy cos-lite --trust --overlay ~ubuntu/offers-overlay.yaml - echo "Waiting for model to settle..." - sudo -u ubuntu juju wait-for model cos --query='forEach(units, unit => unit.agent-status == "idle") && forEach(applications, app => app.status == "active")' --timeout=10m - -final_message: "The COS Lite appliance is ready, after $UPTIME seconds" diff --git a/docs/tutorial/cos-lite-canonical-k8s-sandbox.md b/docs/tutorial/cos-lite-canonical-k8s-sandbox.md index 39dbe7d6..2bbc346d 100644 --- a/docs/tutorial/cos-lite-canonical-k8s-sandbox.md +++ b/docs/tutorial/cos-lite-canonical-k8s-sandbox.md @@ -8,8 +8,6 @@ myst: In this tutorial you deploy a single-node COS Lite appliance, backed by hostPath storage. -You can reproduce the entire tutorial with a [cloud-config](cos-lite-canonical-k8s-sandbox.conf) script. - ## Prerequisites - A 4cpu8gb node or better, with at least 40Gi disk space (see [Sizing guide](../reference/system-requirements) for production deployments). @@ -21,114 +19,120 @@ You can reproduce the entire tutorial with a [cloud-config](cos-lite-canonical-k - K8s cloud added to Juju ([doc](https://documentation.ubuntu.com/juju/3.6/howto/manage-clouds/#add-a-kubernetes-cloud)). -## Deploy the COS Lite bundle +## Deploy COS Lite using Terraform -It is usually a good idea to create a dedicated model for the COS Lite bundle. So let's do just that and call the new model `cos`: +To deploy the COS Lite solution, create this root module and set the `model` to `cos-lite`: -Create a new juju model, `cos`: +```{literalinclude} ../../tests/integration/cos_lite/tls_internal/track-dev.tf +``` -```bash -$ juju add-model cos +```{dropdown} Customize COS Lite +You can customize the deployment of COS Lite by overriding the defaults of these variables: + +```{literalinclude} ../../terraform/cos-lite/variables.tf ``` -Next, deploy the bundle with: +Then, use terraform to deploy the module: -```bash -$ juju deploy cos-lite --trust +```shell +terraform init +terraform apply -var="model=cos-lite" ``` + You can watch the model as it settles with: -```bash -$ juju status --relations --watch=5s +```shell +juju status --relations --watch=5s ``` The status of your deployment should eventually be very similar to the following: +```shell +juju status --relations --storage +``` + ``` -$ juju status --relations --storage -Model Controller Cloud/Region Version SLA Timestamp -cos ck8s ck8s 3.6.6 unsupported 16:44:44-04:00 - -App Version Status Scale Charm Channel Rev Address Exposed Message -alertmanager 0.27.0 active 1 alertmanager-k8s 1/stable 160 10.152.183.253 no -catalogue active 1 catalogue-k8s 1/stable 81 10.152.183.181 no -grafana 9.5.3 active 1 grafana-k8s 1/stable 143 10.152.183.152 no -loki 2.9.6 active 1 loki-k8s 1/stable 190 10.152.183.176 no -prometheus 2.52.0 active 1 prometheus-k8s latest/stable 234 10.152.183.54 no -traefik 2.11.0 active 1 traefik-k8s latest/stable 236 10.152.183.56 no Serving at 10.63.93.172 - -Unit Workload Agent Address Ports Message -alertmanager/0* active idle 10.1.0.221 -catalogue/0* active idle 10.1.0.225 -grafana/0* active idle 10.1.0.129 -loki/0* active idle 10.1.0.72 -prometheus/0* active idle 10.1.0.60 -traefik/0* active idle 10.1.0.65 Serving at 10.63.93.172 +Model Controller Cloud/Region Version SLA Timestamp +cos-lite kub k8s 3.6.21 unsupported 11:39:52+02:00 + +App Version Status Scale Charm Channel Rev Address Exposed Message +alertmanager 0.31.0 active 1 alertmanager-k8s dev/edge 202 10.152.183.170 no +ca active 1 self-signed-certificates 1/edge 637 10.152.183.71 no +catalogue active 1 catalogue-k8s dev/edge 125 10.152.183.24 no +grafana 12.4.2 active 1 grafana-k8s dev/edge 186 10.152.183.59 no +loki 3.7.1 active 1 loki-k8s dev/edge 226 10.152.183.29 no +prometheus 3.11.1 active 1 prometheus-k8s dev/edge 292 10.152.183.104 no +traefik 2.11.0 active 1 traefik-k8s latest/edge 292 10.152.183.108 no Serving at http://192.168.178.192 + +Unit Workload Agent Address Ports Message +alertmanager/0* active idle 10.1.0.207 +ca/0* active idle 10.1.0.155 +catalogue/0* active idle 10.1.0.193 +grafana/0* active idle 10.1.0.229 +loki/0* active idle 10.1.0.133 +prometheus/0* active idle 10.1.0.83 +traefik/0* active idle 10.1.0.93 Serving at http://192.168.178.192 + +Offer Application Charm Rev Connected Endpoint Interface Role +alertmanager-karma-dashboard alertmanager alertmanager-k8s 202 0/0 karma-dashboard karma_dashboard provider +certificates ca self-signed-certificates 637 0/0 certificates tls-certificates provider +grafana-dashboards grafana grafana-k8s 186 0/0 grafana-dashboard grafana_dashboard requirer +loki-logging loki loki-k8s 226 0/0 logging loki_push_api provider +prometheus-metrics-endpoint prometheus prometheus-k8s 292 0/0 metrics-endpoint prometheus_scrape requirer +prometheus-receive-remote-write prometheus prometheus-k8s 292 0/0 receive-remote-write prometheus_remote_write provider +send-ca-cert ca self-signed-certificates 637 0/0 send-ca-cert certificate_transfer provider Integration provider Requirer Interface Type Message -alertmanager:alerting loki:alertmanager alertmanager_dispatch regular -alertmanager:alerting prometheus:alertmanager alertmanager_dispatch regular -alertmanager:grafana-dashboard grafana:grafana-dashboard grafana_dashboard regular -alertmanager:grafana-source grafana:grafana-source grafana_datasource regular -alertmanager:replicas alertmanager:replicas alertmanager_replica peer -alertmanager:self-metrics-endpoint prometheus:metrics-endpoint prometheus_scrape regular -catalogue:catalogue alertmanager:catalogue catalogue regular -catalogue:catalogue grafana:catalogue catalogue regular -catalogue:catalogue prometheus:catalogue catalogue regular -catalogue:replicas catalogue:replicas catalogue_replica peer -grafana:grafana grafana:grafana grafana_peers peer -grafana:metrics-endpoint prometheus:metrics-endpoint prometheus_scrape regular -grafana:replicas grafana:replicas grafana_replicas peer -loki:grafana-dashboard grafana:grafana-dashboard grafana_dashboard regular -loki:grafana-source grafana:grafana-source grafana_datasource regular -loki:metrics-endpoint prometheus:metrics-endpoint prometheus_scrape regular -loki:replicas loki:replicas loki_replica peer -prometheus:grafana-dashboard grafana:grafana-dashboard grafana_dashboard regular -prometheus:grafana-source grafana:grafana-source grafana_datasource regular -prometheus:prometheus-peers prometheus:prometheus-peers prometheus_peers peer -traefik:ingress alertmanager:ingress ingress regular -traefik:ingress catalogue:ingress ingress regular -traefik:ingress-per-unit loki:ingress ingress_per_unit regular -traefik:ingress-per-unit prometheus:ingress ingress_per_unit regular -traefik:metrics-endpoint prometheus:metrics-endpoint prometheus_scrape regular -traefik:peers traefik:peers traefik_peers peer -traefik:traefik-route grafana:ingress traefik_route regular +alertmanager:alerting loki:alertmanager alertmanager_dispatch regular +alertmanager:alerting prometheus:alertmanager alertmanager_dispatch regular +alertmanager:grafana-dashboard grafana:grafana-dashboard grafana_dashboard regular +alertmanager:grafana-source grafana:grafana-source grafana_datasource regular +alertmanager:replicas alertmanager:replicas alertmanager_replica peer +alertmanager:self-metrics-endpoint prometheus:metrics-endpoint prometheus_scrape regular +ca:certificates alertmanager:certificates tls-certificates regular +ca:certificates catalogue:certificates tls-certificates regular +ca:certificates grafana:certificates tls-certificates regular +ca:certificates loki:certificates tls-certificates regular +ca:certificates prometheus:certificates tls-certificates regular +ca:send-ca-cert traefik:receive-ca-cert certificate_transfer regular +catalogue:catalogue alertmanager:catalogue catalogue regular +catalogue:catalogue grafana:catalogue catalogue regular +catalogue:catalogue prometheus:catalogue catalogue regular +catalogue:replicas catalogue:replicas catalogue_replica peer +grafana:grafana grafana:grafana grafana_peers peer +grafana:metrics-endpoint prometheus:metrics-endpoint prometheus_scrape regular +grafana:replicas grafana:replicas grafana_replicas peer +loki:grafana-dashboard grafana:grafana-dashboard grafana_dashboard regular +loki:grafana-source grafana:grafana-source grafana_datasource regular +loki:metrics-endpoint prometheus:metrics-endpoint prometheus_scrape regular +loki:replicas loki:replicas loki_replica peer +prometheus:grafana-dashboard grafana:grafana-dashboard grafana_dashboard regular +prometheus:grafana-source grafana:grafana-source grafana_datasource regular +prometheus:prometheus-peers prometheus:prometheus-peers prometheus_peers peer +traefik:ingress alertmanager:ingress ingress regular +traefik:ingress catalogue:ingress ingress regular +traefik:ingress grafana:ingress ingress regular +traefik:ingress-per-unit loki:ingress ingress_per_unit regular +traefik:ingress-per-unit prometheus:ingress ingress_per_unit regular +traefik:metrics-endpoint prometheus:metrics-endpoint prometheus_scrape regular +traefik:peers traefik:peers traefik_peers peer Storage Unit Storage ID Type Pool Mountpoint Size Status Message -alertmanager/0 data/0 filesystem kubernetes /var/lib/juju/storage/data/0 1.0 GiB attached Successfully provisioned volume pvc-eb1dc923-32a0-4729-9ec8-694b50672987 -grafana/0 database/1 filesystem kubernetes /var/lib/juju/storage/database/0 1.0 GiB attached Successfully provisioned volume pvc-b5df8210-671a-4ed6-a083-3fe50e3c6fdc -loki/0 active-index-directory/2 filesystem kubernetes /var/lib/juju/storage/active-index-directory/0 1.0 GiB attached Successfully provisioned volume pvc-af183528-4399-42c2-ae59-94e71e1a18c9 -loki/0 loki-chunks/3 filesystem kubernetes /var/lib/juju/storage/loki-chunks/0 1.0 GiB attached Successfully provisioned volume pvc-86caedeb-e0a5-438f-9ab9-d118f5629723 -prometheus/0 database/4 filesystem kubernetes /var/lib/juju/storage/database/0 1.0 GiB attached Successfully provisioned volume pvc-eaece84a-5b45-4f08-b82f-5eb07163d637 -traefik/0 configurations/5 filesystem kubernetes /var/lib/juju/storage/configurations/0 1.0 GiB attached Successfully provisioned volume pvc-4b1da33c-e66f-42bf-a12f-4ac11806a63a +alertmanager/0 data/8 filesystem kubernetes /var/lib/juju/storage/data/0 1.0 GiB attached Successfully provisioned volume pvc-cf00986d-5428-467b-89f9-a0788e996702 +grafana/0 database/6 filesystem kubernetes /var/lib/juju/storage/database/0 1.0 GiB attached Successfully provisioned volume pvc-eddf2615-6147-4e78-8224-fcf69e8080c5 +loki/0 active-index-directory/4 filesystem kubernetes /var/lib/juju/storage/active-index-directory/0 1.0 GiB attached Successfully provisioned volume pvc-3d7b9b8d-1d50-469c-b5ad-94201e517d41 +loki/0 loki-chunks/5 filesystem kubernetes /var/lib/juju/storage/loki-chunks/0 1.0 GiB attached Successfully provisioned volume pvc-aa88c858-aa5f-4981-8330-7eb8245ea581 +prometheus/0 database/3 filesystem kubernetes /var/lib/juju/storage/database/0 1.0 GiB attached Successfully provisioned volume pvc-e8ae59b8-e046-4041-9a44-9bde5b9950ae +traefik/0 configurations/2 filesystem kubernetes /var/lib/juju/storage/configurations/0 1.0 GiB attached Successfully provisioned volume pvc-29e9fd9a-b787-4056-9cd3-5135b6a1c442 ``` Now COS Lite is good to go: you can relate software with it to begin the monitoring! -## Add "offers" to enable cross-model relations - -Download the [offers](https://github.com/canonical/cos-lite-bundle/blob/main/overlays/offers-overlay.yaml) -[overlay](https://documentation.ubuntu.com/charmcraft/stable/reference/files/bundle-yaml-file/), - -```bash -curl -L https://raw.githubusercontent.com/canonical/cos-lite-bundle/main/overlays/offers-overlay.yaml -O -``` - - and update the deployment: +Obtain the Grafana admin password, ```bash -juju deploy cos-lite --trust --overlay ./offers-overlay.yaml -``` - -This enables [cross-model relations](https://documentation.ubuntu.com/juju/3.6/reference/relation/#cross-model-relation). -In the output of `juju status` you should now see the following new section: - -``` -Offer Application Charm Rev Connected Endpoint Interface Role -alertmanager-karma-dashboard alertmanager alertmanager-k8s 160 0/0 karma-dashboard karma_dashboard provider -grafana-dashboards grafana grafana-k8s 143 0/0 grafana-dashboard grafana_dashboard requirer -loki-logging loki loki-k8s 190 0/0 logging loki_push_api provider -prometheus-receive-remote-write prometheus prometheus-k8s 234 0/0 receive-remote-write prometheus_remote_write provider +juju run grafana/leader get-admin-password ``` +then head over to the URL listed in the output and use the provided password to log in. diff --git a/docs/tutorial/cos-lite-microk8s-sandbox.md b/docs/tutorial/cos-lite-microk8s-sandbox.md index f3003c08..d63af89f 100644 --- a/docs/tutorial/cos-lite-microk8s-sandbox.md +++ b/docs/tutorial/cos-lite-microk8s-sandbox.md @@ -10,13 +10,36 @@ In this tutorial you deploy a single-node COS Lite appliance, backed by hostPath ## Prerequisites -This tutorial assumes you have a Juju controller bootstrapped on a -MicroK8s cloud that is ready to use, on a 4cpu8gb node or better, with at least 40Gi disk space -(see [Sizing guide](../reference/system-requirements) for production deployments). -Typical setup using [snaps](https://snapcraft.io/) -can be found in the [Juju docs](https://documentation.ubuntu.com/juju/3.6/howto/manage-your-deployment/). +This tutorial assumes you have: + +- A Juju controller bootstrapped on a MicroK8s cloud, running on a node with + at least 4 CPU, 8 GB RAM, and 40 GB of disk space. + + MicroK8s must be installed from a **strict** channel (not `--classic`): Juju + 3.x is itself a strictly confined snap and cannot bootstrap a classic + MicroK8s installation. See the MicroK8s + [strict install guide](https://canonical.com/microk8s/docs/install-strict) + for channel selection, and the Juju + [MicroK8s cloud reference](https://documentation.ubuntu.com/juju/3.6/reference/cloud/list-of-supported-clouds/the-microk8s-cloud-and-juju/) + for how Juju integrates with it. Your user must also be a member of the + `snap_microk8s` group. + +```{dropdown} Add your user to the snap_microk8s group + Strict MicroK8s uses the `snap_microk8s` group rather than the `microk8s` + group used by the classic snap. To add your current user and pick up the new + membership in the current shell: + + sudo usermod -a -G snap_microk8s $USER + newgrp snap_microk8s +``` + +- `jq` installed on the machine running the Juju client. It is used for JSON + parsing in the `metallb` step below and is not preinstalled on a fresh + Ubuntu image. Install it with: -Follow the instructions there to install Juju and MicroK8s. +```bash +sudo apt-get update && sudo apt-get install -y jq +``` ## Introduction @@ -35,7 +58,7 @@ For the COS Lite bundle deployment to go smoothly, make sure the following Micro You can check this with `microk8s status`, and if any are missing, enable them with ```bash -$ microk8s enable dns +microk8s enable dns ``` ```{note} @@ -44,23 +67,23 @@ consider deploying MicroCeph on MicroK8s using this [guide](https://canonical.co ``` ```bash -$ microk8s enable hostpath-storage +microk8s enable hostpath-storage ``` The bundle comes with Traefik to provide ingress, for which you'll need a load balancer controller. If you don't have one already, the `metallb` add-on should be enabled: ```bash -$ IPADDR=$(ip -4 -j route get 2.2.2.2 | jq -r '.[] | .prefsrc') -$ microk8s enable metallb:$IPADDR-$IPADDR +IPADDR=$(ip -4 -j route get 2.2.2.2 | jq -r '.[] | .prefsrc') +microk8s enable metallb:$IPADDR-$IPADDR ``` To wait for all the add-ons to be rolled out, then run: ```bash -$ microk8s kubectl rollout status deployments/hostpath-provisioner -n kube-system -w -$ microk8s kubectl rollout status deployments/coredns -n kube-system -w -$ microk8s kubectl rollout status daemonset.apps/speaker -n metallb-system -w +microk8s kubectl rollout status deployments/hostpath-provisioner -n kube-system -w +microk8s kubectl rollout status deployments/coredns -n kube-system -w +microk8s kubectl rollout status daemonset.apps/speaker -n metallb-system -w ``` ```{note} @@ -76,26 +99,26 @@ By default, MicroK8s will use `8.8.8.8` and `8.8.4.4` as DNS servers, which can It is usually a good idea to create a dedicated model for the COS Lite bundle. So let's do just that and call the new model `cos`: ```bash -$ juju add-model cos -$ juju switch cos +juju add-model cos +juju switch cos ``` Next, deploy the bundle with: ```bash -$ juju deploy cos-lite --trust +juju deploy cos-lite --trust ``` Now you can sit back and watch the deployment take place: ```bash -$ juju status --relations --watch=5s +juju status --relations --watch=5s ``` The status of your deployment should eventually be very similar to the following: ``` -$ juju status --relations +juju status --relations Model Controller Cloud/Region Version SLA Timestamp cos microk8s microk8s/localhost 3.6.4 unsupported 15:48:47+04:00 @@ -172,9 +195,9 @@ So, if you were following the previous steps you would first need to switch to a To use any of the overlays above, you need to include an `--overlay` argument per overlay (applied in order): ```bash -$ curl -L https://raw.githubusercontent.com/canonical/cos-lite-bundle/main/overlays/offers-overlay.yaml -O -$ curl -L https://raw.githubusercontent.com/canonical/cos-lite-bundle/main/overlays/storage-small-overlay.yaml -O -$ juju deploy cos-lite \ +curl -L https://raw.githubusercontent.com/canonical/cos-lite-bundle/main/overlays/offers-overlay.yaml -O +curl -L https://raw.githubusercontent.com/canonical/cos-lite-bundle/main/overlays/storage-small-overlay.yaml -O +juju deploy cos-lite \ --trust \ --overlay ./offers-overlay.yaml \ --overlay ./storage-small-overlay.yaml @@ -182,18 +205,22 @@ $ juju deploy cos-lite \ ## Deploy COS Lite using Terraform -Create a `cos-lite-microk8s-sandbox.tf` file with the following Terraform module, or include it in your Terraform plan: +To deploy the COS Lite solution, create this root module and set the `model` to `cos-lite`: -```{literalinclude} /tutorial/cos-lite-microk8s-sandbox.tf +```{literalinclude} ../../tests/integration/cos_lite/tls_internal/track-dev.tf ``` -**Note**: You can customize further the revisions of each charm and other aspects of COS Lite: have a look at the [`variables.tf`](../../terraform/cos-lite/variables.tf) file of the COS Lite Terraform module for the complete documentation. +```{dropdown} Customize COS Lite +You can customize the deployment of COS Lite by overriding the defaults of these variables: + +```{literalinclude} ../../terraform/cos-lite/variables.tf +``` -It is usually a good idea to create a dedicated model for COS Lite. This module creates one named `cos`, which you can override. +It is usually a good idea to create a dedicated model for COS Lite. This module creates one named `cos-lite`. If there are multiple clouds registered in the controller or a default cloud is not setup, an explicit reference of the cloud in the base terraform file needs to exist. Look at [how to reference a K8s cloud](../how-to/configure-and-tune/reference-k8s-cloud-for-cos.md) section. @@ -206,15 +233,15 @@ To use a custom storage class for container's persistent volume during deploymen Next, deploy COS Lite in the new model, run: ```bash -$ terraform init -$ terraform apply # verify the changes you're applying before accepting! +terraform init +terraform apply -var="model=cos-lite" ``` Now you can sit back and watch the deployment take place: ```bash -$ juju switch cos -$ juju status --relations --watch=5s +juju switch cos +juju status --relations --watch=5s ``` ## Browse dashboards @@ -226,7 +253,7 @@ Traefik action. For example: ```bash -$ juju run traefik/0 show-proxied-endpoints --format=yaml \ +juju run traefik/0 show-proxied-endpoints --format=yaml \ | yq '."traefik/0".results."proxied-endpoints"' \ | jq ``` @@ -261,7 +288,7 @@ Note that Grafana does not appear in the list. Currently, to obtain Grafana's proxied endpoint you would need to look at catalogue's relation data directly - try running: ```bash -$ juju show-unit catalogue/0 | grep url +juju show-unit catalogue/0 | grep url ``` ...which should return a list of the endpoints like this: @@ -278,7 +305,7 @@ With ingress in place, you can still access the workloads via pod IPs, but you w to include the original port, as well as the ingress path. For example: ``` -$ curl 10.1.55.34:9093/cos-alertmanager/-/ready +curl 10.1.55.34:9093/cos-alertmanager/-/ready ``` The default password for Grafana is automatically generated for every installation. To @@ -286,7 +313,7 @@ access Grafana's web interface, use the username `admin`, and the password obtai from the [`get-admin-password`](https://charmhub.io/grafana-k8s/actions) action, e.g: ```bash -$ juju run grafana/leader get-admin-password --model cos +juju run grafana/leader get-admin-password --model cos ``` Enjoy! diff --git a/docs/tutorial/cos-lite-microk8s-sandbox.tf b/docs/tutorial/cos-lite-microk8s-sandbox.tf deleted file mode 100644 index 853c42df..00000000 --- a/docs/tutorial/cos-lite-microk8s-sandbox.tf +++ /dev/null @@ -1,22 +0,0 @@ -terraform { - required_version = ">= 1.5" - required_providers { - juju = { - source = "juju/juju" - version = "~> 1.0" - } - } -} - -resource "juju_model" "cos" { - name = "cos" - config = { logging-config = "=WARNING; unit=DEBUG" } -} - -module "cos-lite" { - source = "git::https://github.com/canonical/observability-stack//terraform/cos-lite?ref=main" - model_uuid = juju_model.cos.uuid - risk = "edge" - ssc = { channel = "1/stable" } - traefik = { channel = "latest/edge" } -} diff --git a/terraform/cos-dev/README.md b/terraform/cos-dev/README.md index fcb03a75..9ff8b7be 100644 --- a/terraform/cos-dev/README.md +++ b/terraform/cos-dev/README.md @@ -19,6 +19,8 @@ This module is intended for development and testing environments where full HA i | [alertmanager](#module\_alertmanager) | git::https://github.com/canonical/alertmanager-k8s-operator//terraform | n/a | | [catalogue](#module\_catalogue) | git::https://github.com/canonical/catalogue-k8s-operator//terraform | n/a | | [grafana](#module\_grafana) | git::https://github.com/canonical/grafana-k8s-operator//terraform | n/a | +| [istio\_beacon](#module\_istio\_beacon) | git::https://github.com/canonical/istio-beacon-k8s-operator//terraform | n/a | +| [istio\_ingress](#module\_istio\_ingress) | git::https://github.com/canonical/istio-ingress-k8s-operator//terraform | n/a | | [loki\_coordinator](#module\_loki\_coordinator) | git::https://github.com/canonical/loki-operators//coordinator/terraform | n/a | | [loki\_worker](#module\_loki\_worker) | git::https://github.com/canonical/loki-operators//worker/terraform | n/a | | [loki\_worker\_backend](#module\_loki\_worker\_backend) | git::https://github.com/canonical/loki-operators//worker/terraform | n/a | @@ -49,28 +51,27 @@ This module is intended for development and testing environments where full HA i | [alertmanager](#input\_alertmanager) | Application configuration for Alertmanager. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "alertmanager")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [base](#input\_base) | The operating system on which to deploy. E.g. ubuntu@24.04. Check Charmhub for per-charm base support. | `string` | `"ubuntu@24.04"` | no | | [catalogue](#input\_catalogue) | Application configuration for Catalogue. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "catalogue")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | -| [external\_ca\_cert\_offer\_url](#input\_external\_ca\_cert\_offer\_url) | A Juju offer URL (e.g. admin/external-ca.send-ca-cert) of a CA providing the 'certificate\_transfer' integration for applications to trust ingress via Traefik. | `string` | `null` | no | -| [external\_certificates\_offer\_url](#input\_external\_certificates\_offer\_url) | A Juju offer URL of a CA providing the 'tls\_certificates' integration for Traefik to supply it with server certificates | `string` | `null` | no | | [grafana](#input\_grafana) | Application configuration for Grafana. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "grafana")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [ingress](#input\_ingress) | Per-component toggle for ingress integrations |
object({
alertmanager = optional(bool, true)
catalogue = optional(bool, true)
grafana = optional(bool, true)
loki = optional(bool, true)
mimir = optional(bool, true)
opentelemetry_collector = optional(bool, true)
tempo = optional(bool, true)
})
| `{}` | no | -| [internal\_tls](#input\_internal\_tls) | Specify whether to use TLS or not for internal COS communication. By default, TLS is enabled using self-signed-certificates | `bool` | `true` | no | -| [loki\_bucket](#input\_loki\_bucket) | Loki S3 bucket name | `string` | `"loki"` | no | +| [istio\_beacon](#input\_istio\_beacon) | Application configuration for istio-beacon. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "istio-beacon")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | +| [istio\_ingress](#input\_istio\_ingress) | Application configuration for istio-ingress. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "istio-ingress")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [loki\_coordinator](#input\_loki\_coordinator) | Application configuration for the Loki coordinator. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "loki")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [loki\_worker](#input\_loki\_worker) | Application configuration for the Loki worker(s). For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "loki-worker")
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
# Monolithic mode (role-all)
config = optional(map(string), {})
storage_directives = optional(map(string), {})
units = optional(number, 1)
# Distributed mode
backend_config = optional(map(string), {})
read_config = optional(map(string), {})
write_config = optional(map(string), {})
backend_storage_directives = optional(map(string), {})
read_storage_directives = optional(map(string), {})
write_storage_directives = optional(map(string), {})
backend_units = optional(number, 1)
read_units = optional(number, 1)
write_units = optional(number, 1)
})
| `{}` | no | -| [mimir\_bucket](#input\_mimir\_bucket) | Mimir S3 bucket name | `string` | `"mimir"` | no | +| [mesh](#input\_mesh) | Configure the service mesh. |
object({
enabled = optional(bool, false)
cmr_urls = optional(object({
alermanager = optional(string, null)
catalogue = optional(string, null)
grafana = optional(string, null)
loki = optional(string, null)
mimir = optional(string, null)
tempo = optional(string, null)
}), {}
)
})
| `{}` | no | | [mimir\_coordinator](#input\_mimir\_coordinator) | Application configuration for the Mimir coordinator. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "mimir")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [mimir\_worker](#input\_mimir\_worker) | Application configuration for the Mimir worker(s). For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "mimir-worker")
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
# Monolithic mode (role-all)
config = optional(map(string), {})
storage_directives = optional(map(string), {})
units = optional(number, 1)
# Distributed mode
backend_config = optional(map(string), {})
read_config = optional(map(string), {})
write_config = optional(map(string), {})
backend_storage_directives = optional(map(string), {})
read_storage_directives = optional(map(string), {})
write_storage_directives = optional(map(string), {})
backend_units = optional(number, 1)
read_units = optional(number, 1)
write_units = optional(number, 1)
})
| `{}` | no | | [model\_uuid](#input\_model\_uuid) | Reference to an existing model resource or data source for the model to deploy to | `string` | n/a | yes | | [opentelemetry\_collector](#input\_opentelemetry\_collector) | Application configuration for OpenTelemetry Collector. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "otelcol")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | +| [reverse\_proxy](#input\_reverse\_proxy) | Configure the reverse proxy. |
object({
enabled = optional(bool, true)
cmr_urls = optional(object({
certificates = optional(string, null)
receive_ca_cert = optional(string, null)
}), {}
)
})
| `{}` | no | | [risk](#input\_risk) | Risk level that the applications are (unless overwritten by individual channels) deployed from | `string` | `"edge"` | no | | [s3\_access\_key](#input\_s3\_access\_key) | S3 access-key credential. Required when storage\_backend is 's3'. | `string` | `null` | no | +| [s3\_buckets](#input\_s3\_buckets) | S3 bucket names for components |
object({
loki = optional(string, "loki")
mimir = optional(string, "mimir")
tempo = optional(string, "tempo")
})
| `{}` | no | | [s3\_endpoint](#input\_s3\_endpoint) | S3 endpoint URL. Required when storage\_backend is 's3'. | `string` | `null` | no | | [s3\_integrator](#input\_s3\_integrator) | Application configuration shared by all S3-integrators (one deployed per coordinated worker). For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [s3\_secret\_key](#input\_s3\_secret\_key) | S3 secret-key credential. Required when storage\_backend is 's3'. | `string` | `null` | no | | [seaweedfs](#input\_seaweedfs) | Application configuration for SeaweedFS. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "seaweedfs")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [ssc](#input\_ssc) | Application configuration for Self-signed-certificates. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "ca")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [storage\_backend](#input\_storage\_backend) | Storage backend: 'seaweedfs' (built-in S3-compatible storage) or 's3' (external S3/Ceph via s3-integrator). | `string` | `"seaweedfs"` | no | -| [tempo\_bucket](#input\_tempo\_bucket) | Tempo S3 bucket name | `string` | `"tempo"` | no | | [tempo\_coordinator](#input\_tempo\_coordinator) | Application configuration for the Tempo coordinator. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "tempo")
config = optional(map(string), {})
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
storage_directives = optional(map(string), {})
units = optional(number, 1)
})
| `{}` | no | | [tempo\_worker](#input\_tempo\_worker) | Application configuration for the Tempo worker(s). For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application |
object({
app_name = optional(string, "tempo-worker")
constraints = optional(string, "arch=amd64")
revision = optional(number, null)
# Monolithic mode (role-all)
config = optional(map(string), {})
storage_directives = optional(map(string), {})
units = optional(number, 1)
# Distributed mode
querier_config = optional(map(string), {})
query_frontend_config = optional(map(string), {})
ingester_config = optional(map(string), {})
distributor_config = optional(map(string), {})
compactor_config = optional(map(string), {})
metrics_generator_config = optional(map(string), {})
querier_storage_directives = optional(map(string), {})
query_frontend_storage_directives = optional(map(string), {})
ingester_storage_directives = optional(map(string), {})
distributor_storage_directives = optional(map(string), {})
compactor_storage_directives = optional(map(string), {})
metrics_generator_storage_directives = optional(map(string), {})
querier_units = optional(number, 1)
query_frontend_units = optional(number, 1)
ingester_units = optional(number, 1)
distributor_units = optional(number, 1)
compactor_units = optional(number, 1)
metrics_generator_units = optional(number, 1)
})
| `{}` | no | | [topology](#input\_topology) | Deployment topology: 'monolithic' (single role-all worker per component) or 'distributed' (separate workers per role). | `string` | `"monolithic"` | no | diff --git a/terraform/cos-dev/applications.tf b/terraform/cos-dev/applications.tf index 24518092..b69056f4 100644 --- a/terraform/cos-dev/applications.tf +++ b/terraform/cos-dev/applications.tf @@ -35,30 +35,30 @@ module "grafana" { replace_triggers = [terraform_data.grafana_litestream_resource.id] } -module "istio-ingress" { +module "istio_ingress" { count = var.mesh_enabled ? 1 : 0 source = "git::https://github.com/canonical/istio-ingress-k8s-operator//terraform" - app_name = var.catalogue.app_name - channel = local.channels.catalogue - config = var.catalogue.config - constraints = var.catalogue.constraints + app_name = var.istio_ingress.app_name + channel = local.channels.istio_ingress + config = var.istio_ingress.config + constraints = var.istio_ingress.constraints model_uuid = var.model_uuid - revision = local.revisions.catalogue - storage_directives = var.catalogue.storage_directives - units = var.catalogue.units + revision = local.revisions.istio_ingress + storage_directives = var.istio_ingress.storage_directives + units = var.istio_ingress.units } -module "istio-beacon" { +module "istio_beacon" { count = var.mesh_enabled ? 1 : 0 source = "git::https://github.com/canonical/istio-beacon-k8s-operator//terraform" - app_name = var.catalogue.app_name - channel = local.channels.catalogue - config = var.catalogue.config - constraints = var.catalogue.constraints + app_name = var.istio_beacon.app_name + channel = local.channels.istio_beacon + config = var.istio_beacon.config + constraints = var.istio_beacon.constraints model_uuid = var.model_uuid - revision = local.revisions.catalogue - storage_directives = var.catalogue.storage_directives - units = var.catalogue.units + revision = local.revisions.istio_beacon + storage_directives = var.istio_beacon.storage_directives + units = var.istio_beacon.units } module "loki_coordinator" { @@ -366,6 +366,7 @@ module "tempo_worker_metrics_generator" { } module "traefik" { + count = var.internal_tls ? 1 : 0 source = "git::https://github.com/canonical/traefik-k8s-operator//terraform" app_name = var.traefik.app_name channel = local.channels.traefik diff --git a/terraform/cos-dev/integrations.tf b/terraform/cos-dev/integrations.tf index 9efba9a7..18947ae2 100644 --- a/terraform/cos-dev/integrations.tf +++ b/terraform/cos-dev/integrations.tf @@ -649,14 +649,14 @@ resource "juju_integration" "ingress" { app_name = module.mimir_coordinator.app_name endpoint = "ingress" } - } : k => v if var.ingress[k] && !var.mesh_enabled + } : k => v if var.ingress[k] && var.internal_tls } model_uuid = var.model_uuid application { - name = module.traefik.app_name - endpoint = module.traefik.endpoints.ingress + name = module.traefik[0].app_name + endpoint = module.traefik[0].endpoints.ingress } application { @@ -666,7 +666,7 @@ resource "juju_integration" "ingress" { } resource "juju_integration" "grafana_ingress" { - count = var.ingress.grafana && !var.mesh_enabled ? 1 : 0 + count = var.ingress.grafana && var.internal_tls ? 1 : 0 model_uuid = var.model_uuid @@ -676,8 +676,8 @@ resource "juju_integration" "grafana_ingress" { } application { - name = module.traefik.app_name - endpoint = module.traefik.endpoints.ingress + name = module.traefik[0].app_name + endpoint = module.traefik[0].endpoints.ingress } lifecycle { replace_triggered_by = [terraform_data.grafana_ingress_interface, terraform_data.grafana_litestream_resource] } @@ -694,14 +694,14 @@ resource "juju_integration" "traefik_route" { app_name = module.tempo_coordinator.app_name endpoint = module.tempo_coordinator.requires.ingress } - } : k => v if var.ingress[k] && !var.mesh_enabled + } : k => v if var.ingress[k] && var.internal_tls } model_uuid = var.model_uuid application { - name = module.traefik.app_name - endpoint = module.traefik.endpoints.traefik_route + name = module.traefik[0].app_name + endpoint = module.traefik[0].endpoints.traefik_route } application { @@ -863,8 +863,8 @@ resource "juju_integration" "traefik_receive_ca_certificate" { } application { - name = module.traefik.app_name - endpoint = module.traefik.endpoints.receive_ca_cert + name = module.traefik[0].app_name + endpoint = module.traefik[0].endpoints.receive_ca_cert } } @@ -874,13 +874,11 @@ resource "juju_integration" "external_traefik_certificates" { count = local.tls_termination ? 1 : 0 model_uuid = var.model_uuid - application { - offer_url = var.external_certificates_offer_url - } + application { offer_url = var.external_certificates_offer_url } application { - name = module.traefik.app_name - endpoint = module.traefik.endpoints.certificates + name = module.traefik[0].app_name + endpoint = module.traefik[0].endpoints.certificates } } @@ -888,10 +886,7 @@ resource "juju_integration" "external_grafana_ca_cert" { count = local.tls_termination ? 1 : 0 model_uuid = var.model_uuid - application { - offer_url = var.external_ca_cert_offer_url - } - + application { offer_url = var.external_ca_cert_offer_url } application { name = module.grafana.app_name endpoint = module.grafana.requires.receive_ca_cert @@ -902,10 +897,7 @@ resource "juju_integration" "external_otelcol_ca_cert" { count = local.tls_termination ? 1 : 0 model_uuid = var.model_uuid - application { - offer_url = var.external_ca_cert_offer_url - } - + application { offer_url = var.external_ca_cert_offer_url } application { name = module.opentelemetry_collector.app_name endpoint = module.opentelemetry_collector.requires.receive_ca_cert diff --git a/terraform/cos-dev/locals.tf b/terraform/cos-dev/locals.tf index cd9c490b..f9f1c3b2 100644 --- a/terraform/cos-dev/locals.tf +++ b/terraform/cos-dev/locals.tf @@ -1,14 +1,16 @@ locals { tls_termination = var.external_certificates_offer_url != null ? true : false - traefik_base = "ubuntu@20.04" + traefik_base = "ubuntu@20.04" tracks = { - alertmanager = "dev" - catalogue = "dev" - grafana = "dev" - loki = "dev" - mimir = "dev" - otelcol = "dev" - tempo = "dev" + alertmanager = "dev" + catalogue = "dev" + grafana = "dev" + istio_beacon = "dev" + istio_ingress = "dev" + loki = "dev" + mimir = "dev" + otelcol = "dev" + tempo = "dev" s3_integrator = "2" seaweedfs = "latest" @@ -19,6 +21,8 @@ locals { alertmanager = "${local.tracks.alertmanager}/${var.risk}" catalogue = "${local.tracks.catalogue}/${var.risk}" grafana = "${local.tracks.grafana}/${var.risk}" + istio_beacon = "${local.tracks.istio_beacon}/${var.risk}" + istio_ingress = "${local.tracks.istio_ingress}/${var.risk}" loki = "${local.tracks.loki}/${var.risk}" mimir = "${local.tracks.mimir}/${var.risk}" otelcol = "${local.tracks.otelcol}/${var.risk}" @@ -32,6 +36,8 @@ locals { alertmanager = var.alertmanager.revision != null ? var.alertmanager.revision : data.juju_charm.alertmanager_info.revision catalogue = var.catalogue.revision != null ? var.catalogue.revision : data.juju_charm.catalogue_info.revision grafana = var.grafana.revision != null ? var.grafana.revision : data.juju_charm.grafana_info.revision + istio_beacon = var.istio_beacon.revision != null ? var.istio_beacon.revision : data.juju_charm.istio_beacon_info.revision + istio_ingress = var.istio_ingress.revision != null ? var.istio_ingress.revision : data.juju_charm.istio_ingress_info.revision loki_coordinator = var.loki_coordinator.revision != null ? var.loki_coordinator.revision : data.juju_charm.loki_coordinator_info.revision loki_worker = var.loki_worker.revision != null ? var.loki_worker.revision : data.juju_charm.loki_worker_info.revision mimir_coordinator = var.mimir_coordinator.revision != null ? var.mimir_coordinator.revision : data.juju_charm.mimir_coordinator_info.revision diff --git a/terraform/cos-dev/offers.tf b/terraform/cos-dev/offers.tf index 37e047c1..f925b10a 100644 --- a/terraform/cos-dev/offers.tf +++ b/terraform/cos-dev/offers.tf @@ -38,6 +38,7 @@ resource "juju_offer" "tempo_tracing" { # -------------- # CMR Mesh offers -------------- # resource "juju_offer" "alertmanager_provide_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "alertmanager-provide-cmr-mesh" model_uuid = var.model_uuid application_name = module.alertmanager.app_name @@ -45,6 +46,7 @@ resource "juju_offer" "alertmanager_provide_cmr_mesh" { } resource "juju_offer" "alertmanager_require_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "alertmanager-require-cmr-mesh" model_uuid = var.model_uuid application_name = module.alertmanager.app_name @@ -52,6 +54,7 @@ resource "juju_offer" "alertmanager_require_cmr_mesh" { } resource "juju_offer" "catalogue_provide_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "catalogue-provide-cmr-mesh" model_uuid = var.model_uuid application_name = module.catalogue.app_name @@ -59,6 +62,7 @@ resource "juju_offer" "catalogue_provide_cmr_mesh" { } resource "juju_offer" "catalogue_require_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "catalogue-require-cmr-mesh" model_uuid = var.model_uuid application_name = module.catalogue.app_name @@ -66,6 +70,7 @@ resource "juju_offer" "catalogue_require_cmr_mesh" { } resource "juju_offer" "grafana_provide_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "grafana-provide-cmr-mesh" model_uuid = var.model_uuid application_name = module.grafana.app_name @@ -75,6 +80,7 @@ resource "juju_offer" "grafana_provide_cmr_mesh" { } resource "juju_offer" "grafana_require_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "grafana-require-cmr-mesh" model_uuid = var.model_uuid application_name = module.grafana.app_name @@ -84,6 +90,7 @@ resource "juju_offer" "grafana_require_cmr_mesh" { } resource "juju_offer" "loki_provide_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "loki-provide-cmr-mesh" model_uuid = var.model_uuid application_name = module.loki_coordinator.app_name @@ -91,6 +98,7 @@ resource "juju_offer" "loki_provide_cmr_mesh" { } resource "juju_offer" "loki_require_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "loki-require-cmr-mesh" model_uuid = var.model_uuid application_name = module.loki_coordinator.app_name @@ -98,6 +106,7 @@ resource "juju_offer" "loki_require_cmr_mesh" { } resource "juju_offer" "mimir_provide_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "mimir-provide-cmr-mesh" model_uuid = var.model_uuid application_name = module.mimir_coordinator.app_name @@ -105,6 +114,7 @@ resource "juju_offer" "mimir_provide_cmr_mesh" { } resource "juju_offer" "mimir_require_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "mimir-require-cmr-mesh" model_uuid = var.model_uuid application_name = module.mimir_coordinator.app_name @@ -112,6 +122,7 @@ resource "juju_offer" "mimir_require_cmr_mesh" { } resource "juju_offer" "opentelemetry_collector_provide_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "opentelemetry-collector-provide-cmr-mesh" model_uuid = var.model_uuid application_name = module.opentelemetry_collector.app_name @@ -119,6 +130,7 @@ resource "juju_offer" "opentelemetry_collector_provide_cmr_mesh" { } resource "juju_offer" "opentelemetry_collector_require_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "opentelemetry-collector-require-cmr-mesh" model_uuid = var.model_uuid application_name = module.opentelemetry_collector.app_name @@ -126,6 +138,7 @@ resource "juju_offer" "opentelemetry_collector_require_cmr_mesh" { } resource "juju_offer" "tempo_provide_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "tempo-provide-cmr-mesh" model_uuid = var.model_uuid application_name = module.tempo_coordinator.app_name @@ -133,6 +146,7 @@ resource "juju_offer" "tempo_provide_cmr_mesh" { } resource "juju_offer" "tempo_require_cmr_mesh" { + count = var.mesh_enabled ? 1 : 0 name = "tempo-require-cmr-mesh" model_uuid = var.model_uuid application_name = module.tempo_coordinator.app_name diff --git a/terraform/cos-dev/outputs.tf b/terraform/cos-dev/outputs.tf index 74d663ba..6de7ddc5 100644 --- a/terraform/cos-dev/outputs.tf +++ b/terraform/cos-dev/outputs.tf @@ -9,20 +9,20 @@ output "offers" { tempo_tracing = juju_offer.tempo_tracing # CMR Mesh - alertmanager_provide_cmr_mesh = juju_offer.alertmanager_provide_cmr_mesh - alertmanager_require_cmr_mesh = juju_offer.alertmanager_require_cmr_mesh - catalogue_provide_cmr_mesh = juju_offer.catalogue_provide_cmr_mesh - catalogue_require_cmr_mesh = juju_offer.catalogue_require_cmr_mesh - grafana_provide_cmr_mesh = juju_offer.grafana_provide_cmr_mesh - grafana_require_cmr_mesh = juju_offer.grafana_require_cmr_mesh - loki_provide_cmr_mesh = juju_offer.loki_provide_cmr_mesh - loki_require_cmr_mesh = juju_offer.loki_require_cmr_mesh - mimir_provide_cmr_mesh = juju_offer.mimir_provide_cmr_mesh - mimir_require_cmr_mesh = juju_offer.mimir_require_cmr_mesh - opentelemetry_collector_provide_cmr_mesh = juju_offer.opentelemetry_collector_provide_cmr_mesh - opentelemetry_collector_require_cmr_mesh = juju_offer.opentelemetry_collector_require_cmr_mesh - tempo_provide_cmr_mesh = juju_offer.tempo_provide_cmr_mesh - tempo_require_cmr_mesh = juju_offer.tempo_require_cmr_mesh + alertmanager_provide_cmr_mesh = try(juju_offer.alertmanager_provide_cmr_mesh[0], null) + alertmanager_require_cmr_mesh = try(juju_offer.alertmanager_require_cmr_mesh[0], null) + catalogue_provide_cmr_mesh = try(juju_offer.catalogue_provide_cmr_mesh[0], null) + catalogue_require_cmr_mesh = try(juju_offer.catalogue_require_cmr_mesh[0], null) + grafana_provide_cmr_mesh = try(juju_offer.grafana_provide_cmr_mesh[0], null) + grafana_require_cmr_mesh = try(juju_offer.grafana_require_cmr_mesh[0], null) + loki_provide_cmr_mesh = try(juju_offer.loki_provide_cmr_mesh[0], null) + loki_require_cmr_mesh = try(juju_offer.loki_require_cmr_mesh[0], null) + mimir_provide_cmr_mesh = try(juju_offer.mimir_provide_cmr_mesh[0], null) + mimir_require_cmr_mesh = try(juju_offer.mimir_require_cmr_mesh[0], null) + opentelemetry_collector_provide_cmr_mesh = try(juju_offer.opentelemetry_collector_provide_cmr_mesh[0], null) + opentelemetry_collector_require_cmr_mesh = try(juju_offer.opentelemetry_collector_require_cmr_mesh[0], null) + tempo_provide_cmr_mesh = try(juju_offer.tempo_provide_cmr_mesh[0], null) + tempo_require_cmr_mesh = try(juju_offer.tempo_require_cmr_mesh[0], null) } description = "All Juju offers which are exposed by this product module" } @@ -34,6 +34,8 @@ output "components" { alertmanager = module.alertmanager catalogue = module.catalogue grafana = module.grafana + istio_beacon = try(module.istio_beacon[0], null) + istio_ingress = try(module.istio_ingress[0], null) loki_coordinator = module.loki_coordinator loki_worker = try(module.loki_worker[0], null) loki_worker_backend = try(module.loki_worker_backend[0], null) @@ -55,7 +57,7 @@ output "components" { tempo_worker_distributor = try(module.tempo_worker_distributor[0], null) tempo_worker_compactor = try(module.tempo_worker_compactor[0], null) tempo_worker_metrics_generator = try(module.tempo_worker_metrics_generator[0], null) - traefik = module.traefik + traefik = try(module.traefik[0], null) } description = "All Terraform charm modules which make up this product module" } diff --git a/terraform/cos-dev/tests/conditional_ingress.tftest.hcl b/terraform/cos-dev/tests/conditional_ingress.tftest.hcl index bc9f5762..819bb44a 100644 --- a/terraform/cos-dev/tests/conditional_ingress.tftest.hcl +++ b/terraform/cos-dev/tests/conditional_ingress.tftest.hcl @@ -1,34 +1,14 @@ mock_provider "juju" {} -variables { - model_uuid = "00000000-0000-0000-0000-000000000000" -} - -# --- Default: all ingress integrations enabled --- +variables { model_uuid = "00000000-0000-0000-0000-000000000000" } -run "default_ingress_all_enabled" { - command = plan - - assert { - condition = length(juju_integration.ingress) == 4 - error_message = "Expected 4 ingress integrations (alertmanager, catalogue, loki, mimir), got ${length(juju_integration.ingress)}" - } +# TODO: This feature also depends on the x2 Traefik story, maybe internal_tls is not the right name +# TODO: Do we need to remove offers / outputs TF conditionally? +# TODO: We need to keep the COS API the same across products: feature in COS, COS Lite, and COS Dev - # Grafana uses a separate count-based resource due to lifecycle replace_triggered_by - assert { - condition = length(juju_integration.grafana_ingress) == 1 - error_message = "Expected 1 grafana_ingress integration, got ${length(juju_integration.grafana_ingress)}" - } +# --- internal_tls: enabled - all ingress disabled --- - assert { - condition = length(juju_integration.traefik_route) == 2 - error_message = "Expected 2 traefik_route integrations (opentelemetry_collector, tempo), got ${length(juju_integration.traefik_route)}" - } -} - -# --- All ingress disabled --- - -run "ingress_all_disabled" { +run "internal_tls_ingress_disabled" { command = plan variables { @@ -59,45 +39,14 @@ run "ingress_all_disabled" { } } -# --- Only grafana exposed --- - -run "ingress_only_grafana" { - command = plan - - variables { - ingress = { - alertmanager = false - catalogue = false - grafana = true - loki = false - mimir = false - opentelemetry_collector = false - tempo = false - } - } - - assert { - condition = length(juju_integration.ingress) == 0 - error_message = "Expected 0 ingress integrations, got ${length(juju_integration.ingress)}" - } - - assert { - condition = length(juju_integration.grafana_ingress) == 1 - error_message = "Expected 1 grafana_ingress integration, got ${length(juju_integration.grafana_ingress)}" - } - - assert { - condition = length(juju_integration.traefik_route) == 0 - error_message = "Expected 0 traefik_route integrations, got ${length(juju_integration.traefik_route)}" - } -} - -# --- Only tempo exposed (traefik_route) --- +# --- mesh: enabled - all ingress disabled --- -run "ingress_only_tempo" { +run "mesh_ingress_disabled" { command = plan variables { + mesh_enabled = true + internal_tls = false ingress = { alertmanager = false catalogue = false @@ -105,176 +54,22 @@ run "ingress_only_tempo" { loki = false mimir = false opentelemetry_collector = false - tempo = true - } - } - - assert { - condition = length(juju_integration.ingress) == 0 - error_message = "Expected 0 ingress integrations, got ${length(juju_integration.ingress)}" - } - - assert { - condition = length(juju_integration.traefik_route) == 1 - error_message = "Expected 1 traefik_route integration (tempo), got ${length(juju_integration.traefik_route)}" - } - - assert { - condition = contains(keys(juju_integration.traefik_route), "tempo") - error_message = "Expected traefik_route to contain 'tempo' key" - } -} - -# --- Partial override: disable alertmanager and tempo --- - -run "ingress_partial_override" { - command = plan - - variables { - ingress = { - alertmanager = false - tempo = false - } - } - - assert { - condition = length(juju_integration.ingress) == 3 - error_message = "Expected 3 ingress integrations (catalogue, loki, mimir), got ${length(juju_integration.ingress)}" - } - - assert { - condition = !contains(keys(juju_integration.ingress), "alertmanager") - error_message = "Expected ingress to NOT contain 'alertmanager' key" - } - - assert { - condition = length(juju_integration.grafana_ingress) == 1 - error_message = "Expected 1 grafana_ingress integration, got ${length(juju_integration.grafana_ingress)}" - } - - assert { - condition = length(juju_integration.traefik_route) == 1 - error_message = "Expected 1 traefik_route integration (opentelemetry_collector), got ${length(juju_integration.traefik_route)}" - } - - assert { - condition = contains(keys(juju_integration.traefik_route), "opentelemetry_collector") - error_message = "Expected traefik_route to contain 'opentelemetry_collector' key" - } -} - -# --- mesh_enabled: all ingress via istio, none via traefik --- - -run "mesh_ingress_all_enabled" { - command = plan - - variables { - mesh_enabled = true - internal_tls = false - } - - # Traefik ingress resources should be empty - assert { - condition = length(juju_integration.ingress) == 0 - error_message = "Expected 0 traefik ingress integrations when mesh is enabled, got ${length(juju_integration.ingress)}" - } - - assert { - condition = length(juju_integration.grafana_ingress) == 0 - error_message = "Expected 0 grafana_ingress integrations when mesh is enabled, got ${length(juju_integration.grafana_ingress)}" - } - - assert { - condition = length(juju_integration.traefik_route) == 0 - error_message = "Expected 0 traefik_route integrations when mesh is enabled, got ${length(juju_integration.traefik_route)}" - } - - # Istio ingress resources should be populated - assert { - condition = length(juju_integration.istio_ingress) == 4 - error_message = "Expected 4 istio_ingress integrations (alertmanager, catalogue, loki, mimir), got ${length(juju_integration.istio_ingress)}" - } - - assert { - condition = length(juju_integration.grafana_istio_ingress) == 1 - error_message = "Expected 1 grafana_istio_ingress integration, got ${length(juju_integration.grafana_istio_ingress)}" - } - - assert { - condition = length(juju_integration.istio_ingress_route) == 2 - error_message = "Expected 2 istio_ingress_route integrations (opentelemetry_collector, tempo), got ${length(juju_integration.istio_ingress_route)}" - } -} - -# --- mesh_enabled with partial ingress override --- - -run "mesh_ingress_partial" { - command = plan - - variables { - mesh_enabled = true - internal_tls = false - ingress = { - alertmanager = false - catalogue = true - grafana = false - loki = true - mimir = false - opentelemetry_collector = true tempo = false } } - # Traefik resources should all be empty - assert { - condition = length(juju_integration.ingress) == 0 - error_message = "Expected 0 traefik ingress integrations when mesh is enabled, got ${length(juju_integration.ingress)}" - } - - assert { - condition = length(juju_integration.grafana_ingress) == 0 - error_message = "Expected 0 grafana_ingress integrations when mesh is enabled" - } - - assert { - condition = length(juju_integration.traefik_route) == 0 - error_message = "Expected 0 traefik_route integrations when mesh is enabled" - } - - # Istio resources should respect the ingress toggles - assert { - condition = length(juju_integration.istio_ingress) == 2 - error_message = "Expected 2 istio_ingress integrations (catalogue, loki), got ${length(juju_integration.istio_ingress)}" - } - - assert { - condition = length(juju_integration.grafana_istio_ingress) == 0 - error_message = "Expected 0 grafana_istio_ingress integrations when grafana ingress is disabled" - } - - assert { - condition = length(juju_integration.istio_ingress_route) == 1 - error_message = "Expected 1 istio_ingress_route integration (opentelemetry_collector), got ${length(juju_integration.istio_ingress_route)}" - } -} - -# --- mesh disabled (default): no istio ingress resources --- - -run "no_mesh_no_istio_ingress" { - command = plan - assert { condition = length(juju_integration.istio_ingress) == 0 - error_message = "Expected 0 istio_ingress integrations when mesh is disabled, got ${length(juju_integration.istio_ingress)}" + error_message = "Expected 0 istio_ingress integrations, got ${length(juju_integration.istio_ingress)}" } assert { condition = length(juju_integration.grafana_istio_ingress) == 0 - error_message = "Expected 0 grafana_istio_ingress integrations when mesh is disabled" + error_message = "Expected 0 grafana_istio_ingress integrations, got ${length(juju_integration.grafana_istio_ingress)}" } assert { condition = length(juju_integration.istio_ingress_route) == 0 - error_message = "Expected 0 istio_ingress_route integrations when mesh is disabled" + error_message = "Expected 0 istio_ingress_route integrations, got ${length(juju_integration.istio_ingress_route)}" } } diff --git a/terraform/cos-dev/tests/mesh.tftest.hcl b/terraform/cos-dev/tests/mesh.tftest.hcl new file mode 100644 index 00000000..acc0bec3 --- /dev/null +++ b/terraform/cos-dev/tests/mesh.tftest.hcl @@ -0,0 +1,105 @@ +mock_provider "juju" {} + +variables { model_uuid = "00000000-0000-0000-0000-000000000000" } + +# --- mesh and internal_tls enabled: validation error --- + +run "mesh_and_internal_tls_enabled_fails" { + command = plan + + variables { + mesh_enabled = true + internal_tls = true + } + + expect_failures = [var.mesh_enabled] +} + +# --- mesh and internal_tls disabled: no validation error --- + +run "mesh_and_internal_tls_disabled" { + command = plan + + variables { + mesh_enabled = false + internal_tls = false + } +} + +# --- default: mesh disabled - no ingress via istio --- + +run "mesh_disabled" { + command = plan + + assert { + condition = length(module.istio_beacon) == 0 + error_message = "Expected no istio_beacon module when the mesh is disabled" + } + + assert { + condition = length(module.istio_ingress) == 0 + error_message = "Expected no istio_ingress module when the mesh is disabled" + } + + assert { + condition = length(juju_integration.istio_beacon) == 0 + error_message = "Expected no istio_beacon integrations when the mesh is disabled" + } + + assert { + condition = length(juju_integration.istio_ingress) == 0 + error_message = "Expected no istio_ingress integrations when the mesh is disabled" + } + + assert { + condition = length(juju_integration.grafana_istio_ingress) == 0 + error_message = "Expected no grafana_istio_ingress integrations when the mesh is disabled" + } + + assert { + condition = length(juju_integration.istio_ingress_route) == 0 + error_message = "Expected no istio_ingress_route integrations when the mesh is disabled" + } +} + +# --- mesh enabled: ingress via istio --- + +run "mesh_enabled" { + command = plan + + variables { + mesh_enabled = true + internal_tls = false + } + + assert { + condition = length(module.istio_beacon) == 1 + error_message = "Expected istio_beacon module when the mesh is enabled" + } + + assert { + condition = length(module.istio_ingress) == 1 + error_message = "Expected istio_ingress module when the mesh is enabled" + } + + assert { + condition = length(juju_integration.istio_beacon) == 7 + error_message = "Unexpected istio_beacon integrations when the mesh is enabled" + } + + assert { + condition = length(juju_integration.istio_ingress) == 4 + error_message = "Unexpected istio_ingress integrations when the mesh is enabled" + } + + assert { + condition = length(juju_integration.grafana_istio_ingress) == 1 + error_message = "Unexpected grafana_istio_ingress integrations when the mesh is enabled" + } + + assert { + condition = length(juju_integration.istio_ingress_route) == 2 + error_message = "Unexpected istio_ingress_route integrations when the mesh is enabled" + + } +} diff --git a/terraform/cos-dev/tests/mesh_enabled.tftest.hcl b/terraform/cos-dev/tests/mesh_enabled.tftest.hcl deleted file mode 100644 index 54f7448b..00000000 --- a/terraform/cos-dev/tests/mesh_enabled.tftest.hcl +++ /dev/null @@ -1,65 +0,0 @@ -mock_provider "juju" {} - -variables { - model_uuid = "00000000-0000-0000-0000-000000000000" -} - -# --- mesh_enabled=false (default): no istio modules or integrations deployed --- - -run "mesh_disabled_by_default" { - command = plan - - assert { - condition = length(module.istio-beacon) == 0 - error_message = "Expected no istio-beacon module when mesh is disabled" - } - - assert { - condition = length(module.istio-ingress) == 0 - error_message = "Expected no istio-ingress module when mesh is disabled" - } - - assert { - condition = length(juju_integration.istio_beacon) == 0 - error_message = "Expected no istio_beacon integrations when mesh is disabled" - } -} - -# --- mesh_enabled=true with internal_tls=false: istio modules and integrations deployed --- - -run "mesh_enabled_without_tls" { - command = plan - - variables { - mesh_enabled = true - internal_tls = false - } - - assert { - condition = length(module.istio-beacon) == 1 - error_message = "Expected istio-beacon module to be deployed when mesh is enabled" - } - - assert { - condition = length(module.istio-ingress) == 1 - error_message = "Expected istio-ingress module to be deployed when mesh is enabled" - } - - assert { - condition = length(juju_integration.istio_beacon) == 7 - error_message = "Expected 7 istio_beacon integrations (one per component) when mesh is enabled" - } -} - -# --- mesh_enabled=true with internal_tls=true (default): validation error --- - -run "mesh_enabled_with_internal_tls_fails" { - command = plan - - variables { - mesh_enabled = true - internal_tls = true - } - - expect_failures = [var.mesh_enabled] -} diff --git a/terraform/cos-dev/tests/reverse_proxy.tftest.hcl b/terraform/cos-dev/tests/reverse_proxy.tftest.hcl new file mode 100644 index 00000000..f6f49d75 --- /dev/null +++ b/terraform/cos-dev/tests/reverse_proxy.tftest.hcl @@ -0,0 +1,67 @@ +mock_provider "juju" {} + +variables { model_uuid = "00000000-0000-0000-0000-000000000000" } + +# --- default: internal_tls enabled - ingress via traefik --- + +run "internal_tls_enabled" { + command = plan + + assert { + condition = length(module.ssc) == 1 + error_message = "Expected ssc module when internal_tls is enabled" + } + + assert { + condition = length(module.traefik) == 1 + error_message = "Expected traefik module when internal_tls is enabled" + } + + assert { + condition = length(juju_integration.ingress) == 4 + error_message = "Unexpected ingress integrations when internal_tls is enabled" + } + + assert { + condition = length(juju_integration.grafana_ingress) == 1 + error_message = "Unexpected grafana_ingress integrations when internal_tls is enabled" + } + + assert { + condition = length(juju_integration.traefik_route) == 2 + error_message = "Unexpected traefik_route integrations when internal_tls is enabled" + } +} + +# --- internal_tls disabled: no ingress via traefik --- + +run "internal_tls_disabled" { + command = plan + + variables { internal_tls = false } + + assert { + condition = length(module.ssc) == 0 + error_message = "Expected no self-signed-certificates module when internal_tls is disabled" + } + + assert { + condition = length(module.traefik) == 0 + error_message = "Expected no traefik module when internal_tls is disabled" + } + + assert { + condition = length(juju_integration.ingress) == 0 + error_message = "Unexpected ingress integrations when internal_tls is disabled" + } + + assert { + condition = length(juju_integration.grafana_ingress) == 0 + error_message = "Unexpected grafana_ingress integrations when internal_tls is disabled" + } + + assert { + condition = length(juju_integration.traefik_route) == 0 + error_message = "Unexpected traefik_route integrations when internal_tls is disabled" + } +} diff --git a/terraform/cos-dev/tests/revision_pin.tftest.hcl b/terraform/cos-dev/tests/revision_pin.tftest.hcl index 7cf5f32e..6e68c9ac 100644 --- a/terraform/cos-dev/tests/revision_pin.tftest.hcl +++ b/terraform/cos-dev/tests/revision_pin.tftest.hcl @@ -1,8 +1,6 @@ mock_provider "juju" {} -variables { - model_uuid = "00000000-0000-0000-0000-000000000000" -} +variables { model_uuid = "00000000-0000-0000-0000-000000000000" } # --- User revision pin is respected and not overridden by juju_charm datasource --- diff --git a/terraform/cos-dev/tests/topology_and_storage.tftest.hcl b/terraform/cos-dev/tests/topology_and_storage.tftest.hcl index ffb3b5c0..fd145fd6 100644 --- a/terraform/cos-dev/tests/topology_and_storage.tftest.hcl +++ b/terraform/cos-dev/tests/topology_and_storage.tftest.hcl @@ -1,8 +1,6 @@ mock_provider "juju" {} -variables { - model_uuid = "00000000-0000-0000-0000-000000000000" -} +variables { model_uuid = "00000000-0000-0000-0000-000000000000" } # --- Default: monolithic topology with seaweedfs --- @@ -214,9 +212,6 @@ run "monolithic_s3" { s3_endpoint = "https://s3.example.com" s3_access_key = "access-key" s3_secret_key = "secret-key" - loki_bucket = "loki" - mimir_bucket = "mimir" - tempo_bucket = "tempo" } assert { @@ -290,9 +285,6 @@ run "distributed_s3" { s3_endpoint = "https://s3.example.com" s3_access_key = "access-key" s3_secret_key = "secret-key" - loki_bucket = "loki" - mimir_bucket = "mimir" - tempo_bucket = "tempo" } assert { diff --git a/terraform/cos-dev/upgrades.tf b/terraform/cos-dev/upgrades.tf index 270c68f8..90c733ae 100644 --- a/terraform/cos-dev/upgrades.tf +++ b/terraform/cos-dev/upgrades.tf @@ -38,6 +38,18 @@ data "juju_charm" "grafana_info" { base = var.base } +data "juju_charm" "istio_beacon_info" { + charm = "istio-beacon-k8s" + channel = local.channels.istio_beacon + base = var.base +} + +data "juju_charm" "istio_ingress_info" { + charm = "istio-ingress-k8s" + channel = local.channels.istio_ingress + base = var.base +} + data "juju_charm" "loki_coordinator_info" { charm = "loki-coordinator-k8s" channel = local.channels.loki diff --git a/terraform/cos-dev/variables.tf b/terraform/cos-dev/variables.tf index 9c5c6de2..a54905aa 100644 --- a/terraform/cos-dev/variables.tf +++ b/terraform/cos-dev/variables.tf @@ -46,20 +46,20 @@ variable "storage_backend" { } } -# -------------- # Mesh configurations -------------- +# -------------- # Network configurations -------------- + +# TODO: If we name this one _enabled then we should also rename to internal_ca_enabled variable "mesh_enabled" { - description = "Specify whether to enable the service mesh or not." + description = "Configure the service mesh." type = bool default = false validation { - condition = !(var.mesh_enabled && var.internal_tls) + condition = !(var.internal_tls && var.mesh_enabled) error_message = "mesh_enabled and internal_tls cannot both be enabled at the same time." } } -# -------------- # TLS configurations -------------- - variable "internal_tls" { description = "Specify whether to use TLS or not for internal COS communication. By default, TLS is enabled using self-signed-certificates" type = bool @@ -142,18 +142,6 @@ variable "tempo_bucket" { default = "tempo" } -variable "s3_integrator" { - type = object({ - config = optional(map(string), {}) - constraints = optional(string, "arch=amd64") - revision = optional(number, null) - storage_directives = optional(map(string), {}) - units = optional(number, 1) - }) - default = {} - description = "Application configuration shared by all S3-integrators (one deployed per coordinated worker). For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application" -} - # -------------- # Application configurations -------------- variable "alertmanager" { @@ -334,6 +322,18 @@ variable "ssc" { description = "Application configuration for Self-signed-certificates. For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application" } +variable "s3_integrator" { + type = object({ + config = optional(map(string), {}) + constraints = optional(string, "arch=amd64") + revision = optional(number, null) + storage_directives = optional(map(string), {}) + units = optional(number, 1) + }) + default = {} + description = "Application configuration shared by all S3-integrators (one deployed per coordinated worker). For more details: https://registry.terraform.io/providers/juju/juju/latest/docs/resources/application" +} + variable "tempo_coordinator" { type = object({ app_name = optional(string, "tempo") diff --git a/terraform/cos-lite/README.md b/terraform/cos-lite/README.md index baef5cc0..51ac3541 100644 --- a/terraform/cos-lite/README.md +++ b/terraform/cos-lite/README.md @@ -51,20 +51,9 @@ This is a Terraform module facilitating the deployment of the COS Lite solution, ## Usage -### Using different Terraform Juju provider versions -If you require the Terraform Juju provider `< 1.0.0`, then deploy the COS Lite module with the `tf-provider-v0` tag: +The minimum version of Terraform Juju provider required is `1.5`. -```hcl -module "cos-lite" { - source = "git::https://github.com/canonical/observability-stack//terraform/cos-lite?ref=tf-provider-v0" -} -``` - -Otherwise, you can deploy from main (without `?ref`) which uses the Terraform Juju provider `~> 1.0`. See the [v1 migration documentation](https://documentation.ubuntu.com/terraform-provider-juju/v1/howto/manage-provider/upgrade-provider-to-v1/) if you need to upgrade your modules. - -### Basic usage - -To deploy the COS Lite solution in a model named `cos`, create this root module: +To deploy the COS Lite solution in a model named `cos-lite`, create this root module: ```hcl terraform { required_version = ">= 1.5" @@ -76,14 +65,14 @@ terraform { } } -resource "juju_model" "cos" { +resource "juju_model" "cos_lite" { name = "cos-lite" } module "cos-lite" { - source = "git::https://github.com/canonical/observability-stack//terraform/cos-lite?ref=track/2" - model_uuid = juju_model.cos.uuid - channel = "2/stable" + source = "git::https://github.com/canonical/observability-stack//terraform/cos-lite?ref=main" + model_uuid = juju_model.cos_lite.uuid + risk = "edge" } ``` diff --git a/tests/integration/cos/tls_external/test_upgrade_cos_tls_external.py b/tests/integration/cos/tls_external/test_upgrade_cos_tls_external.py index c360df87..33b46e17 100644 --- a/tests/integration/cos/tls_external/test_upgrade_cos_tls_external.py +++ b/tests/integration/cos/tls_external/test_upgrade_cos_tls_external.py @@ -8,12 +8,7 @@ from pathlib import Path import jubilant -from helpers import ( - catalogue_apps_are_reachable, - get_tls_context, - no_errors_in_otelcol_logs, - wait_for_active_idle_without_error, -) +from helpers import generic_assertions, no_errors_in_otelcol_logs TRACK_2_TF_FILE = Path(__file__).parent.resolve() / "track-2.tf" TRACK_DEV_TF_FILE = Path(__file__).parent.resolve() / "track-dev.tf" @@ -36,9 +31,7 @@ def test_deploy_from_track_2( # GIVEN a module deployed from track 2 tf_manager.init(TRACK_2_TF_FILE) tf_manager.apply(ca_model=ca_model.model, cos_model=cos_model.model, **S3_ENDPOINT) - wait_for_active_idle_without_error([cos_model], timeout=5400) - tls_ctx = get_tls_context(tmp_path, ca_model, "self-signed-certificates") - catalogue_apps_are_reachable(cos_model, tls_ctx) + generic_assertions(cos_model, ca_model, tmp_path) no_errors_in_otelcol_logs(cos_model) @@ -50,7 +43,5 @@ def test_deploy_to_track_dev( tf_manager.apply(ca_model=ca_model.model, cos_model=cos_model.model, **S3_ENDPOINT) # THEN the model is upgraded and is healthy - wait_for_active_idle_without_error([ca_model, cos_model]) - tls_ctx = get_tls_context(tmp_path, ca_model, "self-signed-certificates") - catalogue_apps_are_reachable(cos_model, tls_ctx) + generic_assertions(cos_model, ca_model, tmp_path) no_errors_in_otelcol_logs(cos_model) diff --git a/tests/integration/cos/tls_full/test_upgrade_cos_tls_full.py b/tests/integration/cos/tls_full/test_upgrade_cos_tls_full.py index a817a76b..9a48df79 100644 --- a/tests/integration/cos/tls_full/test_upgrade_cos_tls_full.py +++ b/tests/integration/cos/tls_full/test_upgrade_cos_tls_full.py @@ -8,12 +8,7 @@ from pathlib import Path import jubilant -from helpers import ( - catalogue_apps_are_reachable, - get_tls_context, - no_errors_in_otelcol_logs, - wait_for_active_idle_without_error, -) +from helpers import generic_assertions, no_errors_in_otelcol_logs TRACK_2_TF_FILE = Path(__file__).parent.resolve() / "track-2.tf" TRACK_DEV_TF_FILE = Path(__file__).parent.resolve() / "track-dev.tf" @@ -36,9 +31,7 @@ def test_deploy_from_track_2( # GIVEN a module deployed from track 2 tf_manager.init(TRACK_2_TF_FILE) tf_manager.apply(ca_model=ca_model.model, cos_model=cos_model.model, **S3_ENDPOINT) - wait_for_active_idle_without_error([cos_model], timeout=5400) - tls_ctx = get_tls_context(tmp_path, ca_model, "self-signed-certificates") - catalogue_apps_are_reachable(cos_model, tls_ctx) + generic_assertions(cos_model, ca_model, tmp_path) no_errors_in_otelcol_logs(cos_model) @@ -50,7 +43,5 @@ def test_deploy_to_track_dev( tf_manager.apply(ca_model=ca_model.model, cos_model=cos_model.model, **S3_ENDPOINT) # THEN the model is upgraded and is healthy - wait_for_active_idle_without_error([ca_model, cos_model]) - tls_ctx = get_tls_context(tmp_path, ca_model, "self-signed-certificates") - catalogue_apps_are_reachable(cos_model, tls_ctx) + generic_assertions(cos_model, ca_model, tmp_path) no_errors_in_otelcol_logs(cos_model) diff --git a/tests/integration/cos/tls_internal/test_upgrade_cos_tls_internal.py b/tests/integration/cos/tls_internal/test_upgrade_cos_tls_internal.py index d288c0e2..c0425362 100644 --- a/tests/integration/cos/tls_internal/test_upgrade_cos_tls_internal.py +++ b/tests/integration/cos/tls_internal/test_upgrade_cos_tls_internal.py @@ -8,11 +8,7 @@ from pathlib import Path import jubilant -from helpers import ( - catalogue_apps_are_reachable, - no_errors_in_otelcol_logs, - wait_for_active_idle_without_error, -) +from helpers import generic_assertions, no_errors_in_otelcol_logs TRACK_2_TF_FILE = Path(__file__).parent.resolve() / "track-2.tf" TRACK_DEV_TF_FILE = Path(__file__).parent.resolve() / "track-dev.tf" @@ -33,8 +29,7 @@ def test_deploy_from_track_2(tf_manager, cos_model: jubilant.Juju): # GIVEN a module deployed from track 2 tf_manager.init(TRACK_2_TF_FILE) tf_manager.apply(model=cos_model.model, **S3_ENDPOINT) - wait_for_active_idle_without_error([cos_model], timeout=5400) - catalogue_apps_are_reachable(cos_model) + generic_assertions(cos_model) no_errors_in_otelcol_logs(cos_model) @@ -44,6 +39,5 @@ def test_deploy_to_track_dev(tf_manager, cos_model: jubilant.Juju): tf_manager.apply(model=cos_model.model, **S3_ENDPOINT) # THEN the model is upgraded and is healthy - wait_for_active_idle_without_error([cos_model]) - catalogue_apps_are_reachable(cos_model) + generic_assertions(cos_model) no_errors_in_otelcol_logs(cos_model) diff --git a/tests/integration/cos/tls_none/test_upgrade_cos_tls_none.py b/tests/integration/cos/tls_none/test_upgrade_cos_tls_none.py index eaf985dd..7f33b146 100644 --- a/tests/integration/cos/tls_none/test_upgrade_cos_tls_none.py +++ b/tests/integration/cos/tls_none/test_upgrade_cos_tls_none.py @@ -8,11 +8,7 @@ from pathlib import Path import jubilant -from helpers import ( - catalogue_apps_are_reachable, - no_errors_in_otelcol_logs, - wait_for_active_idle_without_error, -) +from helpers import generic_assertions, no_errors_in_otelcol_logs TRACK_2_TF_FILE = Path(__file__).parent.resolve() / "track-2.tf" TRACK_DEV_TF_FILE = Path(__file__).parent.resolve() / "track-dev.tf" @@ -33,8 +29,7 @@ def test_deploy_from_track_2(tf_manager, cos_model: jubilant.Juju): # GIVEN a module deployed from track 2 tf_manager.init(TRACK_2_TF_FILE) tf_manager.apply(model=cos_model.model, **S3_ENDPOINT) - wait_for_active_idle_without_error([cos_model], timeout=5400) - catalogue_apps_are_reachable(cos_model) + generic_assertions(cos_model) no_errors_in_otelcol_logs(cos_model) @@ -44,6 +39,5 @@ def test_deploy_to_track_dev(tf_manager, cos_model: jubilant.Juju): tf_manager.apply(model=cos_model.model, **S3_ENDPOINT) # THEN the model is upgraded and is healthy - wait_for_active_idle_without_error([cos_model]) - catalogue_apps_are_reachable(cos_model) + generic_assertions(cos_model) no_errors_in_otelcol_logs(cos_model) diff --git a/tests/integration/cos_lite/tls_external/test_upgrade_cos_lite_tls_external.py b/tests/integration/cos_lite/tls_external/test_upgrade_cos_lite_tls_external.py index bded1290..46e5285a 100644 --- a/tests/integration/cos_lite/tls_external/test_upgrade_cos_lite_tls_external.py +++ b/tests/integration/cos_lite/tls_external/test_upgrade_cos_lite_tls_external.py @@ -7,11 +7,7 @@ from pathlib import Path import jubilant -from helpers import ( - catalogue_apps_are_reachable, - get_tls_context, - wait_for_active_idle_without_error, -) +from helpers import generic_assertions TRACK_2_TF_FILE = Path(__file__).parent.resolve() / "track-2.tf" TRACK_DEV_TF_FILE = Path(__file__).parent.resolve() / "track-dev.tf" @@ -23,9 +19,7 @@ def test_deploy_from_track_2( # GIVEN a module deployed from track 2 tf_manager.init(TRACK_2_TF_FILE) tf_manager.apply(ca_model=ca_model.model, cos_model=cos_model.model) - wait_for_active_idle_without_error([ca_model, cos_model], timeout=60 * 60) - tls_ctx = get_tls_context(tmp_path, ca_model, "self-signed-certificates") - catalogue_apps_are_reachable(cos_model, tls_ctx) + generic_assertions(cos_model, ca_model, tmp_path) def test_deploy_to_track_dev( @@ -36,6 +30,4 @@ def test_deploy_to_track_dev( tf_manager.apply(ca_model=ca_model.model, cos_model=cos_model.model) # THEN the model is upgraded and is healthy - wait_for_active_idle_without_error([ca_model, cos_model]) - tls_ctx = get_tls_context(tmp_path, ca_model, "self-signed-certificates") - catalogue_apps_are_reachable(cos_model, tls_ctx) + generic_assertions(cos_model, ca_model, tmp_path) diff --git a/tests/integration/cos_lite/tls_full/test_upgrade_cos_lite_tls_full.py b/tests/integration/cos_lite/tls_full/test_upgrade_cos_lite_tls_full.py index 0d742340..e834b153 100644 --- a/tests/integration/cos_lite/tls_full/test_upgrade_cos_lite_tls_full.py +++ b/tests/integration/cos_lite/tls_full/test_upgrade_cos_lite_tls_full.py @@ -7,11 +7,7 @@ from pathlib import Path import jubilant -from helpers import ( - catalogue_apps_are_reachable, - get_tls_context, - wait_for_active_idle_without_error, -) +from helpers import generic_assertions TRACK_2_TF_FILE = Path(__file__).parent.resolve() / "track-2.tf" TRACK_DEV_TF_FILE = Path(__file__).parent.resolve() / "track-dev.tf" @@ -23,9 +19,7 @@ def test_deploy_from_track_2( # GIVEN a module deployed from track 2 tf_manager.init(TRACK_2_TF_FILE) tf_manager.apply(ca_model=ca_model.model, cos_model=cos_model.model) - wait_for_active_idle_without_error([ca_model, cos_model], timeout=60 * 60) - tls_ctx = get_tls_context(tmp_path, ca_model, "self-signed-certificates") - catalogue_apps_are_reachable(cos_model, tls_ctx) + generic_assertions(cos_model, ca_model, tmp_path) def test_deploy_to_track_dev( @@ -36,6 +30,4 @@ def test_deploy_to_track_dev( tf_manager.apply(ca_model=ca_model.model, cos_model=cos_model.model) # THEN the model is upgraded and is healthy - wait_for_active_idle_without_error([ca_model, cos_model]) - tls_ctx = get_tls_context(tmp_path, ca_model, "self-signed-certificates") - catalogue_apps_are_reachable(cos_model, tls_ctx) + generic_assertions(cos_model, ca_model, tmp_path) diff --git a/tests/integration/cos_lite/tls_internal/test_upgrade_cos_lite_tls_internal.py b/tests/integration/cos_lite/tls_internal/test_upgrade_cos_lite_tls_internal.py index 9e49fea8..b5d91db6 100644 --- a/tests/integration/cos_lite/tls_internal/test_upgrade_cos_lite_tls_internal.py +++ b/tests/integration/cos_lite/tls_internal/test_upgrade_cos_lite_tls_internal.py @@ -7,7 +7,7 @@ from pathlib import Path import jubilant -from helpers import catalogue_apps_are_reachable, wait_for_active_idle_without_error +from helpers import generic_assertions TRACK_2_TF_FILE = Path(__file__).parent.resolve() / "track-2.tf" TRACK_DEV_TF_FILE = Path(__file__).parent.resolve() / "track-dev.tf" @@ -17,8 +17,7 @@ def test_deploy_from_track_2(tf_manager, cos_model: jubilant.Juju): # GIVEN a module deployed from track 2 tf_manager.init(TRACK_2_TF_FILE) tf_manager.apply(model=cos_model.model) - wait_for_active_idle_without_error([cos_model], timeout=60 * 60) - catalogue_apps_are_reachable(cos_model) + generic_assertions(cos_model) def test_deploy_to_track_dev(tf_manager, cos_model: jubilant.Juju): @@ -27,5 +26,4 @@ def test_deploy_to_track_dev(tf_manager, cos_model: jubilant.Juju): tf_manager.apply(model=cos_model.model) # THEN the model is upgraded and is healthy - wait_for_active_idle_without_error([cos_model]) - catalogue_apps_are_reachable(cos_model) + generic_assertions(cos_model) diff --git a/tests/integration/cos_lite/tls_none/test_upgrade_cos_lite_tls_none.py b/tests/integration/cos_lite/tls_none/test_upgrade_cos_lite_tls_none.py index f253fa9b..0d9eae1c 100644 --- a/tests/integration/cos_lite/tls_none/test_upgrade_cos_lite_tls_none.py +++ b/tests/integration/cos_lite/tls_none/test_upgrade_cos_lite_tls_none.py @@ -7,7 +7,7 @@ from pathlib import Path import jubilant -from helpers import catalogue_apps_are_reachable, wait_for_active_idle_without_error +from helpers import generic_assertions TRACK_2_TF_FILE = Path(__file__).parent.resolve() / "track-2.tf" TRACK_DEV_TF_FILE = Path(__file__).parent.resolve() / "track-dev.tf" @@ -17,8 +17,7 @@ def test_deploy_from_track_2(tf_manager, cos_model: jubilant.Juju): # GIVEN a module deployed from track 2 tf_manager.init(TRACK_2_TF_FILE) tf_manager.apply(model=cos_model.model) - wait_for_active_idle_without_error([cos_model], timeout=60 * 60) - catalogue_apps_are_reachable(cos_model) + generic_assertions(cos_model) def test_deploy_to_track_dev(tf_manager, cos_model: jubilant.Juju): @@ -27,5 +26,4 @@ def test_deploy_to_track_dev(tf_manager, cos_model: jubilant.Juju): tf_manager.apply(model=cos_model.model) # THEN the model is upgraded and is healthy - wait_for_active_idle_without_error([cos_model]) - catalogue_apps_are_reachable(cos_model) + generic_assertions(cos_model) diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index 4be9a10b..6c0f8ac9 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -43,6 +43,21 @@ def destroy(self, **kwargs): subprocess.run(shlex.split(cmd_str), check=True) +def generic_assertions( + cos_model: jubilant.Juju, + ca_model: jubilant.Juju | None = None, + temp_path: Path | None = None, +): + # generic assertions that are shared between products: cos, cos-lite + wait_for_active_idle_without_error([ca_model, cos_model], timeout=60 * 60) + if ca_model: + assert temp_path is not None, "temp_path is required when ca_model is provided" + tls_ctx = get_tls_context(temp_path, ca_model, "self-signed-certificates") + else: + tls_ctx = None + catalogue_apps_are_reachable(cos_model, tls_ctx) + + def wait_for_active_idle_without_error( jujus: List[jubilant.Juju], timeout: int = 60 * 45 ):