1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetes 上での API 実行を自動化する CommandBuilder + Utility 実装解説【TypeScript】

Last updated at Posted at 2025-04-29

はじめに

Kubernetes 上の Pod で curl を実行して API 通信を行うためのコマンドを自動生成し、ログを記録する TypeScript 実装を紹介します。

Remoteの実装については下記の記事をご参照ください。

解決する課題

  • 複雑な curl コマンドを毎回書くのが大変
  • Pod に対しての exec 実行をスクリプト化したい
  • 実行結果をログとして残したい

使用技術

  • TypeScript
  • ssh2(Remote exec)
  • Kubernetes (kubectl exec)

コマンド生成クラス CommandBuilder

目的

Kubernetes 上の Pod に対して kubectl exec 経由で curl コマンドを組み立てるためのビルダークラス CommandBuilder を実装したものです。各メソッドが curl コマンドの一部を動的に構築します。

以下、分割して解説します。


🔹 クラス定義と初期化部分

CommandBuilder.ts
class CommandBuilder {
  private config: string[] = [];
  private command: string[] = [];
  • configkubectl exec などのベース部分を格納。
  • commandcurl コマンドのパラメータ部分を構築。

🔹 reset() メソッド

CommandBuilder.ts
public reset(): void {
  this.command = [];
}
  • command をリセット。config はそのまま保持する。

🔹 pod(podName: string) メソッド

CommandBuilder.ts
public pod(podName: string): void {
  this.reset();
  this.config.push(`kubectl exec`);
  this.config.push(podName);
  this.config.push(`-- curl -Lgkv`);
}
  • Pod 名を指定して kubectl exec + curl のコマンド基盤を構成。
  • curl-L(リダイレクト追従), -g(glob無効), -k(SSL検証無効), -v(verbose)を指定。

🔹 patch() メソッド

CommandBuilder.ts
public patch(): CommandBuilder {
  this.command.push(`-X PATCH`);
  return this;
}
  • PATCH リクエストを指定するオプション。

🔹 token(token: string) メソッド

CommandBuilder.ts
public token(token: string): CommandBuilder {
  this.command.push(`-H "Authorization: Bearer ${token}"`);
  return this;
}
  • Bearer トークンによる認証ヘッダーを追加。

🔹 header(header: string) メソッド

CommandBuilder.ts
public header(header: string): CommandBuilder {
  this.command.push(`-H "${header}"`);
  return this;
}
  • 任意のヘッダーを追加。

🔹 content(content: string = "json") メソッド

CommandBuilder.ts
public content(content: string = "json"): CommandBuilder {
  switch(content) {
    case "form":
      this.command.push(`-H 'Content-Type: multipart/form-data'`);
      break;
    case "patch":
      this.command.push(`-H 'Content-Type: application/json-patch+json'`);
      break;
    default:
      this.command.push(`-H 'Content-Type: application/json'`);
  }
  return this;
}
  • Content-Type ヘッダーを用途に応じて追加。
    • "form":ファイルアップロード用途。
    • "patch":JSON Patch。
    • それ以外:JSON 通信。

🔹 payload(payload: object) メソッド

CommandBuilder.ts
public payload(payload: object): CommandBuilder {
  this.command.push(`-d '${JSON.stringify(payload)}'`);
  return this;
}
  • リクエストボディを JSON 文字列として追加。

🔹 api(api: string) メソッド

CommandBuilder.ts
public api(api: string): CommandBuilder {
  this.command.push(`${api}`);
  return this;
}
  • 実行する API のエンドポイント URL を追加。

🔹 formMeta(meta: string) メソッド

CommandBuilder.ts
public formMeta(meta: string): CommandBuilder {
  this.command.push(`-F "${meta};type=application/json"`);
  return this;
}
  • multipart フォームの一部として JSON メタ情報を送る。

🔹 formFile(file: string) メソッド

public formFile(file: string): CommandBuilder {
  this.command.push(`-F "${file};type=application/pdf"`);
  return this;
}
  • multipart フォームの一部としてファイル(PDF)を送信。

🔹 create() メソッド

public create(): string {
  return [...this.config, ...this.command].join(" ");
}
  • kubectl exec ... curl ... の全体コマンド文字列を組み立てて返す。

🔹 最後の export

export default new CommandBuilder();
  • インスタンスをシングルトンとしてエクスポート。

✅ 使用例(イメージ)

const cmd = CommandBuilder
  .pod("my-pod")
  .patch()
  .token("abc123")
  .content("json")
  .payload({ foo: "bar" })
  .api("http://localhost/api")
  .create();

console.log(cmd);
// kubectl exec my-pod -- curl -Lgkv -X PATCH -H "Authorization: Bearer abc123" -H 'Content-Type: application/json' -d '{"foo":"bar"}' http://localhost/api

このようにして、Kubernetes 上の Pod に curl を通して API を実行するためのコマンドを柔軟に構築できます。

実行ロジックを絞り込む Utility クラス

目的

Kubernetes 上の Pod で curl コマンドを実行し、API 呼び出しとログ記録を行うユーティリティクラス Utility を定義しています。以下、機能ごとに分断して詳しく解説します。

実行フロー一覧

