Kubernetes API Delphi Client from OpenAPI

Every Kubernetes cluster describes its own API, and sgcOpenAPI turns that description into a Pascal unit you call from Delphi. This is the largest of the public specifications on these pages, and the one where the choice of source document actually changes the result, so this page says which endpoint to generate from and what the two known limits are.

Kubernetes + sgcOpenAPI

Everything below was measured by running the generator over the Kubernetes specifications and compiling the result, not estimated.

Generate from this

The per group OpenAPI 3 documents under /openapi/v3, one at a time. This is the route that produces a correct client.

Not from this

The aggregated /openapi/v2 document. It is Swagger 2.0, it converts and generates, but two things come out wrong. Both are described below.

Authentication

Generate with -a 2 and set Authentication.Token.BearerToken, which sends the ServiceAccount token as Authorization: Bearer.

Base URL

Kubernetes declares no server host, so pass -u https://cluster:6443 at generation time or call SetBaseURL at run time.

/openapi/v3 is an index, not a document

This is the first thing to understand. A cluster does not serve one OpenAPI 3 file. /openapi/v3 returns a list of group paths, and each group and version is a separate document at its own URL.

# one document per API group and version, 65 of them in a stock 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

Generated that way, the core group gives 113 paths, 248 methods and 293 model classes in a unit of 42,705 lines, and the apps group gives 38 paths, 77 methods and 180 model classes in 17,061 lines. Both compile clean on RAD Studio 12 for Win32 with nothing on the library path except the sgcOpenAPI Source folder. One unit per group is also easier to live with than one enormous unit, and you only generate the groups you actually call.

The same documents are published in the Kubernetes repository under api/openapi-spec/v3/, so you can generate from a checked in file rather than from a live cluster and keep the result under version control.

List pods in a namespace

Kubernetes writes its operation ids as valid identifiers already, so the generated method keeps the name from the specification.

uses
  k8s_core_v1;   // the unit you just generated

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, then the query parameters in declaration order
  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;

The model classes carry the full Kubernetes names, so a pod is TsgcOpenAPI_io_k8s_api_core_v1_Pod_Class with Metadata, Spec and Status, and the list is TsgcOpenAPI_io_k8s_api_core_v1_PodList_Class with a typed Items. A boolean query parameter that Kubernetes declares as optional becomes TsgcOpenAPIBoolean, whose oapiBoolNull value means the parameter is left off the URL rather than sent as false.

Scale a deployment

Where Kubernetes describes a request body with a named schema, and it usually does, the generated method takes the typed class rather than a 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;

Across the whole Kubernetes surface the split is close to even, roughly half the request bodies arrive as a typed class such as TsgcOpenAPI_io_k8s_api_autoscaling_v1_Scale_Class and roughly half as a string, depending on whether the document names the schema.

What the generated units contain

The unit mirrors the document you point at, so the coverage is exactly the group and version you generated.

Workloads

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

Services and networking

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

Storage, RBAC and policy

apis/storage.k8s.io/v1, apis/rbac.authorization.k8s.io/v1, apis/policy/v1 and apis/admissionregistration.k8s.io/v1, each its own document and each its own unit.

Custom resources

A cluster with CRDs installed serves them at their own group paths, so the index at /openapi/v3 lists them alongside the built in groups and they generate the same way.

Every model as a class

ObjectMeta, PodSpec, PodStatus, Container, the condition types, the list types. The core group alone produces 293 of them.

The documentation from the spec

The Kubernetes field descriptions come through as Pascal comments above each method and property, which is a large part of why the units are long.

What we found by running it

Two of these are specific to the aggregated Swagger 2.0 document, and they are the reason this page tells you to generate per group instead.

Path level parameters are lost from the v2 document

Kubernetes declares namespace and pretty once on the path item and references them with a $ref. Generated from /openapi/v2, those two never reach the method, so listCoreV1NamespacedPod arrives without an aNamespace argument and the URL keeps its {namespace} placeholder. Generated from the /openapi/v3 document for the same group, the argument is there. Use the v3 documents.

The CRD group does not compile

apis/apiextensions.k8s.io/v1 describes JSONSchemaProps.enum as an array of a schema that declares no type at all, so the generator emits TArray<> and the compiler rejects it. It is two lines in the unit. Skip that group, or generate the aggregated document with those 14 operations passed to -x and -p as well, which removes the class and the whole unit then builds.

The aggregated document is enormous

602 paths, 1,202 methods and a unit of about 197,000 lines and 13 MB. It reads and generates in under a second, but the IDE editor is slow with a file that size. The per group units are a fraction of that, 42,705 lines for the core group and 17,061 for apps.

The cluster CA is yours to trust

Most clusters use a self signed CA, so the TLS handshake fails until you say what to trust. The generated client exposes OnSSLVerifyPeer, OnSSLGetHandler and OnSSLAfterCreateHandler, which is where you install the cluster CA bundle from your kubeconfig.

Watches are not enumerables

watch=true and a container log with follow=true are long lived chunked responses. The generated method is an ordinary request that returns when the response ends, so a live watch is something you build on TsgcHTTP1Client rather than something the generated client streams for you.

Bound ServiceAccount tokens expire

They have been time limited since Kubernetes 1.21. Re read /var/run/secrets/kubernetes.io/serviceaccount/token from inside the cluster, or call the TokenRequest API from outside, and assign Authentication.Token.BearerToken again. The client holds whatever you last gave it.

From the blog

OpenAPI Delphi parser

How the reader handles real specifications, and what it records in Warnings when it cannot honour something.

Read post →

OpenAPI client and parser

The companion post that introduces the generated client and the reader it is built on.

Read post →

sgcOpenAPI 2026.6

Release notes for the current version, with the generator options and reader changes.

Read post →
Best value: All-AccessEvery eSeGeCe product, Premium Support included, from €1,059/year.
See All-Access pricing

Drive Kubernetes from Delphi today

sgcOpenAPI ships the reader, the code generator, the OpenAPI server and pre-built SDKs for Amazon, Azure, Google and Microsoft. One product, three tiers, priced by seat rather than by feature.