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")