Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions docs/tutorial/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ or COS Lite, Juju-based observability stacks running on Kubernetes.

1. Deploy the observability stack <installation/index>

Refresh
=============

In this part of the tutorial you will learn how to refresh COS (or COS Lite) to a new channel using Terraform.

.. toctree::
:maxdepth: 1

2. Refresh COS to a new channel <refresh-product-module>

Configuration
=============

Expand All @@ -29,7 +39,7 @@ charm.
.. toctree::
:maxdepth: 1

2. Sync alert rules from Git <sync-alert-rules-from-git>
3. Sync alert rules from Git <sync-alert-rules-from-git>

Instrumentation
===============
Expand All @@ -41,7 +51,7 @@ application using the Grafana Agent machine charm.
.. toctree::
:maxdepth: 1

3. Instrument machine charms <instrument-machine-charms>
4. Instrument machine charms <instrument-machine-charms>


Redaction
Expand All @@ -52,4 +62,4 @@ By implementing a solid redaction strategy you can mitigate the risk of unwanted
.. toctree::
:maxdepth: 1

4. Redact sensitive data <redact-sensitive-data.md>
5. Redact sensitive data <redact-sensitive-data.md>
2 changes: 2 additions & 0 deletions docs/tutorial/installation/cos-lite-microk8s-sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ $ juju deploy cos-lite \
--overlay ./storage-small-overlay.yaml
```

(deploy-cos-ref)=
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Iirc we haven't started using these our docs yet.
Remind me - it's an anchor or cross-sphinx ref?
I think juju had this and it turned out to be brittle and difficult to maintain, but maybe I'm missing something.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an anchor, but it can also be used as a cross-sphinx/project ref for projects that have that enabled.

You haven't started using them in the COS docs yet, but it is the standard approach to cross-linking for sphinx docs projects, so at some point, you should change your links to use these refs. Juju uses them in their docs, but I don't know what conversations happened around it

Using ref targets/anchors is nicer because otherwise anytime you change the filename/path, it'll break any of those links in your docs.

IMO, it could go either way in this PR (add it or don't add it), but the future goal should be that all docs have a ref target, and you use those for linking instead of file path. (Copilot should be able to handle it well when you do make this initiative)


## 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:
Expand Down
2 changes: 2 additions & 0 deletions docs/tutorial/installation/cos-lite-microk8s-sandbox.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ terraform {
}
}

# before-cos

resource "juju_model" "cos" {
name = "cos"
config = { logging-config = "<root>=WARNING; unit=DEBUG" }
Expand Down
111 changes: 111 additions & 0 deletions docs/tutorial/refresh-product-module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Refresh COS to a new channel

In this example, you will learn how to deploy COS Lite and refresh from channel `2/stable` to `2/edge`. To do this, we can deploy COS Lite via Terraform in the same way as [in the tutorial](https://documentation.ubuntu.com/observability/track-2/tutorial/installation/cos-lite-microk8s-sandbox).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming this gets refactored into a tutorial, the language/framing should change so it's not like "In this example, you'll learn...". How-to guides aren't really "learning" experiences, it's more just like "here's the steps you need to do XYZ".

Example: Charmed Kafka: How to upgrade


## Prerequisites

This tutorial assumes that you already:

- Know how to deploy {ref}`COS Lite with Terraform <deploy-cos-ref>`

## Introduction
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to a comment I made above, this section can be less narrative if being refactored into a how-to guide

(btw I'm happy to meet and discuss my comments with you if you want!)


Imagine you have COS Lite (or COS) deployed on a specific channel like `2/stable` and want to
refresh to a different channel (or track) e.g., `2/edge`. To do so, an admin would have to manually
`juju refresh` each COS charm and address any refresh errors. Alternatively, they can determine the
correct charm `channel` and `revision`(s), update the Terraform module, and apply.

This is simplified within COS (and COS Lite) by mimicking the `juju refresh` behavior on a product
level, allowing the juju admin to specify a list of charms to refresh within the specified
`track/channel`. The rest is handled by Terraform.

## Update the COS Lite Terraform module

Once deployed, we can determine which charms to refresh with the `charms_to_refresh` input variable, detailed in the [README](https://github.com/canonical/observability-stack/tree/main/terraform/cos-lite). This defaults to: all charms owned by the `observability-team`.

```{note}
This tutorial assumed you have deployed COS Lite from a root module located at `./main.tf`.
```

Then, replace `2/stable` with `2/edge` in your `cos-lite` module within the existing `./main.tf` file:

```{literalinclude} /tutorial/installation/cos-lite-microk8s-sandbox.tf
---
language: hcl
start-after: "# before-cos"
---
```

```{note}
The `base` input variable for the `cos-lite` module is important if the `track/channel` deploys charms to a different base than the default, detailed in the [README](https://github.com/canonical/observability-stack/tree/main/terraform/cos-lite).
```

Finally, add the provider definitions into the same `./main.tf` file:

```hcl
terraform {
required_providers {
juju = {
source = "juju/juju"
version = "~> 1.0"
}
http = {
source = "hashicorp/http"
version = "~> 3.0"
}
}
}
```

At this point, you will have one `main.tf` file ready for deployment. Now you can plan these changes with:

```shell
terraform plan
```

and Terraform plans to update each charm to the latest revision in the `2/edge` channel:

```shell
Terraform used the selected providers to generate the following
execution plan. Resource actions are indicated with the following
symbols:
+ create
~ update in-place

Terraform will perform the following actions:

# module.cos.module.alertmanager.juju_application.alertmanager will be updated in-place
~ resource "juju_application" "alertmanager" {

# snip ...

~ charm {
~ channel = "2/stable" -> "2/edge"
name = "alertmanager-k8s"
~ revision = 191 -> 192
# (1 unchanged attribute hidden)
}

# snip ...

Plan: 0 to add, 5 to change, 0 to destroy.
```

and finally apply the changes with:

```shell
terraform apply
```

At this point, you will have successfully upgraded COS Lite from `2/stable` to `2/edge`!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The steps overall seem fine and pretty straightforward. I can provide some better feedback once it gets refactored into a how-to


## Refresh information

This tutorial only considers upgrading COS Lite. However, the `charmhub` module is product-agnostic
and can be used to refresh charms, and other products e.g., COS.

You can consult the follow release documentation for refresh compatibility:

- [how-to cross-track upgrade](/how-to/upgrade/)
- [release policy](/reference/release-policy/)
- [release notes](/reference/release-notes/)
89 changes: 89 additions & 0 deletions terraform/charmhub/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Terraform module for the COS solution

This Terraform module computes a charm’s latest revision (from a channel and base) using the CharmHub API.

<!-- BEGIN_TF_DOCS -->
## Providers

| Name | Version |
|------|---------|
| <a name="provider_http"></a> [http](#provider\_http) | ~> 3.0 |

## Modules

No modules.

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_architecture"></a> [architecture](#input\_architecture) | Architecture (e.g., amd64, arm64) | `string` | `"amd64"` | no |
| <a name="input_base"></a> [base](#input\_base) | Base Ubuntu (e.g., ubuntu@22.04, ubuntu@24.04) | `string` | n/a | yes |
| <a name="input_channel"></a> [channel](#input\_channel) | Channel name (e.g., 14/stable, 16/edge) | `string` | n/a | yes |
| <a name="input_charm"></a> [charm](#input\_charm) | Name of the charm (e.g., postgresql) | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_charm_revision"></a> [charm\_revision](#output\_charm\_revision) | The revision number for the specified charm channel and base |
<!-- END_TF_DOCS -->

## Usage

This example defines and provides multiple charm names to the `charmhubs` module. This module then
computes the latest revision in the specified channel e.g., `2/stable`. Finally, it creates
`juju_application.apps` with the computed revisions.

```hcl
terraform {
required_providers {
juju = {
source = "juju/juju"
}
http = {
source = "hashicorp/http"
version = "~> 3.0"
}
}
}

locals {
channel = "2/stable"
base = "ubuntu@24.04"

charms = {
alertmanager = "alertmanager-k8s"
prometheus = "prometheus-k8s"
grafana = "grafana-k8s"
}
}

module "charmhubs" {
source = "../charmhub"
for_each = local.charms

charm = each.value
channel = local.channel
base = local.base
architecture = "amd64"
}

resource "juju_model" "development" {
name = "development"
}

resource "juju_application" "apps" {
for_each = local.charms

model_uuid = juju_model.development.uuid
trust = true

charm {
name = each.value
channel = local.channel
revision = module.charmhubs[each.key].charm_revision
base = local.base
}
}
```
82 changes: 82 additions & 0 deletions terraform/charmhub/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
terraform {
required_providers {
http = {
source = "hashicorp/http"
version = "~> 3.0"
}
}
}

variable "charm" {
description = "Name of the charm (e.g., postgresql)"
type = string
}

variable "channel" {
description = "Channel name (e.g., 14/stable, 16/edge)"
type = string
}

variable "base" {
description = "Base Ubuntu (e.g., ubuntu@22.04, ubuntu@24.04)"
type = string
}

variable "architecture" {
description = "Architecture (e.g., amd64, arm64)"
type = string
default = "amd64"
}

data "http" "charmhub_info" {
url = "https://api.charmhub.io/v2/charms/info/${var.charm}?fields=channel-map.revision.revision"

request_headers = {
Accept = "application/json"
}

lifecycle {
postcondition {
condition = self.status_code == 200
error_message = "Failed to fetch charm info from Charmhub API"
}
}
}

locals {
charmhub_response = jsondecode(data.http.charmhub_info.response_body)
# base_version = split("@", var.base)[1]

matching_channels = [
for entry in local.charmhub_response["channel-map"] :
entry if(
entry.channel.name == var.channel &&

# TODO: I think we can ignore this base input if we assume that 24.04 is always dev/and track/2
# TODO: Capture all matching JSON bodies for channel & architecture. Then validate that it's only one. If not, the user should be warned that the base needs to be specified.
# E.g. you specify channel as 1/stable, but then base defaults to 24.04. This would fail bc 22.04 is for 1/stable

# TODO: Test that this works with the product to charm channel mapping like the revisions override I have
# curl "https://api.charmhub.io/v2/charms/info/alertmanager-k8s?fields=channel-map.revision.revision" | jq -r '.["channel-map"]

# entry.channel.base.channel == local.base_version &&
entry.channel.base.architecture == var.architecture
)
]

revision = length(local.matching_channels) > 0 ? local.matching_channels[0].revision.revision : null
}

check "revision_found" {
assert {
condition = local.revision != null
# TODO: Undo
# error_message = "No matching revision found for charm '${var.charm}' with channel '${var.channel}', base '${var.base}', and architecture '${var.architecture}'. Please verify the combination exists in Charmhub."
error_message = "No matching revision found for charm '${var.charm}' with channel '${var.channel}', and architecture '${var.architecture}'. Please verify the combination exists in Charmhub."
}
}

output "charm_revision" {
description = "The revision number for the specified charm channel and base"
value = local.revision
}
12 changes: 7 additions & 5 deletions terraform/cos-lite/applications.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ module "alertmanager" {
config = var.alertmanager.config
constraints = var.alertmanager.constraints
model_uuid = var.model_uuid
revision = var.alertmanager.revision
revision = local.alertmanager_revision
storage_directives = var.alertmanager.storage_directives
units = var.alertmanager.units

# TODO: Add validation or wrap this in a local
}

module "catalogue" {
Expand All @@ -17,7 +19,7 @@ module "catalogue" {
config = var.catalogue.config
constraints = var.catalogue.constraints
model_uuid = var.model_uuid
revision = var.catalogue.revision
revision = local.catalogue_revision
storage_directives = var.catalogue.storage_directives
units = var.catalogue.units
}
Expand All @@ -29,7 +31,7 @@ module "grafana" {
config = var.grafana.config
constraints = var.grafana.constraints
model_uuid = var.model_uuid
revision = var.grafana.revision
revision = local.grafana_revision
storage_directives = var.grafana.storage_directives
units = var.grafana.units
}
Expand All @@ -42,7 +44,7 @@ module "loki" {
constraints = var.loki.constraints
model_uuid = var.model_uuid
storage_directives = var.loki.storage_directives
revision = var.loki.revision
revision = local.loki_revision
units = var.loki.units
}

Expand All @@ -54,7 +56,7 @@ module "prometheus" {
constraints = var.prometheus.constraints
model_uuid = var.model_uuid
storage_directives = var.prometheus.storage_directives
revision = var.prometheus.revision
revision = local.prometheus_revision
units = var.prometheus.units
}

Expand Down
Loading
Loading