diff --git a/README.md b/README.md index 71910006..832759f9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ # Google Cloud Memorystore Terraform Module [terraform registry](https://registry.terraform.io/modules/terraform-google-modules/memorystore/google/) -A Terraform module for creating a fully functional Google Memorystore Redis instance. For Memcache and Redis Cluster see [sub-modules](./modules/) +A Terraform module for creating a fully functional Google Memorystore Redis instance. For other memory store engine use [sub-modules](https://github.com/terraform-google-modules/terraform-google-memorystore/tree/master/modules). + +- Memcache [sub-modules](https://github.com/terraform-google-modules/terraform-google-memorystore/tree/master/modules/memcache) +- Redis Cluster [sub-modules](https://github.com/terraform-google-modules/terraform-google-memorystore/tree/master/modules/redis-cluster) +- Valkey [sub-modules](https://github.com/terraform-google-modules/terraform-google-memorystore/tree/master/modules/valkey) ## Compatibility This module is meant for use with Terraform 1.3+ and tested using Terraform 1.3+. If you find incompatibilities using Terraform >=1.3, please open an issue. @@ -19,7 +23,7 @@ Current version is 11.0. Upgrade guides: ## Usage -Check the [examples/](./examples/) directory for more. +Check the [examples/](https://github.com/terraform-google-modules/terraform-google-memorystore/tree/master/examples) directory for more. ```hcl module "memorystore" { diff --git a/build/int.cloudbuild.yaml b/build/int.cloudbuild.yaml index 237d7d77..dd42ddd3 100644 --- a/build/int.cloudbuild.yaml +++ b/build/int.cloudbuild.yaml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -timeout: 4200s +timeout: 5400s steps: - id: swap-module-refs name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' @@ -27,15 +27,61 @@ steps: - id: create name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' args: ['/bin/bash', '-c', 'cft test run all --stage init --verbose --test-dir test/integration'] - - id: converge + - id: converge-TestMinimalModule + waitFor: + - create name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' - args: ['/bin/bash', '-c', 'cft test run all --stage apply --verbose --test-dir test/integration'] - - id: verify + args: ['/bin/bash', '-c', 'cft test run TestMinimalModule --stage apply --verbose --test-dir test/integration'] + - id: verify-TestMinimalModule name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' - args: ['/bin/bash', '-c', 'cft test run all --stage verify --verbose --test-dir test/integration'] - - id: destroy + args: ['/bin/bash', '-c', 'cft test run TestMinimalModule --stage verify --verbose --test-dir test/integration'] + - id: destroy-TestMinimalModule name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' - args: ['/bin/bash', '-c', 'cft test run all --stage teardown --verbose --test-dir test/integration'] + args: ['/bin/bash', '-c', 'cft test run TestMinimalModule --stage teardown --verbose --test-dir test/integration'] + - id: converge-TestMemcache + waitFor: + - create + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestMemcache --stage apply --verbose --test-dir test/integration'] + - id: verify-TestMemcache + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestMemcache --stage verify --verbose --test-dir test/integration'] + - id: destroy-TestMemcache + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestMemcache --stage teardown --verbose --test-dir test/integration'] + - id: converge-TestRedis + waitFor: + - create + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestRedis --stage apply --verbose --test-dir test/integration'] + - id: verify-TestRedis + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestRedis --stage verify --verbose --test-dir test/integration'] + - id: destroy-TestRedis + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestRedis --stage teardown --verbose --test-dir test/integration'] + - id: converge-TestRedisCluster + waitFor: + - create + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestRedisCluster --stage apply --verbose --test-dir test/integration'] + - id: verify-TestRedisCluster + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestRedisCluster --stage verify --verbose --test-dir test/integration'] + - id: destroy-TestRedisCluster + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestRedisCluster --stage teardown --verbose --test-dir test/integration'] + - id: converge-TestValkeyCluster + waitFor: + - destroy-TestRedisCluster + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestValkeyCluster --stage apply --verbose --test-dir test/integration'] + - id: verify-TestValkeyCluster + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestValkeyCluster --stage verify --verbose --test-dir test/integration'] + - id: destroy-TestValkeyCluster + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestValkeyCluster --stage teardown --verbose --test-dir test/integration'] tags: - 'ci' - 'integration' diff --git a/examples/redis-cluster/main.tf b/examples/redis-cluster/main.tf index fb6c01d1..1d1a74de 100644 --- a/examples/redis-cluster/main.tf +++ b/examples/redis-cluster/main.tf @@ -61,5 +61,9 @@ module "redis_cluster" { maxmemory-policy = "volatile-ttl" } - depends_on = [module.test_vpc] + depends_on = [ + module.test_vpc, + module.enable_apis, + google_project_iam_member.network_connectivity_sa, + ] } diff --git a/examples/valkey/README.md b/examples/valkey/README.md new file mode 100644 index 00000000..da420d56 --- /dev/null +++ b/examples/valkey/README.md @@ -0,0 +1,27 @@ +# Valkey Test + +This test will create a new valkey cluster. + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| project\_id | Google cloud project id to create valkey cluster. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| authorization\_mode | The valkey cluster authorization mode | +| cluster | The valkey cluster created | +| cluster\_id | The valkey cluster instance ID | +| cluster\_name | The valkey cluster name | +| cluster\_region | The valkey cluster region | +| node\_type | The valkey cluster node type | +| replica\_count | The valkey cluster replica count | +| shard\_count | The valkey cluster shard count | +| size\_gb | The valkey cluster size | +| transit\_encryption\_mode | The valkey cluster transit encryption mode | + + diff --git a/examples/valkey/iam.tf b/examples/valkey/iam.tf new file mode 100644 index 00000000..91e1dfb8 --- /dev/null +++ b/examples/valkey/iam.tf @@ -0,0 +1,31 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +## Enable Service Identity and assign Network Connectivity Service Agent role +## https://cloud.google.com/vpc/docs/configure-service-connection-policies#configure-service-project + +resource "google_project_service_identity" "network_connectivity_sa" { + provider = google-beta + + project = var.project_id + service = "networkconnectivity.googleapis.com" +} + +resource "google_project_iam_member" "network_connectivity_sa" { + project = var.project_id + role = "roles/networkconnectivity.serviceAgent" + member = "serviceAccount:${google_project_service_identity.network_connectivity_sa.email}" +} diff --git a/examples/valkey/main.tf b/examples/valkey/main.tf new file mode 100644 index 00000000..b21809a6 --- /dev/null +++ b/examples/valkey/main.tf @@ -0,0 +1,76 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "enable_apis" { + source = "terraform-google-modules/project-factory/google//modules/project_services" + version = "~> 17.0" + + project_id = var.project_id + enable_apis = true + + disable_services_on_destroy = false + disable_dependent_services = false + + activate_apis = [ + "memorystore.googleapis.com", + "serviceconsumermanagement.googleapis.com", + "networkconnectivity.googleapis.com", + "compute.googleapis.com", + ] +} + + +module "valkey_cluster" { + source = "terraform-google-modules/memorystore/google//modules/valkey" + version = "~> 11.0" + + + instance_id = "test-valkey-cluster" + project_id = var.project_id + location = "us-central1" + node_type = "HIGHMEM_MEDIUM" + deletion_protection_enabled = false + engine_version = "VALKEY_8_0" + + network = local.network_name + + service_connection_policies = { + test-net-valkey-cluster-scp = { + subnet_names = [ + "valkey-subnet-100", + "valkey-subnet-101", + ] + } + } + + persistence_config = { + mode = "RDB" + rdb_config = { + rdb_snapshot_period = "ONE_HOUR" + rdb_snapshot_start_time = "2024-10-02T15:01:23Z" + } + } + + engine_configs = { + maxmemory-policy = "volatile-ttl" + } + + depends_on = [ + module.test_vpc, + module.enable_apis, + google_project_iam_member.network_connectivity_sa, + ] +} diff --git a/examples/valkey/network.tf b/examples/valkey/network.tf new file mode 100644 index 00000000..3df6d6a0 --- /dev/null +++ b/examples/valkey/network.tf @@ -0,0 +1,45 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + network_name = "test-valkey-network" +} + +module "test_vpc" { + source = "terraform-google-modules/network/google" + version = "~> 9.2" + project_id = var.project_id + network_name = local.network_name + mtu = 1460 + + subnets = [ + { + subnet_name = "valkey-subnet-100" + subnet_ip = "10.10.100.0/24" + subnet_region = "us-central1" + }, + { + subnet_name = "valkey-subnet-101" + subnet_ip = "10.10.101.0/24" + subnet_region = "us-central1" + }, + { + subnet_name = "valkey-subnet-102" + subnet_ip = "10.10.102.0/24" + subnet_region = "us-east1" + }, + ] +} diff --git a/examples/valkey/outputs.tf b/examples/valkey/outputs.tf new file mode 100644 index 00000000..821e13c5 --- /dev/null +++ b/examples/valkey/outputs.tf @@ -0,0 +1,65 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "cluster_id" { + description = "The valkey cluster instance ID" + value = module.valkey_cluster.id +} + +output "size_gb" { + description = "The valkey cluster size" + value = module.valkey_cluster.valkey_cluster.node_config[0].size_gb +} + +output "cluster_region" { + description = "The valkey cluster region" + value = module.valkey_cluster.valkey_cluster.location +} + +output "replica_count" { + description = "The valkey cluster replica count" + value = module.valkey_cluster.valkey_cluster.replica_count +} + +output "transit_encryption_mode" { + description = "The valkey cluster transit encryption mode" + value = module.valkey_cluster.valkey_cluster.transit_encryption_mode +} + +output "cluster_name" { + description = "The valkey cluster name" + value = module.valkey_cluster.valkey_cluster.instance_id +} + +output "shard_count" { + description = "The valkey cluster shard count" + value = module.valkey_cluster.valkey_cluster.shard_count +} + +output "authorization_mode" { + description = "The valkey cluster authorization mode" + value = module.valkey_cluster.valkey_cluster.authorization_mode +} + +output "node_type" { + description = "The valkey cluster node type" + value = module.valkey_cluster.valkey_cluster.node_type +} + +output "cluster" { + description = "The valkey cluster created" + value = module.valkey_cluster +} diff --git a/examples/valkey/variables.tf b/examples/valkey/variables.tf new file mode 100644 index 00000000..48bb81fd --- /dev/null +++ b/examples/valkey/variables.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "project_id" { + description = "Google cloud project id to create valkey cluster." + type = string +} diff --git a/modules/valkey/README.md b/modules/valkey/README.md new file mode 100644 index 00000000..4708f3e4 --- /dev/null +++ b/modules/valkey/README.md @@ -0,0 +1,100 @@ +# Memorystore Valkey Terraform Module + +A Terraform module for creating Google [Memorystore for Valkey](https://cloud.google.com/memorystore/docs/valkey/product-overview). It can also create [service connection policies](https://cloud.google.com/vpc/docs/about-service-connection-policies). You can also create service connection policy outside of this module. If you are not creating service connection policy as part of this module then make sure they exist before creating valkey cluster. You can find more details [here](https://cloud.google.com/memorystore/docs/valkey/networking) + +## Compatibility +This module is meant for use with Terraform 1.3+ and tested using Terraform 1.3+. If you find incompatibilities using Terraform >=1.3, please open an issue. + +## Usage + +``` +module "valkey_cluster" { + source = "terraform-google-modules/memorystore/google//modules/valkey" + version = "~> 11.0" + + instance_id = "test-valkey-cluster" + project = var.project_id + location = "us-central1" + node_type = "HIGHMEM_MEDIUM" + deletion_protection_enabled = false + engine_version = "VALKEY_8_0" + + network = "valkey-network" + + service_connection_policies = { + test-net-valkey-cluster-scp = { + subnet_names = [ + "valkey-subnet-100", + "valkey-subnet-101", + ] + } + } + + engine_configs = { + maxmemory-policy = "volatile-ttl" + } +} +``` + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| authorization\_mode | The Immutable. Authorization mode of the instance. Possible values: AUTH\_DISABLED IAM\_AUTH | `string` | `"AUTH_DISABLED"` | no | +| deletion\_protection\_enabled | If set to true deletion of the instance will fail | `bool` | `true` | no | +| enable\_apis | Flag for enabling memcache.googleapis.com in your project | `bool` | `false` | no | +| engine\_configs | User-provided engine configurations for the instance |
object({
maxmemory = optional(string)
maxmemory-clients = optional(string)
maxmemory-policy = optional(string)
notify-keyspace-events = optional(string)
slowlog-log-slower-than = optional(number)
maxclients = optional(number)
})
| `null` | no | +| engine\_version | Immutable. Engine version of the instance | `string` | `"VALKEY_8_0"` | no | +| instance\_id | The ID to use for the instance, which will become the final component of the instance's resource name. Must be 4-63 characters in length with lowercase letters, digits, and hyphens. Must not end with a hyphen. Must be unique within a location | `string` | n/a | yes | +| labels | The resource labels to represent user provided metadata. | `map(string)` | `{}` | no | +| location | The region where valkey cluster will be created | `string` | n/a | yes | +| network | Name of the consumer network where the network address of the discovery endpoint will be reserved | `string` | n/a | yes | +| network\_project | project ID of the consumer network where the network address of the discovery endpoint will be reserved. Required for Shared VPC host | `string` | `null` | no | +| node\_type | The nodeType for the valkey cluster. Possible values are: SHARED\_CORE\_NANO, HIGHMEM\_MEDIUM, HIGHMEM\_XLARGE, STANDARD\_SMALL | `string` | `null` | no | +| persistence\_config | User-provided persistence configurations for the instance |
object({
mode = optional(string)
rdb_config = optional(object({
rdb_snapshot_period = optional(string)
rdb_snapshot_start_time = optional(string)
}), null)
aof_config = optional(object({
append_fsync = string
}), null)
})
| `{}` | no | +| project\_id | The ID of the project in which the resource belongs to. | `string` | n/a | yes | +| replica\_count | Number of replica nodes per shard. If omitted the default is 0 replicas | `number` | `0` | no | +| service\_connection\_policies | The Service Connection Policies to create. Required to create service connection policy. Not needed if service connection policy already exist |
map(object({
subnet_names = list(string)
description = optional(string)
limit = optional(number)
labels = optional(map(string), {})
}))
| `{}` | no | +| shard\_count | Number of shards for the instance | `number` | `3` | no | +| transit\_encryption\_mode | Immutable. In-transit encryption mode of the instance. Possible values: TRANSIT\_ENCRYPTION\_DISABLED SERVER\_AUTHENTICATION | `string` | `"TRANSIT_ENCRYPTION_DISABLED"` | no | +| zone\_distribution\_config\_mode | The mode for zone distribution for Memorystore valkey cluster (Immutable). If not provided, MULTI\_ZONE will be used as default value. Possible values are: MULTI\_ZONE, SINGLE\_ZONE | `string` | `"MULTI_ZONE"` | no | +| zone\_distribution\_config\_zone | The zone for single zone Memorystore valkey cluster (Immutable) | `string` | `null` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| discovery\_endpoints | Endpoints created on each given network, for valkey clients to connect to the cluster. Currently only one endpoint is supported | +| id | The valkey cluster instance ID | +| psc\_connections | PSC connections for discovery of the cluster topology and accessing the cluster | +| valkey\_cluster | The valkey cluster created | + + + +## Requirements + +These sections describe requirements for using this module. + +### Software + +The following dependencies must be available: + +- [Terraform][terraform] v1.3+ +- [Terraform Provider for GCP][terraform-provider-gcp] plugin v6.3+ + +### Service Account + +Following roles contain permissions to deploy resource. + +- Cloud Memorystore valkey Admin: `roles/memorystore.admin` +- Compute Network Admin: `roles/compute.networkAdmin` + +### Enable API's +In order to operate with the Service Account you must activate the following API on the project where the Service Account was created: + +- Memorystore for valkey API - `memorystore.googleapis.com` +- Service Consumer Management API - `serviceconsumermanagement.googleapis.com` +- Network Connectivity API - `networkconnectivity.googleapis.com` +- Compute Engine API - `compute.googleapis.com` + diff --git a/modules/valkey/main.tf b/modules/valkey/main.tf new file mode 100644 index 00000000..4d6ebc9f --- /dev/null +++ b/modules/valkey/main.tf @@ -0,0 +1,106 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_memorystore_instance" "valkey_cluster" { + provider = google-beta + project = var.project_id + instance_id = var.instance_id + shard_count = var.shard_count + + desired_psc_auto_connections { + network = "projects/${coalesce(var.network_project, var.project_id)}/global/networks/${var.network}" + project_id = var.project_id + } + + location = var.location + replica_count = var.replica_count + node_type = var.node_type + transit_encryption_mode = var.transit_encryption_mode + authorization_mode = var.authorization_mode + engine_configs = var.engine_configs + + dynamic "zone_distribution_config" { + for_each = var.zone_distribution_config_mode != null ? ["zone_distribution_config"] : [] + content { + mode = var.zone_distribution_config_mode + zone = var.zone_distribution_config_mode == "SINGLE_ZONE" ? var.zone_distribution_config_zone : null + } + } + + engine_version = var.engine_version + deletion_protection_enabled = var.deletion_protection_enabled + + dynamic "persistence_config" { + for_each = var.persistence_config != null ? ["persistence_config"] : [] + content { + mode = var.persistence_config.mode + dynamic "rdb_config" { + for_each = var.persistence_config.rdb_config != null ? ["rdb_config"] : [] + content { + rdb_snapshot_period = var.persistence_config.rdb_config.rdb_snapshot_period + rdb_snapshot_start_time = var.persistence_config.rdb_config.rdb_snapshot_start_time + } + } + dynamic "aof_config" { + for_each = var.persistence_config.aof_config != null ? ["aof_config"] : [] + content { + append_fsync = var.persistence_config.aof_config.append_fsync + } + } + } + } + labels = var.labels + + depends_on = [ + google_network_connectivity_service_connection_policy.service_connection_policies, + module.enable_apis, + ] +} + +resource "google_network_connectivity_service_connection_policy" "service_connection_policies" { + for_each = var.service_connection_policies + project = coalesce(var.network_project, var.project_id) + name = each.key + location = var.location + service_class = "gcp-memorystore" + description = lookup(each.value, "description", null) + network = "projects/${coalesce(var.network_project, var.project_id)}/global/networks/${var.network}" + labels = each.value.labels + + psc_config { + subnetworks = [for x in each.value.subnet_names : "projects/${coalesce(var.network_project, var.project_id)}/regions/${var.location}/subnetworks/${x}"] + limit = lookup(each.value, "limit", null) + } + +} + +module "enable_apis" { + source = "terraform-google-modules/project-factory/google//modules/project_services" + version = "~> 17.0" + + project_id = var.project_id + enable_apis = var.enable_apis + + disable_services_on_destroy = false + disable_dependent_services = false + + activate_apis = [ + "memorystore.googleapis.com", + "serviceconsumermanagement.googleapis.com", + "networkconnectivity.googleapis.com", + "compute.googleapis.com", + ] +} diff --git a/modules/valkey/outputs.tf b/modules/valkey/outputs.tf new file mode 100644 index 00000000..c5191a24 --- /dev/null +++ b/modules/valkey/outputs.tf @@ -0,0 +1,35 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "id" { + description = "The valkey cluster instance ID" + value = google_memorystore_instance.valkey_cluster.id +} + +output "discovery_endpoints" { + description = "Endpoints created on each given network, for valkey clients to connect to the cluster. Currently only one endpoint is supported" + value = google_memorystore_instance.valkey_cluster.discovery_endpoints +} + +output "psc_connections" { + description = "PSC connections for discovery of the cluster topology and accessing the cluster" + value = google_memorystore_instance.valkey_cluster.psc_auto_connections +} + +output "valkey_cluster" { + description = "The valkey cluster created" + value = google_memorystore_instance.valkey_cluster +} diff --git a/modules/valkey/variables.tf b/modules/valkey/variables.tf new file mode 100644 index 00000000..dd1f4d7f --- /dev/null +++ b/modules/valkey/variables.tf @@ -0,0 +1,147 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "project_id" { + description = "The ID of the project in which the resource belongs to." + type = string +} + +variable "instance_id" { + description = "The ID to use for the instance, which will become the final component of the instance's resource name. Must be 4-63 characters in length with lowercase letters, digits, and hyphens. Must not end with a hyphen. Must be unique within a location" + type = string +} + +variable "shard_count" { + description = "Number of shards for the instance" + type = number + default = 3 +} + +variable "location" { + description = "The region where valkey cluster will be created" + type = string +} + +variable "labels" { + description = "The resource labels to represent user provided metadata." + type = map(string) + default = {} +} + +variable "replica_count" { + description = "Number of replica nodes per shard. If omitted the default is 0 replicas" + type = number + default = 0 +} + +variable "authorization_mode" { + description = "The Immutable. Authorization mode of the instance. Possible values: AUTH_DISABLED IAM_AUTH" + type = string + default = "AUTH_DISABLED" +} + +variable "transit_encryption_mode" { + description = "Immutable. In-transit encryption mode of the instance. Possible values: TRANSIT_ENCRYPTION_DISABLED SERVER_AUTHENTICATION" + type = string + default = "TRANSIT_ENCRYPTION_DISABLED" +} + +variable "node_type" { + description = "The nodeType for the valkey cluster. Possible values are: SHARED_CORE_NANO, HIGHMEM_MEDIUM, HIGHMEM_XLARGE, STANDARD_SMALL" + type = string + default = null +} + +variable "deletion_protection_enabled" { + description = "If set to true deletion of the instance will fail" + type = bool + default = true +} + +variable "zone_distribution_config_mode" { + description = "The mode for zone distribution for Memorystore valkey cluster (Immutable). If not provided, MULTI_ZONE will be used as default value. Possible values are: MULTI_ZONE, SINGLE_ZONE" + type = string + default = "MULTI_ZONE" +} + +variable "zone_distribution_config_zone" { + description = "The zone for single zone Memorystore valkey cluster (Immutable)" + type = string + default = null +} + +variable "engine_version" { + description = "Immutable. Engine version of the instance" + type = string + default = "VALKEY_8_0" +} + +variable "enable_apis" { + description = "Flag for enabling memcache.googleapis.com in your project" + type = bool + default = false +} + +variable "network" { + description = "Name of the consumer network where the network address of the discovery endpoint will be reserved" + type = string +} + +variable "network_project" { + description = "project ID of the consumer network where the network address of the discovery endpoint will be reserved. Required for Shared VPC host" + type = string + default = null +} + +variable "service_connection_policies" { + description = "The Service Connection Policies to create. Required to create service connection policy. Not needed if service connection policy already exist" + type = map(object({ + subnet_names = list(string) + description = optional(string) + limit = optional(number) + labels = optional(map(string), {}) + })) + default = {} +} + + +variable "engine_configs" { + description = "User-provided engine configurations for the instance" + type = object({ + maxmemory = optional(string) + maxmemory-clients = optional(string) + maxmemory-policy = optional(string) + notify-keyspace-events = optional(string) + slowlog-log-slower-than = optional(number) + maxclients = optional(number) + }) + default = null +} + +variable "persistence_config" { + description = "User-provided persistence configurations for the instance" + type = object({ + mode = optional(string) + rdb_config = optional(object({ + rdb_snapshot_period = optional(string) + rdb_snapshot_start_time = optional(string) + }), null) + aof_config = optional(object({ + append_fsync = string + }), null) + }) + default = {} +} diff --git a/modules/valkey/versions.tf b/modules/valkey/versions.tf new file mode 100644 index 00000000..10bf301a --- /dev/null +++ b/modules/valkey/versions.tf @@ -0,0 +1,39 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 1.3" + required_providers { + + google = { + source = "hashicorp/google" + version = ">= 6.3, < 7" + } + google-beta = { + source = "hashicorp/google-beta" + version = ">= 6.3, < 7" + } + } + + provider_meta "google" { + module_name = "blueprints/terraform/terraform-google-memorystore:valkey/v11.0.1" + } + + provider_meta "google-beta" { + module_name = "blueprints/terraform/terraform-google-memorystore:valkey/v11.0.1" + } + +} diff --git a/test/integration/go.mod b/test/integration/go.mod index 6ef41116..e52ca814 100644 --- a/test/integration/go.mod +++ b/test/integration/go.mod @@ -7,12 +7,12 @@ toolchain go1.22.7 require ( github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test v0.16.2 github.com/stretchr/testify v1.9.0 + golang.org/x/oauth2 v0.23.0 ) require ( cloud.google.com/go v0.110.7 // indirect - cloud.google.com/go/compute v1.23.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect cloud.google.com/go/iam v1.1.2 // indirect cloud.google.com/go/storage v1.33.0 // indirect github.com/agext/levenshtein v1.2.3 // indirect @@ -65,7 +65,6 @@ require ( golang.org/x/crypto v0.21.0 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.12.0 // indirect golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/test/integration/go.sum b/test/integration/go.sum index 02485222..13dcb98d 100644 --- a/test/integration/go.sum +++ b/test/integration/go.sum @@ -68,10 +68,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= @@ -595,8 +593,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/test/integration/valkey/valkey_cluster_test.go b/test/integration/valkey/valkey_cluster_test.go new file mode 100755 index 00000000..e70fe3cb --- /dev/null +++ b/test/integration/valkey/valkey_cluster_test.go @@ -0,0 +1,51 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package valkey_cluster + +import ( + "testing" + + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" + "github.com/stretchr/testify/assert" +) + +func TestValkeyCluster(t *testing.T) { + rc := tft.NewTFBlueprintTest(t) + + rc.DefineVerify(func(assert *assert.Assertions) { + projectId := rc.GetTFSetupStringOutput("project_id") + clusterRegion := rc.GetStringOutput("cluster_region") + clusterId := rc.GetStringOutput("cluster_id") + clusterName := rc.GetStringOutput("cluster_name") + shardCount := rc.GetStringOutput("shard_count") + transitEncryptionMode := rc.GetStringOutput("transit_encryption_mode") + replicaCount := rc.GetStringOutput("replica_count") + authorizationMode := rc.GetStringOutput("authorization_mode") + nodeType := rc.GetStringOutput("node_type") + + op := gcloud.Runf(t, "beta memorystore instances describe %s --project %s --location %s", clusterName, projectId, clusterRegion) + assert.Equal(op.Get("name").String(), clusterId, "mismatched clusterName") + assert.Equal(op.Get("shardCount").String(), shardCount, "mismatched shardCount") + assert.Equal(op.Get("transitEncryptionMode").String(), transitEncryptionMode, "mismatched transitEncryptionMode") + assert.Equal(op.Get("replicaCount").String(), replicaCount, "mismatched replicaCount") + assert.Equal(op.Get("authorizationMode").String(), authorizationMode, "mismatched authorizationMode") + assert.Equal(op.Get("engineVersion").String(), "VALKEY_8_0", "mismatched engineVersion") + assert.Equal(op.Get("nodeType").String(), nodeType, "mismatched nodeType") + assert.Equal(op.Get("engineConfigs.maxmemory-policy").String(), "volatile-ttl", "mismatched valkeyConfigs.maxmemory-policy") + }) + + rc.Test() +}