Skip to content

feat(gke-node-pool): Add support for "registry_hosts"#2563

Open
marcmillien wants to merge 5 commits intoterraform-google-modules:mainfrom
marcmillien:feat_add_support_for_registry_hosts
Open

feat(gke-node-pool): Add support for "registry_hosts"#2563
marcmillien wants to merge 5 commits intoterraform-google-modules:mainfrom
marcmillien:feat_add_support_for_registry_hosts

Conversation

@marcmillien
Copy link
Copy Markdown

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the GKE node pool module by integrating support for containerd registry host configurations. This new feature allows users to precisely define how their GKE nodes interact with container image registries, enabling advanced scenarios such as private registry access, custom certificate authorities, and client authentication. It provides granular control over registry communication, aligning the module with the latest GKE capabilities for container runtime customization.

Highlights

  • GKE Node Pool Configuration: Implemented support for registry_hosts within the containerd_config block for GKE node pools.
  • Container Registry Customization: Introduced comprehensive configuration options for registry_hosts, including server definitions, host-specific settings, headers, and CA/client certificate management via GCP Secret Manager.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces new functionality to configure Containerd registry hosts within GKE node pools, allowing for detailed specification of host settings, including advanced authentication via GCP Secret Manager for CA certificates and client certificates/keys. A critical issue was identified in modules/gke-node-pool/main.tf where a typo (host.value.client instead of hosts.value.client) in a for_each expression for the client dynamic block will cause a Terraform error.

@marcmillien

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces new functionality to the GKE node pool module, enabling advanced Containerd registry host configuration. This includes support for private registry access and detailed host settings, with integration for GCP Secret Manager to manage certificates. The review feedback indicates that the README.md documentation for the node_config variable is outdated and needs to be updated to reflect the newly added registry_hosts attribute.

@marcmillien
Copy link
Copy Markdown
Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the capability to configure custom container registry hosts for GKE node pools, enhancing flexibility for image management. It also refactors the existing private_registry_access_config to use dynamic blocks for improved robustness. A high-severity issue was identified where the certificate_authority_domain_config block within private_registry_access_config should also be dynamic to prevent errors when it's optional but not provided.

