Cliente Delphi de la API de Kubernetes desde OpenAPI

Cada cluster Kubernetes describe su propia API, y sgcOpenAPI convierte esa descripción en una unit Pascal que llamas desde Delphi. Es la mayor de las especificaciones públicas de estas páginas, y también aquella en la que la elección del documento de origen sí cambia el resultado, así que esta página te dice desde qué endpoint generar y cuáles son los dos límites conocidos.

Kubernetes + sgcOpenAPI

Todo lo que sigue se midió ejecutando el generador sobre las especificaciones de Kubernetes y compilando el resultado, no son estimaciones.

Genera desde esto

Los documentos OpenAPI 3 por grupo que hay bajo /openapi/v3, de uno en uno. Esta es la vía que produce un cliente correcto.

No desde esto

El documento agregado /openapi/v2. Es Swagger 2.0, se convierte y se genera, pero dos cosas salen mal. Las dos se describen más abajo.

Autenticación

Genera con -a 2 y asigna Authentication.Token.BearerToken, que envía el token de la ServiceAccount como Authorization: Bearer.

URL base

Kubernetes no declara ningún host de servidor, así que pasa -u https://cluster:6443 al generar o llama a SetBaseURL en tiempo de ejecución.

/openapi/v3 es un índice, no un documento

Esto es lo primero que hay que entender. Un cluster no sirve un único archivo OpenAPI 3. /openapi/v3 devuelve una lista de rutas de grupo, y cada grupo y versión es un documento aparte en su propia URL.

# un documento por grupo y versión de API, 65 en un cluster estándar
> 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

Generado así, el grupo core da 113 paths, 248 métodos y 293 clases de modelo en una unit de 42.705 líneas, y el grupo apps da 38 paths, 77 métodos y 180 clases de modelo en 17.061 líneas. Los dos compilan limpios en RAD Studio 12 para Win32 sin nada en la ruta de librerías salvo la carpeta Source de sgcOpenAPI. Una unit por grupo también es más llevadera que una unit enorme, y solo generas los grupos que realmente llamas.

Los mismos documentos están publicados en el repositorio de Kubernetes bajo api/openapi-spec/v3/, así que puedes generar desde un archivo versionado en lugar de desde un cluster en marcha y mantener el resultado bajo control de versiones.

Lista los pods de un namespace

Kubernetes ya escribe sus operation ids como identificadores válidos, así que el método generado conserva el nombre que trae la especificación.

uses
  k8s_core_v1;   // la unit que acabas de generar

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 y luego los parámetros de query en orden
  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;

Las clases de modelo llevan los nombres completos de Kubernetes, así que un pod es TsgcOpenAPI_io_k8s_api_core_v1_Pod_Class con Metadata, Spec y Status, y la lista es TsgcOpenAPI_io_k8s_api_core_v1_PodList_Class con un Items tipado. Un parámetro de query booleano que Kubernetes declara como opcional se convierte en TsgcOpenAPIBoolean, cuyo valor oapiBoolNull significa que el parámetro se deja fuera de la URL en lugar de enviarse como false.

Escala un deployment

Donde Kubernetes describe el cuerpo de la petición con un esquema con nombre, y suele hacerlo, el método generado recibe la clase tipada en lugar de una cadena.

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;

En toda la superficie de Kubernetes el reparto es casi mitad y mitad, aproximadamente la mitad de los cuerpos de petición llegan como una clase tipada del estilo de TsgcOpenAPI_io_k8s_api_autoscaling_v1_Scale_Class y aproximadamente la mitad como una cadena, según si el documento pone nombre al esquema.

Qué contienen las units generadas

La unit refleja el documento al que apuntas, así que la cobertura es exactamente el grupo y la versión que hayas generado.

Cargas de trabajo

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

Servicios y red

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

Almacenamiento, RBAC y políticas

apis/storage.k8s.io/v1, apis/rbac.authorization.k8s.io/v1, apis/policy/v1 y apis/admissionregistration.k8s.io/v1, cada uno su propio documento y cada uno su propia unit.

Recursos personalizados

Un cluster con CRDs instalados los sirve en sus propias rutas de grupo, así que el índice de /openapi/v3 los lista junto a los grupos integrados y se generan igual.

Cada modelo como una clase

ObjectMeta, PodSpec, PodStatus, Container, los tipos de condición, los tipos de lista. Solo el grupo core produce 293 de ellas.

La documentación de la spec

Las descripciones de campo de Kubernetes llegan como comentarios Pascal encima de cada método y cada propiedad, que es buena parte del motivo de que las units sean tan largas.

Lo que encontramos al ejecutarlo

Dos de estos puntos son propios del documento Swagger 2.0 agregado, y son la razón por la que esta página te dice que generes por grupo.

El documento v2 pierde los parámetros a nivel de path

Kubernetes declara namespace y pretty una sola vez en el path item y los referencia con un $ref. Generados desde /openapi/v2, esos dos nunca llegan al método, así que listCoreV1NamespacedPod aparece sin argumento aNamespace y la URL conserva su marcador {namespace}. Generado desde el documento /openapi/v3 del mismo grupo, el argumento está. Usa los documentos v3.

El grupo de las CRD no compila

apis/apiextensions.k8s.io/v1 describe JSONSchemaProps.enum como un array de un esquema que no declara tipo alguno, así que el generador emite TArray<> y el compilador lo rechaza. Son dos líneas de la unit. Sáltate ese grupo, o genera el documento agregado pasando también esas 14 operaciones a -x y a -p, lo que elimina la clase y hace que la unit entera compile.

El documento agregado es enorme

602 paths, 1.202 métodos y una unit de unas 197.000 líneas y 13 MB. Se lee y se genera en menos de un segundo, pero el editor del IDE va lento con un archivo de ese tamaño. Las units por grupo son una fracción de eso, 42.705 líneas para el grupo core y 17.061 para apps.

La CA del cluster es cosa tuya

La mayoría de los clusters usan una CA autofirmada, así que el handshake TLS falla hasta que dices en qué confiar. El cliente generado expone OnSSLVerifyPeer, OnSSLGetHandler y OnSSLAfterCreateHandler, que es donde instalas el bundle de CA del cluster que tienes en tu kubeconfig.

Los watches no son enumerables

watch=true y el log de un contenedor con follow=true son respuestas chunked de larga duración. El método generado es una petición corriente que retorna cuando la respuesta termina, así que un watch en vivo es algo que construyes sobre TsgcHTTP1Client, no algo que el cliente generado te sirva en streaming.

Los tokens acotados de ServiceAccount caducan

Tienen tiempo limitado desde Kubernetes 1.21. Vuelve a leer /var/run/secrets/kubernetes.io/serviceaccount/token desde dentro del cluster, o llama a la API TokenRequest desde fuera, y asigna otra vez Authentication.Token.BearerToken. El cliente conserva lo último que le hayas dado.

Desde el blog

Parser OpenAPI Delphi

Cómo el lector maneja especificaciones reales, y qué anota en Warnings cuando no puede respetar algo.

Leer post →

Cliente OpenAPI + parser

El post complementario que presenta el cliente generado y el lector sobre el que se apoya.

Leer post →

sgcOpenAPI 2026.6

Notas de la versión actual, con las opciones del generador y los cambios del lector.

Leer post →
La mejor opción: All-AccessTodos los productos de eSeGeCe, con Premium Support incluido, desde €1,059 al año.
Ver precios de All-Access

Conduce Kubernetes desde Delphi hoy

sgcOpenAPI incluye el lector, el generador de código, el servidor OpenAPI y los SDK ya construidos de Amazon, Azure, Google y Microsoft. Un producto, tres niveles, con precio por puesto y no por funcionalidad.