OpenAPI から Kubernetes API Delphi クライアント

すべての Kubernetes クラスタは自分自身の API を記述しており、sgcOpenAPI はその記述を、Delphi から呼び出せる Pascal ユニットに変換します。これはこれらのページで扱う公開仕様の中で最大のものであり、ソースドキュメントの選び方が結果を実際に変えてしまう唯一のものでもあります。そこでこのページでは、どのエンドポイントから生成すべきか、そして既知の 2 つの限界が何かを説明します。

Kubernetes + sgcOpenAPI

以下はすべて、Kubernetes の仕様に対してジェネレーターを実行し、その出力をコンパイルして計測したものです。推定値ではありません。

こちらから生成します

/openapi/v3 の下にあるグループ単位の OpenAPI 3 ドキュメントを、1 つずつ。正しいクライアントが得られるのはこの経路です。

こちらからは生成しません

集約された /openapi/v2 ドキュメント。これは Swagger 2.0 で、変換も生成も通りますが、2 つの点が誤った形で出力されます。どちらも下で説明します。

認証

-a 2 を付けて生成し、Authentication.Token.BearerToken を設定します。これで ServiceAccount のトークンが Authorization: Bearer として送られます。

ベース URL

Kubernetes はサーバーホストを宣言しないので、生成時に -u https://cluster:6443 を渡すか、実行時に SetBaseURL を呼び出してください。

/openapi/v3 はドキュメントではなくインデックス

まず理解しておきたい点です。クラスタは 1 つの OpenAPI 3 ファイルを配信するわけではありません。/openapi/v3 はグループパスの一覧を返し、グループとバージョンの組ごとに、それぞれの URL に別々のドキュメントが置かれています。

# API グループとバージョンごとに 1 ドキュメント、標準的なクラスタでは 65 個
> 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

この方法で生成すると、core グループは 113 個のパス、248 個のメソッド、293 個のモデルクラスとなり、42,705 行のユニットになります。apps グループは 38 個のパス、77 個のメソッド、180 個のモデルクラスで、17,061 行です。どちらも、ライブラリパスに sgcOpenAPI の Source フォルダー以外を何も置かない状態で、RAD Studio 12 の Win32 向けにクリーンにコンパイルされます。グループごとに 1 ユニットという形は巨大な 1 ユニットより扱いやすく、実際に呼び出すグループだけを生成すれば済みます。

同じドキュメントは Kubernetes のリポジトリの api/openapi-spec/v3/ にも公開されているので、稼働中のクラスタではなくチェックイン済みのファイルから生成し、その結果をバージョン管理下に置くこともできます。

名前空間内の Pod を一覧表示

Kubernetes は操作 ID をもとから有効な識別子として書いているので、生成されるメソッドは仕様どおりの名前を保ちます。

uses
  k8s_core_v1;   // 生成したユニット

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、続いてクエリパラメーターが宣言順に並ぶ
  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;

モデルクラスは Kubernetes の完全な名前を保持するので、Pod は MetadataSpecStatus を持つ TsgcOpenAPI_io_k8s_api_core_v1_Pod_Class となり、そのリストは型付きの Items を持つ TsgcOpenAPI_io_k8s_api_core_v1_PodList_Class になります。Kubernetes が省略可能として宣言したブール型のクエリパラメーターは TsgcOpenAPIBoolean になり、その oapiBoolNull という値は、false として送るのではなく URL からそのパラメーターを外すことを意味します。

Deployment をスケーリング

Kubernetes がリクエストボディを名前付きスキーマで記述している場合、そしてたいていはそうなのですが、生成されるメソッドは文字列ではなく型付きクラスを受け取ります。

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;

Kubernetes の API 全体では割合はほぼ半々で、リクエストボディの約半分は TsgcOpenAPI_io_k8s_api_autoscaling_v1_Scale_Class のような型付きクラスとして、残りの約半分は文字列として渡ってきます。どちらになるかは、ドキュメントがそのスキーマに名前を付けているかどうかで決まります。

生成ユニットに含まれるもの

ユニットは指定したドキュメントをそのまま写すので、網羅範囲はまさに生成したグループとバージョンの分だけです。

ワークロード

Deployment、StatefulSet、DaemonSet、ReplicaSet は apis/apps/v1。Job と CronJob は apis/batch/v1。Pod と ReplicationController は api/v1

サービスとネットワーキング

Service、Endpoints、ConfigMap、Secret は api/v1。Ingress と NetworkPolicy は apis/networking.k8s.io/v1。EndpointSlice は apis/discovery.k8s.io/v1