Comment on lines +246 to +251
certificate_authority_domain_config {
fqdns = private_registry_access_config.value.certificate_authority_domain_config.fqdns
gcp_secret_manager_certificate_config {
secret_uri = private_registry_access_config.value.certificate_authority_domain_config.gcp_secret_manager_certificate_config.secret_uri
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The certificate_authority_domain_config block is optional within private_registry_access_config. To prevent potential errors when private_registry_access_config is enabled but certificate_authority_domain_config is not provided, this block should be created dynamically. This will make the configuration more robust and prevent unexpected failures.

              dynamic "certificate_authority_domain_config" {
                for_each = private_registry_access_config.value.certificate_authority_domain_config != null ? [private_registry_access_config.value.certificate_authority_domain_config] : []
                content {
                  fqdns = certificate_authority_domain_config.value.fqdns
                  gcp_secret_manager_certificate_config {
                    secret_uri = certificate_authority_domain_config.value.gcp_secret_manager_certificate_config.secret_uri
                  }
                }
              }

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gemini-code-assist I am going to check it, but this is not related to my changes

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct that the primary focus of your changes is the addition of registry_hosts. However, the change you made to wrap private_registry_access_config in a dynamic block (lines 242-252 in main.tf in the AFTER DIFF section) makes the certificate_authority_domain_config block, which is nested inside it, susceptible to errors if private_registry_access_config is provided but certificate_authority_domain_config is not. My suggestion aims to make the certificate_authority_domain_config block also dynamic to align with this new structure and prevent potential issues, ensuring the configuration remains robust. This is a direct follow-up improvement related to the refactoring you introduced for private_registry_access_config.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Cf.: https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/container_cluster#certificate_authority_domain_config-1

> certificate_authority_domain_config (Optional) - List of configuration objects for CA and domains. Each object identifies a certificate and its assigned domains.
@marcmillien marcmillien force-pushed the feat_add_support_for_registry_hosts branch from cc373ae to 7a8decd Compare March 25, 2026 14:43
@marcmillien
Copy link
Copy Markdown
Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the GKE node pool module by introducing support for containerd_config.registry_hosts and updating certificate_authority_domain_config to be a list. The review suggests adding a description for the new registry_hosts block in the README.md to improve clarity and usability for module consumers.

| name\_prefix | Creates a unique name for the node pool beginning with the specified prefix. Conflicts with name. | `string` | `null` | no |
| network\_config | The network configuration of the pool. | <pre>object({<br> create_pod_range = optional(bool)<br> enable_private_nodes = optional(bool)<br> pod_ipv4_cidr_block = optional(string)<br> pod_range = optional(string)<br> additional_node_network_configs = optional(list(object({<br> network = string<br> subnetwork = string<br> })))<br> additional_pod_network_configs = optional(list(object({<br> subnetwork = string<br> secondary_pod_range = string<br> max_pods_per_node = number<br> })))<br> pod_cidr_overprovision_config = optional(object({<br> disabled = bool<br> }))<br> network_performance_config = optional(object({<br> total_egress_bandwidth_tier = string<br> }))<br> })</pre> | `null` | no |
| node\_config | Parameters used in creating the node pool. | <pre>object({<br> confidential_nodes = optional(object({<br> enabled = bool<br> }))<br> disk_size_gb = optional(number)<br> disk_type = optional(string)<br> enable_confidential_storage = optional(bool)<br> local_ssd_encryption_mode = optional(string)<br> ephemeral_storage_config = optional(object({<br> local_ssd_count = number<br> }))<br> ephemeral_storage_local_ssd_config = optional(object({<br> local_ssd_count = number<br> data_cache_count = optional(number)<br> }))<br> fast_socket = optional(object({<br> enabled = bool<br> }))<br> local_nvme_ssd_block_config = optional(object({<br> local_ssd_count = number<br> }))<br> logging_variant = optional(string)<br> secondary_boot_disks = optional(object({<br> disk_image = string<br> mode = optional(string)<br> }))<br> gcfs_config = optional(object({<br> enabled = bool<br> }))<br> gvnic = optional(object({<br> enabled = bool<br> }))<br> guest_accelerator = optional(object({<br> type = string<br> count = number<br> gpu_driver_installation_config = optional(object({<br> gpu_driver_version = string<br> }))<br> gpu_partition_size = optional(string)<br> gpu_sharing_config = optional(object({<br> gpu_sharing_strategy = string<br> max_shared_clients_per_gpu = number<br> }))<br> }))<br> image_type = optional(string)<br> labels = optional(map(string))<br> resource_labels = optional(map(string))<br> max_run_duration = optional(string)<br> flex_start = optional(bool)<br> local_ssd_count = optional(number)<br> machine_type = optional(string)<br> metadata = optional(map(string))<br> min_cpu_platform = optional(string)<br> oauth_scopes = optional(list(string))<br> preemptible = optional(bool)<br> reservation_affinity = optional(object({<br> consume_reservation_type = string<br> key = optional(string)<br> values = optional(list(string))<br> }))<br> spot = optional(bool)<br> sandbox_config = optional(object({<br> sandbox_type = string<br> }))<br> boot_disk_kms_key = optional(string)<br> service_account = optional(string)<br> shielded_instance_config = optional(object({<br> enable_secure_boot = optional(bool)<br> enable_integrity_monitoring = optional(bool)<br> }))<br> storage_pools = optional(list(string))<br> tags = optional(list(string))<br> resource_manager_tags = optional(map(string))<br> taint = optional(list(object({<br> key = string<br> value = string<br> effect = string<br> })))<br> workload_metadata_config = optional(object({<br> mode = optional(string)<br> }))<br> kubelet_config = optional(object({<br> cpu_manager_policy = optional(string)<br> cpu_cfs_quota = optional(bool)<br> cpu_cfs_quota_period = optional(string)<br> insecure_kubelet_readonly_port_enabled = optional(bool)<br> pod_pids_limit = optional(number)<br> container_log_max_size = optional(string)<br> container_log_max_files = optional(number)<br> image_gc_low_threshold_percent = optional(number)<br> image_gc_high_threshold_percent = optional(number)<br> image_minimum_gc_age = optional(string)<br> allowed_unsafe_sysctls = optional(list(string))<br> }))<br> linux_node_config = optional(object({<br> sysctls = optional(map(string))<br> cgroup_mode = optional(string)<br> hugepages_config = optional(object({<br> hugepage_size_2m = optional(number)<br> hugepage_size_1g = optional(number)<br> }))<br> }))<br> windows_node_config = optional(object({<br> osversion = string<br> }))<br> containerd_config = optional(object({<br> private_registry_access_config = optional(object({<br> enabled = bool<br> certificate_authority_domain_config = optional(object({<br> fqdns = list(string)<br> gcp_secret_manager_certificate_config = object({<br> secret_uri = string<br> })<br> }))<br> }))<br> }))<br> node_group = optional(string)<br> sole_tenant_config = optional(object({<br> node_affinity = optional(object({<br> key = string<br> operator = string<br> values = list(string)<br> }))<br> }))<br> })</pre> | <pre>{<br> "disk_size_gb": 100,<br> "disk_type": "pd-standard",<br> "image_type": "COS_CONTAINERD",<br> "kubelet_config": {<br> "insecure_kubelet_readonly_port_enabled": false<br> },<br> "machine_type": "e2-medium",<br> "workload_metadata_config": {<br> "mode": "GKE_METADATA"<br> }<br>}</pre> | no |
| node\_config | Parameters used in creating the node pool. | <pre>object({<br> confidential_nodes = optional(object({<br> enabled = bool<br> }))<br> disk_size_gb = optional(number)<br> disk_type = optional(string)<br> enable_confidential_storage = optional(bool)<br> local_ssd_encryption_mode = optional(string)<br> ephemeral_storage_config = optional(object({<br> local_ssd_count = number<br> }))<br> ephemeral_storage_local_ssd_config = optional(object({<br> local_ssd_count = number<br> data_cache_count = optional(number)<br> }))<br> fast_socket = optional(object({<br> enabled = bool<br> }))<br> local_nvme_ssd_block_config = optional(object({<br> local_ssd_count = number<br> }))<br> logging_variant = optional(string)<br> secondary_boot_disks = optional(object({<br> disk_image = string<br> mode = optional(string)<br> }))<br> gcfs_config = optional(object({<br> enabled = bool<br> }))<br> gvnic = optional(object({<br> enabled = bool<br> }))<br> guest_accelerator = optional(object({<br> type = string<br> count = number<br> gpu_driver_installation_config = optional(object({<br> gpu_driver_version = string<br> }))<br> gpu_partition_size = optional(string)<br> gpu_sharing_config = optional(object({<br> gpu_sharing_strategy = string<br> max_shared_clients_per_gpu = number<br> }))<br> }))<br> image_type = optional(string)<br> labels = optional(map(string))<br> resource_labels = optional(map(string))<br> max_run_duration = optional(string)<br> flex_start = optional(bool)<br> local_ssd_count = optional(number)<br> machine_type = optional(string)<br> metadata = optional(map(string))<br> min_cpu_platform = optional(string)<br> oauth_scopes = optional(list(string))<br> preemptible = optional(bool)<br> reservation_affinity = optional(object({<br> consume_reservation_type = string<br> key = optional(string)<br> values = optional(list(string))<br> }))<br> spot = optional(bool)<br> sandbox_config = optional(object({<br> sandbox_type = string<br> }))<br> boot_disk_kms_key = optional(string)<br> service_account = optional(string)<br> shielded_instance_config = optional(object({<br> enable_secure_boot = optional(bool)<br> enable_integrity_monitoring = optional(bool)<br> }))<br> storage_pools = optional(list(string))<br> tags = optional(list(string))<br> resource_manager_tags = optional(map(string))<br> taint = optional(list(object({<br> key = string<br> value = string<br> effect = string<br> })))<br> workload_metadata_config = optional(object({<br> mode = optional(string)<br> }))<br> kubelet_config = optional(object({<br> cpu_manager_policy = optional(string)<br> cpu_cfs_quota = optional(bool)<br> cpu_cfs_quota_period = optional(string)<br> insecure_kubelet_readonly_port_enabled = optional(bool)<br> pod_pids_limit = optional(number)<br> container_log_max_size = optional(string)<br> container_log_max_files = optional(number)<br> image_gc_low_threshold_percent = optional(number)<br> image_gc_high_threshold_percent = optional(number)<br> image_minimum_gc_age = optional(string)<br> allowed_unsafe_sysctls = optional(list(string))<br> }))<br> linux_node_config = optional(object({<br> sysctls = optional(map(string))<br> cgroup_mode = optional(string)<br> hugepages_config = optional(object({<br> hugepage_size_2m = optional(number)<br> hugepage_size_1g = optional(number)<br> }))<br> }))<br> windows_node_config = optional(object({<br> osversion = string<br> }))<br> containerd_config = optional(object({<br> private_registry_access_config = optional(object({<br> enabled = bool<br> certificate_authority_domain_config = optional(list(object({<br> fqdns = list(string)<br> gcp_secret_manager_certificate_config = object({<br> secret_uri = string<br> })<br> })))<br> }))<br> registry_hosts = optional(list(object({<br> server = string<br> hosts = list(object({<br> host = string<br> capabilities = optional(list(string))<br> override_path = optional(bool)<br> dial_timeout = optional(string)<br> header = optional(list(object({<br> key = string<br> value = list(string)<br> })))<br> ca = optional(list(object({<br> gcp_secret_manager_secret_uri = string<br> })))<br> client = optional(list(object({<br> cert = object({<br> gcp_secret_manager_secret_uri = string<br> })<br> key = object({<br> gcp_secret_manager_secret_uri = string<br> })<br> })))<br> }))<br> })))<br> }))<br> node_group = optional(string)<br> sole_tenant_config = optional(object({<br> node_affinity = optional(object({<br> key = string<br> operator = string<br> values = list(string)<br> }))<br> }))<br> })</pre> | <pre>{<br> "disk_size_gb": 100,<br> "disk_type": "pd-standard",<br> "image_type": "COS_CONTAINERD",<br> "kubelet_config": {<br> "insecure_kubelet_readonly_port_enabled": false<br> },<br> "machine_type": "e2-medium",<br> "workload_metadata_config": {<br> "mode": "GKE_METADATA"<br> }<br>}</pre> | no |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The node_config variable has been updated to include the containerd_config.registry_hosts block. While the type definition is correctly reflected, adding a brief description for registry_hosts and its purpose would greatly improve the clarity and usability of this module for consumers. This helps users understand what this new configuration option does without needing to consult external documentation.

Copy link
Copy Markdown
Author

@marcmillien marcmillien Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not done for other configs under node_config, I don't think this comment is relevant here, am I wrong?

@marcmillien marcmillien marked this pull request as ready for review March 25, 2026 14:52
Copy link
Copy Markdown
Collaborator

@apeabody apeabody left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution @marcmillien

From the initial lint test:

Checking submodule's files generation
diff -r '--exclude=.terraform' '--exclude=.kitchen' '--exclude=.git' /workspace/modules/gke-node-pool/metadata.yaml /tmp/tmp.OaPGXUVVRT/workspace/modules/gke-node-pool/metadata.yaml
302c302
<                   certificate_authority_domain_config = optional(object({
---
>                   certificate_authority_domain_config = optional(list(object({
307c307
<                   }))
---
>                   })))
308a309,332
>                 registry_hosts = optional(list(object({
>                   server = string
>                   hosts = list(object({
>                     host          = string
>                     capabilities  = optional(list(string))
>                     override_path = optional(bool)
>                     dial_timeout  = optional(string)
>                     header = optional(list(object({
>                       key   = string
>                       value = list(string)
>                     })))
>                     ca = optional(list(object({
>                       gcp_secret_manager_secret_uri = string
>                     })))
>                     client = optional(list(object({
>                       cert = object({
>                         gcp_secret_manager_secret_uri = string
>                       })
>                       key = object({
>                         gcp_secret_manager_secret_uri = string
>                       })
>                     })))
>                   }))
>                 })))
Error: submodule's files generation has not been run, please run the
'make build' command and commit changes

Please see the contribution template section: https://github.com/terraform-google-modules/terraform-google-kubernetes-engine/blob/main/CONTRIBUTING.md#templating

@marcmillien marcmillien marked this pull request as draft March 25, 2026 16:00
@marcmillien
Copy link
Copy Markdown
Author

Thanks 🤗 , I check/fix it :)

@marcmillien marcmillien marked this pull request as ready for review March 27, 2026 15:46
@marcmillien marcmillien requested a review from apeabody March 27, 2026 15:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants