Pular para o conteúdo principal

Configuração

Quando usar: sempre que um número, um texto ou um liga/desliga do serviço precisa mudar sem release nova — ou, no mínimo, sem ficar escondido no código.

Configuração não é segredo: ela é declarada, tipada, versionada no descritor e aparece nos comandos de inspeção do módulo. O que não pode ser lido por quem opera (token, chave, senha) é slot de segredo (veja Segredo).

Declarar​

nns/service.yaml:

config:
- {key: scan_seconds, type: integer, default: 3}
- {key: notify_enabled, type: boolean, default: true}
  • key é minúscula com underscore (^[a-z][a-z0-9_]{0,62}$), no máximo 64 chaves.
  • type é string, integer ou boolean — e é conferido de verdade: "abc" numa chave integer derruba a partida, não vira zero.
  • default é opcional. Chave declarada sem valor e sem default fica ausente e o acessor devolve o zero do tipo.

De onde vem o valor, nesta ordem: NN_CONFIG_<CHAVE> no ambiente → o arquivo NN_CONFIG_FILE (YAML/JSON, um mapa) → o default do descritor. O nome da variável é a chave em MAIÚSCULAS: scan_seconds → NN_CONFIG_SCAN_SECONDS.

Chave que não está declarada — no ambiente (NN_CONFIG_*) ou no arquivo — é recusada na partida: config não declarada em service.yaml: <chave>. Nada é ignorado em silêncio.

Ler​

O serviço recebe o Settings pronto (bean do SDK) e lê por tipo:

public SampleService(
Db db,
NnsConfig config,
NnsModule module,
Settings settings,
ActionStore actions,
TxAuditEmitter emitter,
AuditConfig auditConfig,
RoleWatcher role,
Notifier notifier) {
// ...
// Configuração DECLARADA no service.yaml: o número não está no código.
this.scanDelay = Duration.ofSeconds(settings.integer("scan_seconds"));
this.clock = InstantSource.system();
}

Os quatro acessores:

settings.integer("scan_seconds") // long; 0 se ausente ou de outro tipo
settings.string("engine_name") // "" se ausente
settings.bool("notify_enabled") // false se ausente
settings.has("scan_seconds") // a chave tem valor (do ambiente, do arquivo ou do default)?

O validador do descritor confere o cruzamento (chave declarada no service.yaml e usada no código você confere com um teste — veja abaixo).

Como o valor chega ao pod​

No Kubernetes o valor entra pelo chart, como ambiente:

env:
- name: NN_CONFIG_SCAN_SECONDS
value: "{{ .Values.scanSeconds }}"

Em desenvolvimento, export NN_CONFIG_SCAN_SECONDS=1 no shell e pronto. Para muitas chaves, o NN_CONFIG_FILE aceita um mapa YAML — o mesmo arquivo que o deployment target renderiza.

Como testar​

O teste do descritor confere o default declarado:

@Test
void serviceDeclaresTheOutboundDependencyTheSecretAndTheConfiguration() {
var service = MODULE.service();
assertThat(service.config())
.anySatisfy(
c -> {
assertThat(c.key()).isEqualTo("scan_seconds");
assertThat(((Number) c.defaultValue()).intValue()).isEqualTo(3);
});
}

E o efeito da configuração aparece no teste de comportamento quando o valor muda:

// No teste do handler: sem espera, o teste não dorme — a configuração é o que manda.
env.put("NN_CONFIG_SCAN_SECONDS", "0");
NnsConfig config = NnsConfig.fromEnviron(env);

Erros comuns​

  • Número mágico no código (Duration.ofSeconds(3)) — ninguém consegue ajustar sem release, e o valor real de produção passa a ser desconhecido.
  • Usar configuração para segredo — a config é lida por quem opera, aparece em inspeção e é versionada. Segredo é slot.
  • Chave no ambiente e não no descritor — a partida falha com config não declarada em service.yaml: é de propósito, para não existir configuração fantasma que ninguém declara.
  • Depender do default para um valor que importa — default é conveniência de desenvolvimento; o ambiente de produção deve dizer o valor.
  • Trocar o tipo depois de publicado — integer que vira string quebra o pod que ainda tem a variável antiga; trate como mudança de contrato.
  • Ler System.getenv() no handler — a leitura é pelo Settings/NnsConfig; assim o teste controla o valor e a chave declarada é a única fonte.
  • Achar que a config recarrega sozinha — ela é lida na partida. Mudar valor é mudar o pod.