Kubernetes-API-Delphi-Client aus OpenAPI

Jeder Kubernetes-Cluster beschreibt seine eigene API, und sgcOpenAPI macht aus dieser Beschreibung eine Pascal-Unit, die du aus Delphi heraus aufrufst. Das ist die größte der öffentlichen Spezifikationen auf diesen Seiten, und die einzige, bei der die Wahl des Quelldokuments das Ergebnis tatsächlich verändert. Deshalb sagt diese Seite, aus welchem Endpunkt du generierst und welche zwei Grenzen bekannt sind.

Kubernetes + sgcOpenAPI

Alles weiter unten ist nicht geschätzt, sondern gemessen: der Generator lief über die Kubernetes-Spezifikationen und das Ergebnis wurde kompiliert.

Generiere hieraus

Aus den OpenAPI-3-Dokumenten pro Gruppe unter /openapi/v3, eines nach dem anderen. Das ist der Weg, der einen korrekten Client ergibt.

Nicht hieraus

Aus dem aggregierten Dokument /openapi/v2. Es ist Swagger 2.0, es konvertiert und generiert, aber zwei Dinge kommen falsch heraus. Beide stehen weiter unten.

Authentifizierung

Generiere mit -a 2 und setze Authentication.Token.BearerToken, damit wird der ServiceAccount-Token als Authorization: Bearer gesendet.

Basis-URL

Kubernetes deklariert keinen Server-Host, übergib also beim Generieren -u https://cluster:6443 oder ruf zur Laufzeit SetBaseURL auf.

/openapi/v3 ist ein Index, kein Dokument

Das ist das Erste, was du verstehen solltest. Ein Cluster liefert keine einzelne OpenAPI-3-Datei aus. /openapi/v3 gibt eine Liste von Gruppenpfaden zurück, und jede Gruppe mit ihrer Version ist ein eigenes Dokument unter einer eigenen URL.

# ein Dokument pro API-Gruppe und Version, 65 davon in einem Standard-Cluster
> 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

So generiert, ergibt die Core-Gruppe 113 Pfade, 248 Methoden und 293 Modellklassen in einer Unit von 42.705 Zeilen, und die Apps-Gruppe 38 Pfade, 77 Methoden und 180 Modellklassen in 17.061 Zeilen. Beide bauen sauber unter RAD Studio 12 für Win32, mit nichts im Bibliothekspfad außer dem Source-Ordner von sgcOpenAPI. Eine Unit pro Gruppe lebt sich außerdem angenehmer als eine einzige riesige Unit, und du generierst nur die Gruppen, die du wirklich aufrufst.

Dieselben Dokumente liegen im Kubernetes-Repository unter api/openapi-spec/v3/, du kannst also aus einer eingecheckten Datei statt aus einem laufenden Cluster generieren und das Ergebnis unter Versionskontrolle halten.

Pods in einem Namespace auflisten

Kubernetes schreibt seine Operation-IDs bereits als gültige Bezeichner, die generierte Methode behält den Namen aus der Spezifikation also bei.

uses
  k8s_core_v1;   // die gerade generierte Unit

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, dann die Query-Parameter in Deklarationsreihenfolge
  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;

Die Modellklassen tragen die vollständigen Kubernetes-Namen, ein Pod ist also TsgcOpenAPI_io_k8s_api_core_v1_Pod_Class mit Metadata, Spec und Status, und die Liste ist TsgcOpenAPI_io_k8s_api_core_v1_PodList_Class mit einem typisierten Items. Ein boolescher Query-Parameter, den Kubernetes als optional deklariert, wird zu TsgcOpenAPIBoolean, dessen Wert oapiBoolNull bedeutet, dass der Parameter aus der URL wegbleibt statt als false gesendet zu werden.

Ein Deployment skalieren

Wo Kubernetes einen Request-Body mit einem benannten Schema beschreibt, und das tut es meistens, nimmt die generierte Methode die typisierte Klasse entgegen statt eines Strings.

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;

Über die gesamte Kubernetes-Oberfläche hinweg ist die Aufteilung nahezu ausgeglichen: rund die Hälfte der Request-Bodies kommt als typisierte Klasse an, etwa TsgcOpenAPI_io_k8s_api_autoscaling_v1_Scale_Class, und rund die Hälfte als String, je nachdem, ob das Dokument das Schema benennt.

Was die generierten Units enthalten

Die Unit spiegelt das Dokument, auf das du zeigst, die Abdeckung ist also genau die Gruppe und Version, aus der du generiert hast.

Workloads

apis/apps/v1 für Deployments, StatefulSets, DaemonSets und ReplicaSets. apis/batch/v1 für Jobs und CronJobs. api/v1 für Pods und ReplicationController.

Services und Networking

api/v1 für Services, Endpoints, ConfigMaps und Secrets. apis/networking.k8s.io/v1 für Ingress und NetworkPolicy. apis/discovery.k8s.io/v1 für EndpointSlices.

