From 29f90e02017d201bfedc29e89998763d38c3e9fb Mon Sep 17 00:00:00 2001 From: RB <7775707+nitrocode@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:06:10 -0500 Subject: [PATCH 1/2] feat(elasticache-redis): add engine input for valkey support (#1170) --- modules/elasticache-redis/README.md | 5 +++++ modules/elasticache-redis/main.tf | 1 + modules/elasticache-redis/modules/redis_cluster/main.tf | 3 ++- .../elasticache-redis/modules/redis_cluster/variables.tf | 8 +++++++- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/elasticache-redis/README.md b/modules/elasticache-redis/README.md index eaec1c2ae..a103f07d3 100644 --- a/modules/elasticache-redis/README.md +++ b/modules/elasticache-redis/README.md @@ -38,6 +38,7 @@ components: num_replicas: 1 num_shards: 0 replicas_per_shard: 0 + engine: "redis" engine_version: 6.0.5 instance_type: cache.t2.small parameters: @@ -68,6 +69,9 @@ components: value: lK ``` +The `engine` can either be `redis` or `valkey`. For more information, see +[why aws supports valkey](https://aws.amazon.com/blogs/opensource/why-aws-supports-valkey/). + ## Requirements @@ -109,6 +113,7 @@ No resources. | [at\_rest\_encryption\_enabled](#input\_at\_rest\_encryption\_enabled) | Enable encryption at rest | `bool` | n/a | yes | | [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | | [auth\_token\_enabled](#input\_auth\_token\_enabled) | Enable auth token | `bool` | `true` | no | +| [auto\_minor\_version\_upgrade](#input\_auto\_minor\_version\_upgrade) | Specifies whether minor version engine upgrades will be applied automatically to the underlying Cache Cluster instances during the maintenance window. Only supported if the engine version is 6 or higher. | `bool` | `false` | no | | [automatic\_failover\_enabled](#input\_automatic\_failover\_enabled) | Enable automatic failover | `bool` | n/a | yes | | [availability\_zones](#input\_availability\_zones) | Availability zone IDs | `list(string)` | `[]` | no | | [cloudwatch\_metric\_alarms\_enabled](#input\_cloudwatch\_metric\_alarms\_enabled) | Boolean flag to enable/disable CloudWatch metrics alarms | `bool` | n/a | yes | diff --git a/modules/elasticache-redis/main.tf b/modules/elasticache-redis/main.tf index 0f2f91638..c13c90dbe 100644 --- a/modules/elasticache-redis/main.tf +++ b/modules/elasticache-redis/main.tf @@ -69,6 +69,7 @@ module "redis_clusters" { num_replicas = lookup(each.value, "num_replicas", 1) num_shards = lookup(each.value, "num_shards", 0) replicas_per_shard = lookup(each.value, "replicas_per_shard", 0) + engine = lookup(each.value, "engine", "redis") engine_version = each.value.engine_version create_parameter_group = lookup(each.value, "create_parameter_group", true) parameters = lookup(each.value, "parameters", null) diff --git a/modules/elasticache-redis/modules/redis_cluster/main.tf b/modules/elasticache-redis/modules/redis_cluster/main.tf index 37a3ee332..59fc64052 100644 --- a/modules/elasticache-redis/modules/redis_cluster/main.tf +++ b/modules/elasticache-redis/modules/redis_cluster/main.tf @@ -10,7 +10,7 @@ locals { module "redis" { source = "cloudposse/elasticache-redis/aws" - version = "1.4.1" + version = "1.7.0" name = var.cluster_name @@ -29,6 +29,7 @@ module "redis" { cluster_mode_replicas_per_node_group = var.replicas_per_shard cluster_size = var.num_replicas dns_subdomain = var.dns_subdomain + engine = var.engine engine_version = var.engine_version family = var.cluster_attributes.family instance_type = var.instance_type diff --git a/modules/elasticache-redis/modules/redis_cluster/variables.tf b/modules/elasticache-redis/modules/redis_cluster/variables.tf index 1c9af10cd..411f00d59 100644 --- a/modules/elasticache-redis/modules/redis_cluster/variables.tf +++ b/modules/elasticache-redis/modules/redis_cluster/variables.tf @@ -11,9 +11,15 @@ variable "create_parameter_group" { description = "Whether new parameter group should be created. Set to false if you want to use existing parameter group" } +variable "engine" { + type = string + default = "redis" + description = "Name of the cache engine to use: either `redis` or `valkey`" +} + variable "engine_version" { type = string - description = "Redis Version" + description = "Version of the cache engine to use" default = "6.0.5" } From fba6cde9bae9e5fe9beef62eb19fb57119fcf6be Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Thu, 31 Oct 2024 15:02:54 -0400 Subject: [PATCH 2/2] feat: Support `enabled` flag for EKS Storage Classes (#1173) --- modules/eks/storage-class/README.md | 4 ++-- modules/eks/storage-class/main.tf | 12 +++++++++--- modules/eks/storage-class/variables.tf | 2 ++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/modules/eks/storage-class/README.md b/modules/eks/storage-class/README.md index a9c64d06e..264c2f5ef 100644 --- a/modules/eks/storage-class/README.md +++ b/modules/eks/storage-class/README.md @@ -156,8 +156,8 @@ eks/storage-class: | [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional\_tag\_map, which are merged. | `any` |
{
"additional_tag_map": {},
"attributes": [],
"delimiter": null,
"descriptor_formats": {},
"enabled": true,
"environment": null,
"id_length_limit": null,
"label_key_case": null,
"label_order": [],
"label_value_case": null,
"labels_as_tags": [
"unset"
],
"name": null,
"namespace": null,
"regex_replace_chars": null,
"stage": null,
"tags": {},
"tenant": null
}
| no | | [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no | | [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.
Map of maps. Keys are names of descriptors. Values are maps of the form
`{
format = string
labels = list(string)
}`
(Type is `any` so the map values can later be enhanced to provide additional options.)
`format` is a Terraform format string to be passed to the `format()` function.
`labels` is a list of labels, in order, to pass to `format()` function.
Label values will be normalized before being passed to `format()` so they will be
identical to how they appear in `id`.
Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no | -| [ebs\_storage\_classes](#input\_ebs\_storage\_classes) | A map of storage class name to EBS parameters to create |
map(object({
make_default_storage_class = optional(bool, false)
include_tags = optional(bool, true) # If true, StorageClass will set our tags on created EBS volumes
labels = optional(map(string), null)
reclaim_policy = optional(string, "Delete")
volume_binding_mode = optional(string, "WaitForFirstConsumer")
mount_options = optional(list(string), null)
# Allowed topologies are poorly documented, and poorly implemented.
# According to the API spec https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#storageclass-v1-storage-k8s-io
# it should be a list of objects with a `matchLabelExpressions` key, which is a list of objects with `key` and `values` keys.
# However, the Terraform resource only allows a single object in a matchLabelExpressions block, not a list,
# the EBS driver appears to only allow a single matchLabelExpressions block, and it is entirely unclear
# what should happen if either of the lists has more than one element.
# So we simplify it here to be singletons, not lists, and allow for a future change to the resource to support lists,
# and a future replacement for this flattened object which can maintain backward compatibility.
allowed_topologies_match_label_expressions = optional(object({
key = optional(string, "topology.ebs.csi.aws.com/zone")
values = list(string)
}), null)
allow_volume_expansion = optional(bool, true)
# parameters, see https://github.com/kubernetes-sigs/aws-ebs-csi-driver/blob/master/docs/parameters.md
parameters = object({
fstype = optional(string, "ext4") # "csi.storage.k8s.io/fstype"
type = optional(string, "gp3")
iopsPerGB = optional(string, null)
allowAutoIOPSPerGBIncrease = optional(string, null) # "true" or "false"
iops = optional(string, null)
throughput = optional(string, null)

encrypted = optional(string, "true")
kmsKeyId = optional(string, null) # ARN of the KMS key to use for encryption. If not specified, the default key is used.
blockExpress = optional(string, null) # "true" or "false"
blockSize = optional(string, null)
})
provisioner = optional(string, "ebs.csi.aws.com")

# TODO: support tags
# https://github.com/kubernetes-sigs/aws-ebs-csi-driver/blob/master/docs/tagging.md
}))
| `{}` | no | -| [efs\_storage\_classes](#input\_efs\_storage\_classes) | A map of storage class name to EFS parameters to create |
map(object({
make_default_storage_class = optional(bool, false)
labels = optional(map(string), null)
efs_component_name = optional(string, "eks/efs")
reclaim_policy = optional(string, "Delete")
volume_binding_mode = optional(string, "Immediate")
# Mount options are poorly documented.
# TLS is now the default and need not be specified. https://github.com/kubernetes-sigs/aws-efs-csi-driver/tree/master/docs#encryption-in-transit
# Other options include `lookupcache` and `iam`.
mount_options = optional(list(string), null)
parameters = optional(object({
basePath = optional(string, "/efs_controller")
directoryPerms = optional(string, "700")
provisioningMode = optional(string, "efs-ap")
gidRangeStart = optional(string, null)
gidRangeEnd = optional(string, null)
# Support for cross-account EFS mounts
# See https://github.com/kubernetes-sigs/aws-efs-csi-driver/tree/master/examples/kubernetes/cross_account_mount
# and for gritty details on secrets: https://kubernetes-csi.github.io/docs/secrets-and-credentials-storage-class.html
az = optional(string, null)
provisioner-secret-name = optional(string, null) # "csi.storage.k8s.io/provisioner-secret-name"
provisioner-secret-namespace = optional(string, null) # "csi.storage.k8s.io/provisioner-secret-namespace"
}), {})
provisioner = optional(string, "efs.csi.aws.com")
}))
| `{}` | no | +| [ebs\_storage\_classes](#input\_ebs\_storage\_classes) | A map of storage class name to EBS parameters to create |
map(object({
enabled = optional(bool, true)
make_default_storage_class = optional(bool, false)
include_tags = optional(bool, true) # If true, StorageClass will set our tags on created EBS volumes
labels = optional(map(string), null)
reclaim_policy = optional(string, "Delete")
volume_binding_mode = optional(string, "WaitForFirstConsumer")
mount_options = optional(list(string), null)
# Allowed topologies are poorly documented, and poorly implemented.
# According to the API spec https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#storageclass-v1-storage-k8s-io
# it should be a list of objects with a `matchLabelExpressions` key, which is a list of objects with `key` and `values` keys.
# However, the Terraform resource only allows a single object in a matchLabelExpressions block, not a list,
# the EBS driver appears to only allow a single matchLabelExpressions block, and it is entirely unclear
# what should happen if either of the lists has more than one element.
# So we simplify it here to be singletons, not lists, and allow for a future change to the resource to support lists,
# and a future replacement for this flattened object which can maintain backward compatibility.
allowed_topologies_match_label_expressions = optional(object({
key = optional(string, "topology.ebs.csi.aws.com/zone")
values = list(string)
}), null)
allow_volume_expansion = optional(bool, true)
# parameters, see https://github.com/kubernetes-sigs/aws-ebs-csi-driver/blob/master/docs/parameters.md
parameters = object({
fstype = optional(string, "ext4") # "csi.storage.k8s.io/fstype"
type = optional(string, "gp3")
iopsPerGB = optional(string, null)
allowAutoIOPSPerGBIncrease = optional(string, null) # "true" or "false"
iops = optional(string, null)
throughput = optional(string, null)

encrypted = optional(string, "true")
kmsKeyId = optional(string, null) # ARN of the KMS key to use for encryption. If not specified, the default key is used.
blockExpress = optional(string, null) # "true" or "false"
blockSize = optional(string, null)
})
provisioner = optional(string, "ebs.csi.aws.com")

# TODO: support tags
# https://github.com/kubernetes-sigs/aws-ebs-csi-driver/blob/master/docs/tagging.md
}))
| `{}` | no | +| [efs\_storage\_classes](#input\_efs\_storage\_classes) | A map of storage class name to EFS parameters to create |
map(object({
enabled = optional(bool, true)
make_default_storage_class = optional(bool, false)
labels = optional(map(string), null)
efs_component_name = optional(string, "eks/efs")
reclaim_policy = optional(string, "Delete")
volume_binding_mode = optional(string, "Immediate")
# Mount options are poorly documented.
# TLS is now the default and need not be specified. https://github.com/kubernetes-sigs/aws-efs-csi-driver/tree/master/docs#encryption-in-transit
# Other options include `lookupcache` and `iam`.
mount_options = optional(list(string), null)
parameters = optional(object({
basePath = optional(string, "/efs_controller")
directoryPerms = optional(string, "700")
provisioningMode = optional(string, "efs-ap")
gidRangeStart = optional(string, null)
gidRangeEnd = optional(string, null)
# Support for cross-account EFS mounts
# See https://github.com/kubernetes-sigs/aws-efs-csi-driver/tree/master/examples/kubernetes/cross_account_mount
# and for gritty details on secrets: https://kubernetes-csi.github.io/docs/secrets-and-credentials-storage-class.html
az = optional(string, null)
provisioner-secret-name = optional(string, null) # "csi.storage.k8s.io/provisioner-secret-name"
provisioner-secret-namespace = optional(string, null) # "csi.storage.k8s.io/provisioner-secret-namespace"
}), {})
provisioner = optional(string, "efs.csi.aws.com")
}))
| `{}` | no | | [eks\_component\_name](#input\_eks\_component\_name) | The name of the EKS component for the cluster in which to create the storage classes | `string` | `"eks/cluster"` | no | | [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | | [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | diff --git a/modules/eks/storage-class/main.tf b/modules/eks/storage-class/main.tf index e4abdd8fb..511772fe3 100644 --- a/modules/eks/storage-class/main.tf +++ b/modules/eks/storage-class/main.tf @@ -1,8 +1,14 @@ locals { enabled = module.this.enabled - efs_components = local.enabled ? toset([for k, v in var.efs_storage_classes : v.efs_component_name]) : [] + efs_storage_classes = { + for k, v in var.efs_storage_classes : k => v if v.enabled + } + efs_components = local.enabled ? toset([for k, v in local.efs_storage_classes : v.efs_component_name]) : [] + ebs_storage_classes = { + for k, v in var.ebs_storage_classes : k => v if v.enabled + } # In order to use `optional()`, the variable must be an object, but # object keys must be valid identifiers and cannot be like "csi.storage.k8s.io/fstype" # See https://github.com/hashicorp/terraform/issues/22681 @@ -24,7 +30,7 @@ locals { } resource "kubernetes_storage_class_v1" "ebs" { - for_each = local.enabled ? var.ebs_storage_classes : {} + for_each = local.enabled ? local.ebs_storage_classes : {} metadata { name = each.key @@ -69,7 +75,7 @@ resource "kubernetes_storage_class_v1" "ebs" { } resource "kubernetes_storage_class_v1" "efs" { - for_each = local.enabled ? var.efs_storage_classes : {} + for_each = local.enabled ? local.efs_storage_classes : {} metadata { name = each.key diff --git a/modules/eks/storage-class/variables.tf b/modules/eks/storage-class/variables.tf index 597970e54..38b20af1e 100644 --- a/modules/eks/storage-class/variables.tf +++ b/modules/eks/storage-class/variables.tf @@ -12,6 +12,7 @@ variable "eks_component_name" { variable "ebs_storage_classes" { type = map(object({ + enabled = optional(bool, true) make_default_storage_class = optional(bool, false) include_tags = optional(bool, true) # If true, StorageClass will set our tags on created EBS volumes labels = optional(map(string), null) @@ -57,6 +58,7 @@ variable "ebs_storage_classes" { variable "efs_storage_classes" { type = map(object({ + enabled = optional(bool, true) make_default_storage_class = optional(bool, false) labels = optional(map(string), null) efs_component_name = optional(string, "eks/efs")