はじめに
Kubernetes 上の Pod で curl
を実行して API 通信を行うためのコマンドを自動生成し、ログを記録する TypeScript 実装を紹介します。
Remoteの実装については下記の記事をご参照ください。
解決する課題
- 複雑な curl コマンドを毎回書くのが大変
- Pod に対しての exec 実行をスクリプト化したい
- 実行結果をログとして残したい
使用技術
- TypeScript
- ssh2(Remote exec)
- Kubernetes (
kubectl exec
)
コマンド生成クラス CommandBuilder
目的
Kubernetes 上の Pod に対して kubectl exec
経由で curl
コマンドを組み立てるためのビルダークラス CommandBuilder
を実装したものです。各メソッドが curl
コマンドの一部を動的に構築します。
以下、分割して解説します。
🔹 クラス定義と初期化部分
class CommandBuilder {
private config: string[] = [];
private command: string[] = [];
-
config
:kubectl exec
などのベース部分を格納。 -
command
:curl
コマンドのパラメータ部分を構築。
🔹 reset()
メソッド
public reset(): void {
this.command = [];
}
-
command
をリセット。config
はそのまま保持する。
🔹 pod(podName: string)
メソッド
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()
メソッド
public patch(): CommandBuilder {
this.command.push(`-X PATCH`);
return this;
}
-
PATCH
リクエストを指定するオプション。
🔹 token(token: string)
メソッド
public token(token: string): CommandBuilder {
this.command.push(`-H "Authorization: Bearer ${token}"`);
return this;
}
- Bearer トークンによる認証ヘッダーを追加。
🔹 header(header: string)
メソッド
public header(header: string): CommandBuilder {
this.command.push(`-H "${header}"`);
return this;
}
- 任意のヘッダーを追加。
🔹 content(content: string = "json")
メソッド
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)
メソッド
public payload(payload: object): CommandBuilder {
this.command.push(`-d '${JSON.stringify(payload)}'`);
return this;
}
- リクエストボディを JSON 文字列として追加。
🔹 api(api: string)
メソッド
public api(api: string): CommandBuilder {
this.command.push(`${api}`);
return this;
}
- 実行する API のエンドポイント URL を追加。
🔹 formMeta(meta: string)
メソッド
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
を定義しています。以下、機能ごとに分断して詳しく解説します。
実行フロー一覧
- Pod 名取得:
checkPodname()
- Token 取得:
Postman.getToken()
- curl 組み立て:
CommandBuilder.xxx()
- Pod 上で実行:
Remote.execRemote()
- 結果解析とログ記録:
handleApiResponse()
&PodLogger
このコードは、Kubernetes 上の Pod で curl
コマンドを実行し、API 呼び出しとログ記録を行うユーティリティクラス Utility
を定義しています。以下、機能ごとに分断して詳しく解説します。
🔹 インポート & 初期設定
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()
デコレータの定義
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
クラスの本体
class Utility {
public podName?: string;
- Kubernetes の Pod 名を保持。
🔹 Pod 名取得メソッド
private async checkPodname() {
if (!this.podName) {
const podName = (await Pod.getPodName())[0] || "";
this.podName = podName;
CommandBuilder.pod(podName);
}
}
- Pod 名が未取得の場合に取得し、CommandBuilder に設定。
🔹 updatePaymentStatus
メソッド
@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
メソッド
@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
メソッド
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()
を記録。
🔹 エクスポート
export default new Utility();
- シングルトンとしてクラスインスタンスを外部に提供。
おまとめ
本記事では、Pod 上での curl 実行を簡素化・共通化する方法として、CommandBuilder
と Utility
クラスを組み合わせた実装を紹介しました。
今後の拡張としては:
- CommandBuilder に GET, PUT などのメソッド追加
- PodLogger の標準化
- CLIツールとしての切り出し
などが考えられます。