ストレージ、RBAC、ポリシー

apis/storage.k8s.io/v1apis/rbac.authorization.k8s.io/v1apis/policy/v1apis/admissionregistration.k8s.io/v1。それぞれが独立したドキュメントであり、それぞれが 1 つのユニットになります。

カスタムリソース

CRD を導入したクラスタは、それらを独自のグループパスで配信します。そのため /openapi/v3 のインデックスには組み込みのグループと並んで載り、生成方法も同じです。

すべてのモデルがクラスに

ObjectMeta、PodSpec、PodStatus、Container、各種の condition 型、各種のリスト型。core グループだけで 293 個が生成されます。

仕様に書かれたドキュメント

Kubernetes のフィールド説明は、各メソッドと各プロパティの上に Pascal のコメントとして引き継がれます。ユニットが長くなる理由の大きな部分がこれです。

実行して分かったこと

このうち 2 つは集約された Swagger 2.0 ドキュメントに固有のもので、このページがグループ単位の生成を勧めている理由でもあります。

v2 ドキュメントではパスレベルのパラメーターが失われます

Kubernetes は namespacepretty をパスアイテム側で 1 度だけ宣言し、$ref で参照しています。/openapi/v2 から生成すると、この 2 つはメソッドまで届かず、listCoreV1NamespacedPodaNamespace 引数を持たないまま現れ、URL には {namespace} のプレースホルダーが残ります。同じグループの /openapi/v3 ドキュメントから生成すれば引数は付きます。v3 のドキュメントを使ってください。

CRD のグループはコンパイルできません

apis/apiextensions.k8s.io/v1JSONSchemaProps.enum を、型をまったく宣言していないスキーマの配列として記述しています。そのためジェネレーターは TArray<> を出力し、コンパイラーはこれを受け付けません。ユニット内では 2 行です。そのグループを飛ばすか、集約ドキュメントを生成する際に該当する 14 個の操作を -x-p にも渡してください。そうすればクラスが取り除かれ、ユニット全体がビルドできます。

集約ドキュメントは巨大です

602 個のパス、1,202 個のメソッド、そして約 197,000 行、13 MB のユニットになります。読み込みと生成は 1 秒もかかりませんが、このサイズのファイルでは IDE のエディターが重くなります。グループ単位のユニットはその何分の一かで、core グループは 42,705 行、apps は 17,061 行です。

クラスタの CA は自分で信頼します

多くのクラスタは自己署名の CA を使うので、何を信頼するかを指定するまで TLS ハンドシェイクは失敗します。生成されたクライアントは OnSSLVerifyPeerOnSSLGetHandlerOnSSLAfterCreateHandler を公開しており、kubeconfig にあるクラスタの CA バンドルはそこで組み込みます。

Watch は列挙可能ではありません

watch=true と、follow=true を付けたコンテナログは、長時間続くチャンク形式のレスポンスです。生成されたメソッドはレスポンスが終わったところで戻る通常のリクエストなので、ライブの watch は生成クライアントがストリーミングしてくれるものではなく、TsgcHTTP1Client の上に自分で組み立てるものになります。

バインドされた ServiceAccount トークンは期限切れになります

Kubernetes 1.21 以降、これらには期限が設けられています。クラスタ内部からは /var/run/secrets/kubernetes.io/serviceaccount/token を読み直し、外部からは TokenRequest API を呼び出して、Authentication.Token.BearerToken に再度代入してください。クライアントは最後に渡されたものを保持するだけです。

ブログから

OpenAPI Delphi パーサー

リーダーが実際の仕様をどう扱うか。対応できないものを Warnings にどう記録するか。

投稿を読む →

OpenAPI クライアント + パーサー

生成されたクライアントと、その土台になっているリーダーを紹介する姉妹投稿。

投稿を読む →

sgcOpenAPI 2026.6

現行バージョンのリリースノート。ジェネレーターのオプションとリーダーの変更点を掲載しています。

投稿を読む →
最もお得な選択: All-AccesseSeGeCe の全製品にプレミアムサポートが付いて、年間 €1,059 からご利用いただけます。
All-Access の価格を見る

今日 Delphi から Kubernetes を駆動

sgcOpenAPI には、リーダー、コードジェネレーター、OpenAPI サーバー、そして Amazon、Azure、Google、Microsoft 向けのビルド済み SDK が同梱されています。1 製品、3 ティア、機能単位ではなくシート単位の価格です。