Storage, RBAC und Policy

apis/storage.k8s.io/v1, apis/rbac.authorization.k8s.io/v1, apis/policy/v1 und apis/admissionregistration.k8s.io/v1, jeweils ein eigenes Dokument und jeweils eine eigene Unit.

Custom Resources

Ein Cluster mit installierten CRDs liefert sie unter ihren eigenen Gruppenpfaden aus, der Index unter /openapi/v3 führt sie also neben den eingebauten Gruppen auf, und sie generieren genauso.

Jedes Modell als Klasse

ObjectMeta, PodSpec, PodStatus, Container, die Condition-Typen, die List-Typen. Allein die Core-Gruppe bringt 293 davon hervor.

Die Dokumentation aus der Spezifikation

Die Kubernetes-Feldbeschreibungen kommen als Pascal-Kommentare über jeder Methode und jeder Eigenschaft an, was zu einem großen Teil erklärt, warum die Units so lang sind.

Was ein echter Lauf zutage gefördert hat

Zwei davon betreffen nur das aggregierte Swagger-2.0-Dokument, und sie sind der Grund, warum diese Seite dir sagt, stattdessen pro Gruppe zu generieren.

Parameter auf Pfadebene gehen im v2-Dokument verloren

Kubernetes deklariert namespace und pretty einmal am Path Item und verweist mit einem $ref darauf. Aus /openapi/v2 generiert, erreichen die beiden die Methode nie, listCoreV1NamespacedPod kommt also ohne Argument aNamespace an und die URL behält ihren Platzhalter {namespace}. Aus dem /openapi/v3-Dokument derselben Gruppe generiert, ist das Argument da. Nimm die v3-Dokumente.

Die CRD-Gruppe kompiliert nicht

apis/apiextensions.k8s.io/v1 beschreibt JSONSchemaProps.enum als Array eines Schemas, das überhaupt keinen Typ deklariert, der Generator gibt deshalb TArray<> aus und der Compiler lehnt das ab. Es sind zwei Zeilen in der Unit. Lass diese Gruppe weg, oder generiere das aggregierte Dokument und übergib die betroffenen 14 Operationen zusätzlich an -x und -p, was die Klasse entfernt und die ganze Unit dann bauen lässt.

Das aggregierte Dokument ist riesig

602 Pfade, 1.202 Methoden und eine Unit von rund 197.000 Zeilen und 13 MB. Das Lesen und Generieren dauert unter einer Sekunde, aber der Editor der IDE wird bei einer Datei dieser Größe langsam. Die Units pro Gruppe sind ein Bruchteil davon, 42.705 Zeilen für die Core-Gruppe und 17.061 für Apps.

Der Cluster-CA liegt in deiner Verantwortung

Die meisten Cluster verwenden eine selbst signierte CA, der TLS-Handshake schlägt also fehl, bis du sagst, wem zu trauen ist. Der generierte Client stellt OnSSLVerifyPeer, OnSSLGetHandler und OnSSLAfterCreateHandler bereit, dort installierst du das CA-Bundle des Clusters aus deiner kubeconfig.

Watches sind keine Enumerables

watch=true und ein Container-Log mit follow=true sind langlebige Chunked-Antworten. Die generierte Methode ist ein gewöhnlicher Request, der zurückkehrt, wenn die Antwort endet, ein laufender Watch ist also etwas, das du auf TsgcHTTP1Client aufbaust, und nichts, was der generierte Client für dich streamt.

Gebundene ServiceAccount-Tokens laufen ab

Seit Kubernetes 1.21 sind sie zeitlich begrenzt. Lies /var/run/secrets/kubernetes.io/serviceaccount/token von innerhalb des Clusters erneut, oder ruf von außen die TokenRequest-API auf, und weise Authentication.Token.BearerToken erneut zu. Der Client hält, was du ihm zuletzt gegeben hast.

Aus dem Blog

OpenAPI-Delphi-Parser

Wie der Parser reale Spezifikationen handhabt, und was er in Warnings festhält, wenn er etwas nicht umsetzen kann.

Beitrag lesen →

OpenAPI-Client und -Parser

Der Begleitbeitrag, der den generierten Client vorstellt und den Parser, auf dem er aufsetzt.

Beitrag lesen →

sgcOpenAPI 2026.6

Release Notes zur aktuellen Version, mit den Generator-Optionen und den Änderungen am Parser.

Beitrag lesen →
Bestes Preis-Leistungs-Verhältnis: All-AccessAlle eSeGeCe-Produkte, inklusive Premium-Support, ab €1,059 pro Jahr.
All-Access-Preise ansehen

Steuere Kubernetes noch heute aus Delphi

sgcOpenAPI liefert den Parser, den Codegenerator, den OpenAPI-Server und fertige SDKs für Amazon, Azure, Google und Microsoft mit. Ein Produkt, drei Stufen, nach Arbeitsplatz statt nach Funktionsumfang bepreist.