From 1fa589cda715971fba071a447169fb9271a20224 Mon Sep 17 00:00:00 2001 From: phvalguima Date: Fri, 22 Nov 2024 14:46:21 +0100 Subject: [PATCH] [DPE-5866] Add Charm Terraform Module (#501) This PR extends our current charm to add a terraform module for the charm. It also adds github workflow to test + a basic test setup. --- .github/workflows/ci.yaml | 53 ++++++++++++++++++++++ .gitignore | 47 +++++++++++++++++++ terraform/README.md | 66 +++++++++++++++++++++++++++ terraform/main.tf | 41 +++++++++++++++++ terraform/outputs.tf | 41 +++++++++++++++++ terraform/tests/preamble.tf | 18 ++++++++ terraform/tests/providers.tf | 16 +++++++ terraform/tests/simple_deployment.tf | 39 ++++++++++++++++ terraform/tests/variables.tf | 16 +++++++ terraform/variables.tf | 67 ++++++++++++++++++++++++++++ terraform/versions.tf | 12 +++++ 11 files changed, 416 insertions(+) create mode 100644 terraform/README.md create mode 100644 terraform/main.tf create mode 100644 terraform/outputs.tf create mode 100644 terraform/tests/preamble.tf create mode 100644 terraform/tests/providers.tf create mode 100644 terraform/tests/simple_deployment.tf create mode 100644 terraform/tests/variables.tf create mode 100644 terraform/variables.tf create mode 100644 terraform/versions.tf diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 57f3821ab..581a1054a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,6 +32,59 @@ jobs: - name: Run tests run: tox run -e unit + terraform-test: + name: Terraform - Lint and Simple Deployment + runs-on: ubuntu-latest + timeout-minutes: 120 + steps: + - name: Checkout repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: lint charm module + run: | + pushd ./terraform + terraform init + terraform fmt + terraform validate + + pushd ./tests + terraform init + terraform fmt + terraform validate + popd + popd + + - name: run checks - prepare + run: | + sudo snap install juju --channel=3.6/beta --classic + sudo snap install juju-wait --channel=latest/stable --classic + sudo snap install jq + + - name: LXD setup + run: | + sudo snap refresh lxd --channel=latest/stable + sudo adduser "$USER" 'lxd' + # `newgrp` does not work in GitHub Actions; use `sg` instead + sg 'lxd' -c "lxd waitready" + sg 'lxd' -c "lxd init --auto" + sg 'lxd' -c "lxc network set lxdbr0 ipv6.address none" + sudo iptables -F FORWARD + sudo iptables -P FORWARD ACCEPT + + - name: Juju setup + run: | + sg 'lxd' -c "juju bootstrap 'localhost' --config model-logs-size=10G" + juju model-defaults logging-config='=INFO; unit=DEBUG' + juju add-model test + + - name: Terraform deploy + run: | + pushd ./terraform/tests/ + TF_VAR_model_name="test" terraform apply -target null_resource.simple_deployment_juju_wait_deployment -auto-approve + popd + lib-check: name: Check libraries diff --git a/.gitignore b/.gitignore index 51ccad860..41e332957 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,50 @@ cloudinit-userdata.yaml # Moving to Poetry, we do not need this file to be pushed any longer /requirements.txt /requirements-last-build.txt + +######################################################## +# +# Terraform .gitignore +# +######################################################## + + +# Local .terraform directories +**/.terraform/* +*.terraform.lock.hcl + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log +crash.*.log + +# Generated files +*.key +credentials* + +# Exclude all .tfvars files, which are likely to contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +*.tfvars +*.tfvars.json + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/terraform/README.md b/terraform/README.md new file mode 100644 index 000000000..47fa9164f --- /dev/null +++ b/terraform/README.md @@ -0,0 +1,66 @@ +# Terraform module for opensearch-operator + +This is a Terraform module facilitating the deployment of the OpenSearch charm with [Terraform juju provider](https://github.com/juju/terraform-provider-juju/). For more information, refer to the provider [documentation](https://registry.terraform.io/providers/juju/juju/latest/docs). + +## Requirements +This module requires a `juju` model to be available. Refer to the [usage section](#usage) below for more details. + +## API + +### Inputs +The module offers the following configurable inputs: + +| Name | Type | Description | Required | +| - | - | - | - | +| `app_name`| string | Application name | False | +| `channel`| string | Channel that the charm is deployed from | False | +| `base`| string | The series to be used for this charm | False | +| `config`| map(string) | Map of the charm configuration options | False | +| `model_name`| string | Name of the model that the charm is deployed on | True | +| `resources`| map(string) | Map of the charm resources | False | +| `revision`| number | Revision number of the charm name | False | +| `units`| number | Number of units to be deployed | False | +| `constraints`| string | Machine constraints for the charm | False | +| `storage`| map(string) | Storage description, must follow the juju provider schema | False | + + +### Outputs +Upon applied, the module exports the following outputs: + +| Name | Description | +| - | - | +| `app_name`| Application name | +| `provides`| Map of `provides` endpoints | +| `requires`| Map of `requires` endpoints | + +## Usage + +This module is intended to be used as part of a higher-level module. When defining one, users should ensure that Terraform is aware of the `juju_model` dependency of the charm module. There are two options to do so when creating a high-level module: + +### Define a `juju_model` resource +Define a `juju_model` resource and pass to the `model_name` input a reference to the `juju_model` resource's name. For example: + +``` +resource "juju_model" "opensearch" { + name = opensearch +} + +module "opensearch-operator" { + source = "" + model_name = juju_model.opensearch.name +} +``` + +### Define a `data` source +Define a `data` source and pass to the `model_name` input a reference to the `data.juju_model` resource's name. This will enable Terraform to look for a `juju_model` resource with a name attribute equal to the one provided, and apply only if this is present. Otherwise, it will fail before applying anything. + +``` +data "juju_model" "opensearch" { + name = var.model_name +} + +module "opensearch" { + source = "" + model_name = data.juju_model.opensearch.name +} +``` diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 000000000..9a27a8936 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,41 @@ +# Copyright 2024 Canonical Ltd. +# See LICENSE file for licensing details. + +resource "juju_application" "opensearch" { + + charm { + name = "opensearch" + channel = var.channel + revision = var.revision + base = var.base + } + config = var.config + model = var.model + name = var.app_name + units = var.units + constraints = var.constraints + + + # TODO: uncomment once final fixes have been added for: + # Error: juju/terraform-provider-juju#443, juju/terraform-provider-juju#182 + # placement = join(",", var.machines) + + endpoint_bindings = [ + for k, v in var.endpoint_bindings : { + endpoint = k, space = v + } + ] + + storage_directives = var.storage + + lifecycle { + precondition { + condition = length(var.machines) == 0 || length(var.machines) == var.units + error_message = "Machine count does not match unit count" + } + precondition { + condition = length(var.storage) == 0 || lookup(var.storage, "count", 0) <= 1 + error_message = "Only one storage is supported" + } + } +} diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 000000000..bdc32fc18 --- /dev/null +++ b/terraform/outputs.tf @@ -0,0 +1,41 @@ +# Copyright 2024 Canonical Ltd. +# See LICENSE file for licensing details. + +output "app_name" { + description = "Name of the deployed application." + value = juju_application.opensearch.name +} + +# Required integration endpoints + +output "certificates_endpoint" { + description = "Name of the endpoint used to integrate with the TLS certificates provider." + value = "certificates" +} + +output "peer_cluster_endpoint" { + description = "Name of the endpoint used to connect with the peer-cluster." + value = "peer-cluster" +} + +output "s3_credentials_endpoint" { + description = "Name of the endpoint used to provide s3 support for backups." + value = "s3-credentials" +} + +# Provided integration endpoints + +output "peer_cluster_orchestrator_endpoint" { + description = "Name of the peer cluster orchestrator endpoint." + value = "peer-cluster-orchestrator" +} + +output "opensearch_client_endpoint" { + description = "Name of the endpoint opensearch-client endpoint." + value = "opensearch-client" +} + +output "cos_agent_endpoint" { + description = "Name of the endpoint used to provide COS agent integration." + value = "cos-agent-endpoint" +} diff --git a/terraform/tests/preamble.tf b/terraform/tests/preamble.tf new file mode 100644 index 000000000..69074f9be --- /dev/null +++ b/terraform/tests/preamble.tf @@ -0,0 +1,18 @@ +resource "null_resource" "preamble" { + provisioner "local-exec" { + command = <<-EOT + sudo snap install juju-wait --classic || true + sudo sysctl -w vm.max_map_count=262144 vm.swappiness=0 net.ipv4.tcp_retries2=5 + EOT + } + +} + +resource "juju_application" "self-signed-certificates" { + charm { + name = "self-signed-certificates" + channel = "latest/stable" + } + model = var.model_name + depends_on = [null_resource.preamble] +} diff --git a/terraform/tests/providers.tf b/terraform/tests/providers.tf new file mode 100644 index 000000000..3be3d8566 --- /dev/null +++ b/terraform/tests/providers.tf @@ -0,0 +1,16 @@ +terraform { + required_providers { + juju = { + source = "juju/juju" + version = "~> 0.14.0" + } + http = { + source = "hashicorp/http" + version = "~> 3.4.5" + } + external = { + source = "hashicorp/external" + version = "~> 2.3.4" + } + } +} diff --git a/terraform/tests/simple_deployment.tf b/terraform/tests/simple_deployment.tf new file mode 100644 index 000000000..be461085c --- /dev/null +++ b/terraform/tests/simple_deployment.tf @@ -0,0 +1,39 @@ +module "opensearch" { + source = "../" + app_name = var.app_name + model = var.model_name + units = var.simple_opensearch_units + config = { + profile = "testing" + } + + channel = "2/edge" + + depends_on = [juju_application.self-signed-certificates] +} + +resource "juju_integration" "simple_deployment_tls-operator_opensearch-integration" { + model = var.model_name + + application { + name = juju_application.self-signed-certificates.name + } + application { + name = var.app_name + } + depends_on = [ + juju_application.self-signed-certificates, + module.opensearch + ] + +} + +resource "null_resource" "simple_deployment_juju_wait_deployment" { + provisioner "local-exec" { + command = <<-EOT + juju-wait -v --model ${var.model_name} + EOT + } + + depends_on = [juju_integration.simple_deployment_tls-operator_opensearch-integration] +} diff --git a/terraform/tests/variables.tf b/terraform/tests/variables.tf new file mode 100644 index 000000000..9f4170a07 --- /dev/null +++ b/terraform/tests/variables.tf @@ -0,0 +1,16 @@ +variable "model_name" { + description = "Model name" + type = string +} + +variable "app_name" { + description = "OpenSearch app name" + type = string + default = "opensearch" +} + +variable "simple_opensearch_units" { + description = "Node count" + type = number + default = 1 +} diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 000000000..7d374bf9f --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,67 @@ +# Copyright 2024 Canonical Ltd. +# See LICENSE file for licensing details. + +variable "app_name" { + description = "Application name" + type = string + default = "opensearch" +} + +variable "channel" { + description = "Charm channel" + type = string + default = "2/stable" +} + +variable "base" { + description = "Charm base (old name: series)" + type = string + default = "ubuntu@22.04" +} + +variable "config" { + description = "Map of charm configuration options" + type = map(string) + default = {} +} + +variable "model" { + description = "Model name" + type = string +} + +variable "revision" { + description = "Charm revision" + type = number + default = null +} + +variable "units" { + description = "Charm units" + type = number + default = 3 +} + +variable "constraints" { + description = "String listing constraints for this application" + type = string + default = "arch=amd64" +} + +variable "machines" { + description = "List of machines for placement" + type = list(string) + default = [] +} + +variable "storage" { + description = "Map of storage used by the application" + type = map(string) + default = {} +} + +variable "endpoint_bindings" { + description = "Map of endpoint bindings" + type = map(string) + default = {} +} diff --git a/terraform/versions.tf b/terraform/versions.tf new file mode 100644 index 000000000..e25f43836 --- /dev/null +++ b/terraform/versions.tf @@ -0,0 +1,12 @@ +# Copyright 2024 Canonical Ltd. +# See LICENSE file for licensing details. + +terraform { + required_version = ">= 1.6" + required_providers { + juju = { + source = "juju/juju" + version = "~> 0.14.0" + } + } +}