Abstrair o provider do Kubernetes num módulo

TLDR: Extrair o stack Kubernetes compartilhado para um módulo com abstração de provider, para que o mesmo terraform apply sirva tanto o cluster gerido da DigitalOcean quanto o EKS via uma única variável provider_type — transformando a migração para EKS numa troca de variável em vez de uma reescrita destrutiva e sem volta.

Contexto

O stack em src/shared/kubernetes/terraform/ era hardcoded no provedor antigo. A migração pendente para EKS seria uma reescrita in-place do stack: destrutiva e de mão única.

O resto do projeto já usa o padrão de abstração de provider (src/modules/providers/{cache,database,messagebroker,storage}/), onde uma variável provider_type seleciona a implementação e os outputs são normalizados. O Kubernetes era a única peça grande sem essa abstração.

Com a extração, a migração vira uma mudança de variável, e o rollback é igualmente simples. Os dois providers ficam no código, permitindo blue/green ou reversão rápida.

Objetivos

  • Criar src/modules/providers/kubernetes/ com os sub-providers digitalocean e aws.
  • Manter todos os outputs existentes (kubernetes_cluster_endpoint, kubernetes_cluster_token, kubernetes_cluster_ca_certificate, kubernetes_cluster_id, ingress_loadbalancer_ip) para não quebrar projeto consumidor.
  • Reescrever o main.tf do stack para delegar ao módulo novo.
  • Migrar os recursos existentes via terraform state mv, no mesmo arquivo de state.
  • Ter a implementação AWS (EKS + node groups + LB de ingress) pronta, habilitável só virando o provider_type.

Fora de escopo

  • rbac.tf, cert-manager.tf, ingress-controller.tf, metrics-server.tf e datadog.tf ficam no nível do stack, sem mudança — falam apenas com a API do Kubernetes e são agnósticos de provedor.
  • Fazer o switch em si. Esta spec entrega a capacidade; virar a chave é outra decisão.

Mudanças

Novo: src/modules/providers/kubernetes/

kubernetes/ ├── main.tf # provider_type → active = providers[provider_type] ├── variables.tf # interface unificada ├── outputs.tf # outputs normalizados, encaminhados do provider ativo └── providers/ ├── digitalocean/ # cluster gerido + node pools └── aws/ # aws_eks_cluster + aws_iam_role + aws_eks_node_group

Mesmo padrão de src/modules/providers/database/: count = var.provider_type == "X" ? 1 : 0 mais um locals.active.

Modificado: src/shared/kubernetes/terraform/

  • main.tf — passa a chamar module "kubernetes".
  • node-pools.tf — absorvido pelo módulo; apagado.
  • variables.tf — ganha provider_type (default no provedor antigo, por segurança), mantém kubernetes_version, adiciona as variáveis de AWS (aws_region, eks_node_instance_types).
  • outputs.tf — re-exporta os outputs do módulo com os mesmos nomes.
  • data.tf — remote state condicional, conforme o provider.
  • versions.tf — hashicorp/aws ~> 5.0 ao lado do provider existente.

Migração de state

Feita uma vez, à mão, no momento do merge — sem script:

bash cd src/shared/kubernetes/terraform ward exec -- terraform state mv 'digitalocean_kubernetes_cluster.main' \ 'module.kubernetes.module.digitalocean[0].digitalocean_kubernetes_cluster.this' ward exec -- terraform state mv 'digitalocean_project_resources.kubernetes' \ 'module.kubernetes.module.digitalocean[0].digitalocean_project_resources.this' ward exec -- terraform plan # obrigatório: zero mudanças

Como fazer o switch depois

  1. shared.network.apply (a VPC AWS já está rascunhada em src/shared/network/).
  2. provider_type = "aws" no vault.
  3. make shared.kubernetes.apply — o terraform destrói o cluster antigo e cria o EKS no mesmo workspace. Alternativa: blue/green num workspace temporário e cutover de DNS.

Rollback é o inverso: voltar o provider_type.

Como verificar

  • terraform plan com o provider_type original mostra zero mudanças depois do state mv — é o que prova que o refactor é não-destrutivo.
  • terraform plan com provider_type = "aws" mostra o grafo completo de EKS (cluster, node groups, IAM roles).
  • Todos os outputs existentes continuam resolvendo para os mesmos valores.
  • kubectl get nodes continua funcionando contra o cluster antigo até o provider ser virado.

Documentação

  • CLAUDE.md — caminho do módulo novo.
  • Arquitetura de abstração — o padrão que esta spec aplica.
  • Adicionar aprendizado sobre o padrão de terraform state mv se ele revelar pegadinha não-óbvia.