Se você já pensou em desenvolver um cliente de linha de comando para a API do Kubernetes — especialmente se considerou tornar seu cliente utilizável como um plugin do kubectl — talvez tenha se perguntado como fazer com que sua ferramenta pareça familiar para os usuários habituados ao ecossistema.
Ao observar a saída de kubectl options, a reação imediata costuma ser de hesitação: "Eu realmente preciso implementar todas essas opções?". A resposta curta é: não do zero. O projeto Kubernetes fornece duas bibliotecas principais para lidar com argumentos de linha de comando no estilo kubectl em programas Go: clientcmd e cli-runtime (que utiliza a primeira internamente). Este artigo explora como aplicar a clientcmd para garantir consistência operacional.
Filosofia Geral
Como parte da client-go, o objetivo final da clientcmd é fornecer uma instância de restclient.Config capaz de emitir requisições para um API server. Ela segue rigorosamente a semântica do kubectl:
- Os valores padrão são extraídos de
~/.kubeou equivalente; - Arquivos podem ser especificados via variável de ambiente
KUBECONFIG; - Todas as configurações acima podem ser sobrescritas por argumentos de linha de comando.
Vale notar que ela não configura automaticamente o argumento --kubeconfig. Veremos como fazer isso na seção "Bind das flags".
Recursos Disponíveis
A clientcmd permite que os programas gerenciem:
- Seleção de
kubeconfig(viaKUBECONFIG); - Seleção de
context; - Seleção de
namespace; - Certificados de cliente e chaves privadas;
- Impersonation de usuário;
- Suporte a autenticação HTTP Basic (username/password).
Mesclagem de Configuração (Merging)
A biblioteca suporta a mesclagem de configurações em vários cenários. A variável KUBECONFIG pode especificar múltiplos arquivos, cujos conteúdos são combinados.
Um ponto de atenção: as configurações são mescladas de formas diferentes dependendo da implementação. Se uma configuração for definida em um map, a primeira definição prevalece; se não for um map, a última definição vence. Além disso, se o usuário não definir a variável, o arquivo padrão ~/.kube/config é utilizado.
O Processo Global
O padrão de uso geral é resumido em seis etapas principais, conforme a documentação do pacote:
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
// Para alterar as regras de carregamento (arquivos e ordem)
configOverrides := &clientcmd.ConfigOverrides{}
// Para alterar valores de override ou vinculá-los a flags
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
config, err := kubeConfig.ClientConfig()
if err != nil {
// Tratamento de erro
}
client, err := metav1.New(config)
1. Configure as Loading Rules
clientcmd.NewDefaultClientConfigLoadingRules() constrói as regras que utilizam a variável KUBECONFIG ou o caminho padrão. Para a maioria dos casos de uso em empresas brasileiras que operam múltiplos clusters, os padrões são suficientes.
2. Configure os Overrides
A struct clientcmd.ConfigOverrides armazena os valores que substituirão as configurações carregadas. Aqui, utilizaremos a biblioteca pflag (substituto do pacote flag do Go) para suportar argumentos de traço duplo (--).
3. Construa o conjunto de Flags
As flags representam os argumentos de linha de comando (ex: --namespace ou -n). Três conjuntos estão disponíveis:
- Autenticação: Certificados, tokens, impersonation.
- Cluster: API server, CA, proxy, compressão.
- Contexto: Nome do cluster, usuário, namespace.
A recomendação é incluir os três utilizando as funções Recommended...Flags. Ao passar um prefixo vazio (""), você obtém os argumentos padrão como --context e --namespace.
4. Bind das Flags
Após definir as flags, utilize clientcmd.BindOverrideFlags para vincular os argumentos aos overrides. Se desejar vincular o caminho explícito do --kubeconfig, faça-o agora:
flags.StringVarP(&loadingRules.ExplicitPath, "kubeconfig", "", "", "caminho absoluto para o arquivo kubeconfig")
5. Construa a Configuração Mesclada
Existem funções para os modos interativo e não-interativo. O termo "deferred" (adiado) indica que a configuração final será determinada o mais tarde possível, permitindo que a função seja chamada antes do parse final dos argumentos.
6. Obtenha o API Client
A configuração mesclada retorna uma instância de ClientConfig. Se nenhuma configuração for encontrada, o sistema pode retornar erros legados e obscuros. É uma boa prática verificar com clientcmd.IsEmptyConfig(err) para fornecer uma mensagem de erro clara ao usuário.
Exemplo Completo
Este exemplo demonstra a implementação de uma ferramenta que lista os nós de um cluster respeitando as configurações de contexto e namespace do usuário:
package main
import (
"context"
"fmt"
"os"
"github.com/spf13/pflag"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
configOverrides := &clientcmd.ConfigOverrides{}
flags := pflag.NewFlagSet("demonuvemonline", pflag.ExitOnError)
clientcmd.BindOverrideFlags(configOverrides, flags, clientcmd.RecommendedConfigOverrideFlags(""))
flags.StringVarP(&loadingRules.ExplicitPath, "kubeconfig", "", "", "caminho absoluto para o kubeconfig")
flags.Parse(os.Args)
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
config, err := kubeConfig.ClientConfig()
if err != nil {
if clientcmd.IsEmptyConfig(err) {
panic("Por favor, forneça uma configuração válida para o API server do Kubernetes")
}
panic(err)
}
client, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
namespace, overridden, err := kubeConfig.Namespace()
if err != nil {
panic(err)
}
fmt.Printf("Namespace selecionado: %s; Sobrescrito: %t\n", namespace, overridden)
nodeList, err := client.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
if err != nil {
panic(err)
}
for _, node := range nodeList.Items {
fmt.Println(node.Name)
}
}
Ao adotar esse padrão, você garante que as ferramentas internas da sua infraestrutura ofereçam a mesma experiência operacional do ecossistema oficial, reduzindo a curva de aprendizado para o time de engenharia.
Artigo originalmente publicado por Stephen Kitt (Red Hat) em Kubernetes Blog.