-
Notifications
You must be signed in to change notification settings - Fork 7
feat: Charmhub module for upgrades without revision pins #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
aa244a2
6a5e192
d8b0fbd
0634ec8
2c634d1
424447a
b3b6492
91dcf5f
188e4f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
| ``` | ||
MichaelThamm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| 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`! | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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/) | ||
| 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 | ||
| } | ||
| } | ||
| ``` |
| 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 | ||
| } | ||
MichaelThamm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| 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 | ||
| } | ||
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)