Cliente Delphi para a API do Kubernetes a partir do OpenAPI

Todo cluster Kubernetes descreve a sua própria API, e o sgcOpenAPI transforma essa descrição em uma unit Pascal que você chama do Delphi. Esta é a maior das especificações públicas destas páginas, e aquela em que a escolha do documento de origem realmente muda o resultado, então esta página diz de qual endpoint gerar e quais são os dois limites conhecidos.

Kubernetes + sgcOpenAPI

Tudo abaixo foi medido rodando o gerador sobre as especificações do Kubernetes e compilando o resultado, não foi estimado.

Gere a partir daqui

Os documentos OpenAPI 3 por grupo em /openapi/v3, um de cada vez. Esse é o caminho que produz um cliente correto.

Não a partir daqui

O documento agregado /openapi/v2. Ele é Swagger 2.0, converte e gera, mas duas coisas saem erradas. As duas estão descritas abaixo.

Autenticação

Gere com -a 2 e defina Authentication.Token.BearerToken, que envia o token da ServiceAccount como Authorization: Bearer.

URL base

O Kubernetes não declara host de servidor, então passe -u https://cluster:6443 na geração ou chame SetBaseURL em tempo de execução.

/openapi/v3 é um índice, não um documento

Essa é a primeira coisa a entender. Um cluster não serve um único arquivo OpenAPI 3. O /openapi/v3 devolve uma lista de caminhos de grupo, e cada grupo e versão é um documento separado na sua própria URL.

# um documento por grupo e versão da API, 65 deles em um cluster padrão
> sgcOpenAPI.exe -i "https://10.0.0.1:6443/openapi/v3/api/v1" \
      -o "k8s_core_v1.pas" -a 2 -u "https://10.0.0.1:6443"

File successfully created k8s_core_v1.pas

> sgcOpenAPI.exe -i "https://10.0.0.1:6443/openapi/v3/apis/apps/v1" \
      -o "k8s_apps_v1.pas" -a 2 -u "https://10.0.0.1:6443"

File successfully created k8s_apps_v1.pas

Gerado dessa forma, o grupo core dá 113 paths, 248 métodos e 293 classes de modelo em uma unit de 42.705 linhas, e o grupo apps dá 38 paths, 77 métodos e 180 classes de modelo em 17.061 linhas. Os dois compilam limpos no RAD Studio 12 para Win32 sem nada no library path além da pasta Source do sgcOpenAPI. Uma unit por grupo também é mais fácil de conviver do que uma unit enorme, e você gera apenas os grupos que realmente chama.

Os mesmos documentos são publicados no repositório do Kubernetes em api/openapi-spec/v3/, então você pode gerar a partir de um arquivo versionado em vez de um cluster ao vivo e manter o resultado sob controle de versão.

Liste os pods de um namespace

O Kubernetes já escreve os seus operation ids como identificadores válidos, então o método gerado mantém o nome da especificação.

uses
  k8s_core_v1;   // a unit que você acabou de gerar

procedure TfrmK8s.btnPodsClick(Sender: TObject);
var
  oResponse: TsgcOpenAPI_listCoreV1NamespacedPod_Response;
  oPod: TsgcOpenAPI_io_k8s_api_core_v1_Pod_Class;
begin
  GetOpenAPIClient.Authentication.Token.BearerToken := vToken;
  GetOpenAPIClient.SetBaseURL('https://10.0.0.1:6443');

  // aNamespace, aPretty, depois os parâmetros de query na ordem declarada
  oResponse := GetOpenAPIClient.listCoreV1NamespacedPod('production',
    '', oapiBoolNull, '', '', 'app=api,tier=backend');
  try
    if oResponse.IsSuccessful then
      for oPod in oResponse.Successful.Items do
        memoLog.Lines.Add(Format('%-30s %s %s',
          [oPod.Metadata.Name, oPod.Status.Phase, oPod.Status.HostIP]))
    else
      memoLog.Lines.Add(IntToStr(oResponse.ResponseCode) + ' ' +
        oResponse.ResponseError);
  finally
    oResponse.Free;
  end;
end;

As classes de modelo carregam os nomes completos do Kubernetes, então um pod é TsgcOpenAPI_io_k8s_api_core_v1_Pod_Class com Metadata, Spec e Status, e a lista é TsgcOpenAPI_io_k8s_api_core_v1_PodList_Class com um Items tipado. Um parâmetro de query booleano que o Kubernetes declara como opcional vira TsgcOpenAPIBoolean, cujo valor oapiBoolNull significa que o parâmetro fica fora da URL em vez de ser enviado como false.

Escale um deployment

Onde o Kubernetes descreve um corpo de requisição com um schema nomeado, e ele geralmente descreve, o método gerado recebe a classe tipada em vez de uma string.

uses
  k8s_apps_v1;

var
  oRead: TsgcOpenAPI_readAppsV1NamespacedDeploymentScale_Response;
  oWrite: TsgcOpenAPI_replaceAppsV1NamespacedDeploymentScale_Response;