  1. Pod 名取得:checkPodname()
  2. Token 取得:Postman.getToken()
  3. curl 組み立て:CommandBuilder.xxx()
  4. Pod 上で実行:Remote.execRemote()
  5. 結果解析とログ記録:handleApiResponse() & PodLogger

このコードは、Kubernetes 上の Pod で curl コマンドを実行し、API 呼び出しとログ記録を行うユーティリティクラス Utility を定義しています。以下、機能ごとに分断して詳しく解説します。


🔹 インポート & 初期設定

Utility.ts
import Remote from "@class/ssh/Remote";
import CommandBuilder from "@class/builder/CommandBuilder";
import PodLogger from "@class/dto/PodLogger";
import PocketBase from "@class/db/PocketBase";
import Pod from "@class/ssh/Pod";
  • 外部 API 型やユーティリティクラスを読み込む。

🔹 @reset() デコレータの定義

Utility.ts
function reset() {
  return function (_target, _key, descriptor) {
    const original = descriptor.value;
    descriptor.value = function (...args) {
      CommandBuilder.reset();
      return original.apply(this, args);
    };
    return descriptor;
  };
}
  • メソッド実行前に CommandBuilder.reset() を自動的に挿入するデコレータ。
  • コマンドビルダーの使い回しによる副作用を防止。

🔹 Utility クラスの本体

Utility.ts
class Utility {
  public podName?: string;
  • Kubernetes の Pod 名を保持。

🔹 Pod 名取得メソッド

Utility.ts
private async checkPodname() {
  if (!this.podName) {
    const podName = (await Pod.getPodName())[0] || "";
    this.podName = podName;
    CommandBuilder.pod(podName);
  }
}
  • Pod 名が未取得の場合に取得し、CommandBuilder に設定。

🔹 updatePaymentStatus メソッド

Utility.ts
@reset()
public async updatePaymentStatus(): Promise<void> {
  this.checkPodname();

  const api = apis.UPDATE_PAYMENT_STATUS;
  const token = await Postman.getToken();
  const cmd = CommandBuilder.content("json")
    .header("correlationId:12345")
    .header("requestorUserId:random_user")
    .token(token)
    .api(api)
    .create();

  const result = await Remote.execRemote(cmd);
  const analyze = (data: { numberOfScanned: number }) => {
    if (data.numberOfScanned > 0) PodLogger.success();
  };
  await this.handleApiResponse(result, analyze);

  PodLogger.api(api);
  await PocketBase.writePodLog();
}
  • 支払ステータス更新API を呼び出すためのコマンドを組み立てて実行。
  • 成功判定後に Pod ログを保存。

🔹 uploadFile メソッド

Utility.ts
@reset()
public async uploadFile(fileName: string): Promise<string> {
  this.checkPodname();

  const api = apis.UPLOAD_FILE_TO_DMS;
  const payload = PodLogger.getPayload();
  const jsonString = JSON.stringify(payload).replace(/"/g, '\\"');
  const token = await Postman.getToken();
  const cmd = CommandBuilder.content("form")
    .token(token)
    .header("requestorUserId:123")
    .formMeta(`listOfFile=${jsonString}`)
    .formFile(`fileStream=@/tmp/${fileName}`)
    .api(api)
    .create();

  const result = await Remote.execRemote(cmd);
  let fileId = "";
  const analyze = async (res: FileResponse) => {
    if (res.responseCode === "200") {
      PodLogger.success();
      fileId = res.listOfFiles[0]!.documentIdentifier;
    } else {
      await this.uploadOrderKYC(fileName); // 再帰的リトライ
    }
  };
  await this.handleApiResponse(result, analyze);

  PodLogger.api(api);
  await PocketBase.writePodLog();
  return fileId;
}
  • ファイルを DMS にアップロードし、documentIdentifier を取得。
  • 成功時に ID を取得、失敗時はリトライ。

🔹 handleApiResponse メソッド

Utility.ts
private async handleApiResponse(
  raw: string,
  callback: (data: any) => void
): Promise<void> {
  let response;
  try {
    response = JSON.parse(raw);
    await callback(response);
  } catch {
    response = { msg: response || "" };
    PodLogger.failed();
  }
  PodLogger.response(response);
}
  • JSON をパースして結果を渡す。
  • パース失敗時は failed() を記録。

🔹 エクスポート

Utility.ts
export default new Utility();
  • シングルトンとしてクラスインスタンスを外部に提供。

おまとめ

本記事では、Pod 上での curl 実行を簡素化・共通化する方法として、CommandBuilderUtility クラスを組み合わせた実装を紹介しました。

今後の拡張としては:

  • CommandBuilder に GET, PUT などのメソッド追加
  • PodLogger の標準化
  • CLIツールとしての切り出し

などが考えられます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?