begin
  oRead := GetOpenAPIClient.readAppsV1NamespacedDeploymentScale(
    'api', 'production');
  try
    if not oRead.IsSuccessful then
      raise Exception.Create(oRead.ResponseError);

    oRead.Successful.Spec.Replicas := 5;

    oWrite := GetOpenAPIClient.replaceAppsV1NamespacedDeploymentScale(
      'api', 'production', oRead.Successful);
    try
      memoLog.Lines.Add(IntToStr(oWrite.ResponseCode));
    finally
      oWrite.Free;
    end;
  finally
    oRead.Free;
  end;
end;

Em toda a superfície do Kubernetes a divisão é quase meio a meio, cerca de metade dos corpos de requisição chega como uma classe tipada, do tipo TsgcOpenAPI_io_k8s_api_autoscaling_v1_Scale_Class, e cerca de metade chega como string, conforme o documento nomeie ou não o schema.

O que as units geradas contêm

A unit espelha o documento para o qual você aponta, então a cobertura é exatamente o grupo e a versão que você gerou.

Workloads

apis/apps/v1 para Deployments, StatefulSets, DaemonSets e ReplicaSets. apis/batch/v1 para Jobs e CronJobs. api/v1 para Pods e ReplicationControllers.

Services e networking

api/v1 para Services, Endpoints, ConfigMaps e Secrets. apis/networking.k8s.io/v1 para Ingress e NetworkPolicy. apis/discovery.k8s.io/v1 para EndpointSlices.

Storage, RBAC e policy

apis/storage.k8s.io/v1, apis/rbac.authorization.k8s.io/v1, apis/policy/v1 e apis/admissionregistration.k8s.io/v1, cada um com o seu documento e cada um com a sua unit.

Custom resources

Um cluster com CRDs instalados os serve nos seus próprios caminhos de grupo, então o índice em /openapi/v3 os lista ao lado dos grupos nativos e eles são gerados da mesma forma.

Cada modelo como uma classe

ObjectMeta, PodSpec, PodStatus, Container, os tipos de condição, os tipos de lista. Só o grupo core produz 293 deles.

A documentação da spec

As descrições de campo do Kubernetes vêm como comentários Pascal acima de cada método e de cada propriedade, o que é boa parte do motivo pelo qual as units são longas.

O que encontramos rodando o gerador

Dois destes são específicos do documento Swagger 2.0 agregado, e são o motivo pelo qual esta página manda gerar por grupo.

Parâmetros no nível do path se perdem no documento v2

O Kubernetes declara namespace e pretty uma vez no path item e os referencia com um $ref. Gerados a partir do /openapi/v2, esses dois nunca chegam ao método, então o listCoreV1NamespacedPod chega sem o argumento aNamespace e a URL mantém o marcador {namespace}. Gerado a partir do documento /openapi/v3 do mesmo grupo, o argumento está lá. Use os documentos v3.

O grupo de CRDs não compila

O apis/apiextensions.k8s.io/v1 descreve JSONSchemaProps.enum como um array de um schema que não declara tipo algum, então o gerador emite TArray<> e o compilador rejeita. São duas linhas na unit. Pule esse grupo, ou gere o documento agregado passando também aquelas 14 operações para -x e -p, o que remove a classe e aí a unit inteira compila.

O documento agregado é enorme

602 paths, 1.202 métodos e uma unit de cerca de 197.000 linhas e 13 MB. Ele é lido e gerado em menos de um segundo, mas o editor da IDE fica lento com um arquivo desse tamanho. As units por grupo são uma fração disso, 42.705 linhas para o grupo core e 17.061 para o apps.

A CA do cluster fica por sua conta

A maioria dos clusters usa uma CA autoassinada, então o handshake TLS falha até você dizer no que confiar. O cliente gerado expõe OnSSLVerifyPeer, OnSSLGetHandler e OnSSLAfterCreateHandler, que é onde você instala o bundle da CA do cluster vindo do seu kubeconfig.

Watches não são enumeráveis

watch=true e um log de container com follow=true são respostas em chunks de vida longa. O método gerado é uma requisição comum que retorna quando a resposta termina, então um watch ao vivo é algo que você constrói sobre o TsgcHTTP1Client, não algo que o cliente gerado transmite para você.

Tokens de ServiceAccount vinculados expiram

Eles têm tempo limitado desde o Kubernetes 1.21. Releia o /var/run/secrets/kubernetes.io/serviceaccount/token de dentro do cluster, ou chame a API TokenRequest de fora, e atribua Authentication.Token.BearerToken de novo. O cliente guarda o que você deu por último.

Do blog

Parser OpenAPI Delphi

Como o leitor lida com especificações reais, e o que ele registra em Warnings quando não consegue atender a algo.

Leia o post →

Cliente e parser OpenAPI

O post companheiro que apresenta o cliente gerado e o leitor sobre o qual ele é construído.

Leia o post →

sgcOpenAPI 2026.6

Release notes da versão atual, com as opções do gerador e as mudanças do leitor.

Leia o post →
Melhor custo-benefício: All-AccessTodos os produtos da eSeGeCe, com Suporte Premium incluído, a partir de €1,059/ano.
Ver preços do All-Access

Dirija o Kubernetes a partir do Delphi hoje

O sgcOpenAPI entrega o leitor, o gerador de código, o servidor OpenAPI e SDKs pré-compilados para Amazon, Azure, Google e Microsoft. Um produto, três tiers, com preço por assento em vez de por recurso.