# 機能設計書 126-ConfigMapボリューム

## 概要

本ドキュメントは、ConfigMapボリュームプラグインの設計を記述する。ConfigMapボリュームは、Kubernetes ConfigMapリソースのデータをファイルとしてPod内にマウントする機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：アプリケーションの設定情報をConfigMapとして管理し、ファイルシステム経由でコンテナに提供する。設定ファイル、スクリプト、環境情報等をコンテナイメージとは分離して管理可能とする。

**機能の利用シーン**：Pod SpecでConfigMapボリュームを定義し、コンテナのvolumeMountsでマウントする。kubeletがConfigMap APIからデータを取得し、ファイルとしてPod内に配置する。ConfigMapの更新は定期的に同期される。

**主要な処理内容**：
1. API ServerからConfigMapデータの取得
2. ペイロード（ファイル投影）の生成（MakePayload）
3. EmptyDir（tmpfs）を内部ラッパーとしたディレクトリセットアップ
4. AtomicWriterによるアトミックなファイル書き込み
5. FSGroup権限の適用
6. 定期的な再マウントによるConfigMap更新の反映

**関連システム・外部連携**：Kubernetes API Server（ConfigMap取得）、EmptyDirプラグイン（内部ラッパー）、AtomicWriter（ファイル書き込み）。

**権限による制御**：ConfigMapのRBACによるアクセス制御。ファイルモードはdefaultModeまたはitems[].modeで個別指定可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | ConfigMapボリュームはCLI画面を持たないkubelet内部処理 |

## 機能種別

ボリュームプラグイン / 設定データ投影

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | ConfigMap名 | ConfigMap存在チェック（optional=false時） |
| items | []KeyToPath | No | キーとパスのマッピング | 指定時はキー存在チェック |
| defaultMode | *int32 | No | デフォルトファイルパーミッション | nilでないこと |
| optional | *bool | No | ConfigMap不在時にエラーとしない | - |

### 入力データソース

- Pod Spec（volumes[].configMap）
- ConfigMap API（namespace/name）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| データファイル | ファイル群 | ConfigMapのdata/binaryDataの各キーをファイル名、値をファイル内容として配置 |
| シンボリックリンク | リンク | AtomicWriterによるアトミック更新用のシンボリックリンク構造 |

### 出力先

- `<kubeletDir>/pods/<podUID>/volumes/kubernetes.io~configmap/<volumeName>/`

## 処理フロー

### 処理シーケンス（SetUpAt）

```
1. EmptyDirラッパーのセットアップ（wrappedVolumeSpec → NewWrapperMounter → SetUpAt）
2. ConfigMap取得（getConfigMap API呼び出し）
   └─ optional=true時: NotFoundでも空ConfigMapで継続
3. MakePayload: ConfigMap data/binaryData → FileProjection変換
   ├─ items未指定: 全キーをファイル名としてマッピング
   └─ items指定: 指定キーのみマッピング（パス名はitems[].path）
4. wrappedのSetUpAtでディレクトリ準備
5. MakeNestedMountpoints: ネストされたマウントポイント作成
6. AtomicWriter.Write: アトミックなファイル書き込み
7. FSGroup権限適用
```

### フローチャート

```mermaid
flowchart TD
    A[SetUpAt開始] --> B[EmptyDirラッパーSetUp]
    B --> C[ConfigMap取得]
    C --> D{取得成功?}
    D -->|No & optional=false| E[エラー返却]
    D -->|No & optional=true| F[空ConfigMap使用]
    D -->|Yes| G[MakePayload]
    F --> G
    G --> H[wrappedSetUpAt]
    H --> I[MakeNestedMountpoints]
    I --> J[AtomicWriter.Write]
    J --> K{書き込み成功?}
    K -->|No| L[TearDown → エラー返却]
    K -->|Yes| M[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-126-1 | 読み取り専用 | ConfigMapボリュームは常にReadOnly=true | 常時 |
| BR-126-2 | 再マウント必須 | RequiresRemount=trueで定期更新をサポート | 常時 |
| BR-126-3 | optional対応 | optional=trueの場合、ConfigMap不在でも空ファイルセットで継続 | optional指定時 |
| BR-126-4 | items未指定時 | ConfigMapの全data/binaryDataキーをファイルとして展開 | items未指定 |
| BR-126-5 | items指定時 | 指定されたキーのみをitems[].pathで展開（キー不在はerror/skip） | items指定 |
| BR-126-6 | モード指定 | items[].modeが指定されればそれを使用、なければdefaultMode | 常時 |
| BR-126-7 | アトミック更新 | AtomicWriterによりシンボリックリンクの切り替えでアトミックに更新 | 常時 |
| BR-126-8 | セットアップ失敗時クリーンアップ | SetUp失敗時にdeferでTearDownを実行 | 失敗時 |

### 計算ロジック

- ファイルサイズ: `totalBytes(configMap)` = 全data値のlen合計 + 全binaryData値のlen合計（307-317行目）

## データベース操作仕様

| 操作 | 対象リソース | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ConfigMap取得 | ConfigMap | SELECT | namespace/nameでConfigMap取得（キャッシュ経由） |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ConfigMap不在 | optional=falseでConfigMapが存在しない | ConfigMapの作成 |
| - | キー不在 | items指定でキーが存在しない（optional=false） | ConfigMapにキーを追加 |
| - | defaultMode未指定 | defaultModeがnil | Pod Specの修正 |
| - | AtomicWriter失敗 | ファイル書き込みエラー | kubeletがリトライ |

### リトライ仕様

RequiresRemount=trueにより、kubeletのsync loopで定期的に再マウントが実行される。ConfigMap更新はこのメカニズムで反映される。

## トランザクション仕様

AtomicWriterがシンボリックリンクの切り替えによるアトミック更新を保証する。書き込み中に失敗した場合、前の状態が保持される。

## パフォーマンス要件

ConfigMap取得はkubeletのキャッシュを経由するため、API Serverへの直接リクエストは初回のみ。sync loop間隔でのデータ更新チェックが行われる。

## セキュリティ考慮事項

- ConfigMapのRBACによるアクセス制御が適用される
- ファイルパーミッションはdefaultMode/items[].modeで制御
- ConfigMapには機密情報を格納すべきではない（Secretを使用すべき）

## 備考

- ConfigMapの更新は、kubeletのsync intervalに依存して反映される（即時反映ではない）
- MakePayload関数はProjectedボリュームからも呼び出される（エクスポート済み）

---

## コードリーディングガイド

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | configmap.go | `pkg/volume/configmap/configmap.go` | configMapPlugin構造体（44-47行目）: host、getConfigMap |
| 1-2 | configmap.go | `pkg/volume/configmap/configmap.go` | configMapVolume構造体（132-138行目）: volName、podUID、plugin |
| 1-3 | configmap.go | `pkg/volume/configmap/configmap.go` | configMapVolumeMounter構造体（148-154行目）: source、pod、getConfigMap |

#### Step 2: セットアップ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | configmap.go | `pkg/volume/configmap/configmap.go` | wrappedVolumeSpec()（166-174行目）: EmptyDirをラッパーとして使用 |
| 2-2 | configmap.go | `pkg/volume/configmap/configmap.go` | SetUpAt()（180-260行目）: メインセットアップ処理 |
| 2-3 | configmap.go | `pkg/volume/configmap/configmap.go` | **189-202行目**: ConfigMap取得とoptional処理 |
| 2-4 | configmap.go | `pkg/volume/configmap/configmap.go` | **211行目**: MakePayload呼び出し |
| 2-5 | configmap.go | `pkg/volume/configmap/configmap.go` | **239-244行目**: AtomicWriter作成 |
| 2-6 | configmap.go | `pkg/volume/configmap/configmap.go` | **246-251行目**: setPerms関数（FSGroup権限適用） |
| 2-7 | configmap.go | `pkg/volume/configmap/configmap.go` | **252行目**: writer.Write(payload, setPerms) |

#### Step 3: ペイロード生成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | configmap.go | `pkg/volume/configmap/configmap.go` | MakePayload()（263-305行目）: ファイル投影の生成 |
| 3-2 | configmap.go | `pkg/volume/configmap/configmap.go` | **271-281行目**: items未指定時のdata/binaryData全展開 |
| 3-3 | configmap.go | `pkg/volume/configmap/configmap.go` | **282-301行目**: items指定時のキーマッピング |

#### Step 4: ティアダウンを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | configmap.go | `pkg/volume/configmap/configmap.go` | TearDownAt()（330-332行目）: UnmountViaEmptyDir呼び出し |

### プログラム呼び出し階層図

```
kubelet volume manager
    |
    ├─ configMapPlugin.NewMounter(spec, pod)
    |      └─ configMapVolumeMounter{} 構築
    |
    ├─ configMapVolumeMounter.SetUp()
    |      └─ SetUpAt(dir)
    |             ├─ host.NewWrapperMounter()  [EmptyDirラッパー]
    |             ├─ getConfigMap(namespace, name)  [API呼び出し]
    |             ├─ MakePayload(items, configMap, defaultMode, optional)
    |             ├─ wrapped.SetUpAt()  [EmptyDir SetUp]
    |             ├─ volumeutil.MakeNestedMountpoints()
    |             ├─ volumeutil.NewAtomicWriter()
    |             └─ writer.Write(payload, setPerms)
    |                    └─ volume.NewVolumeOwnership().ChangePermissions()
    |
    └─ configMapVolumeUnmounter.TearDown()
           └─ TearDownAt()
                  └─ volumeutil.UnmountViaEmptyDir()
```

### データフロー図

```
[入力]                            [処理]                         [出力]

ConfigMapVolumeSource ────▶ NewMounter() ──────────▶ configMapVolumeMounter
  name ────────────────────▶    │
  items ───────────────────▶    │
  defaultMode ─────────────▶    │
                                 │
ConfigMap API ─────────────▶ SetUpAt()
  data ────────────────────▶     ├──▶ ファイル群（data keys → files）
  binaryData ──────────────▶     ├──▶ シンボリックリンク構造
                                  └──▶ パーミッション設定
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| configmap.go | `pkg/volume/configmap/configmap.go` | ソース | メイン実装 |
| util/atomic_writer.go | `pkg/volume/util/atomic_writer.go` | ソース | アトミックファイル書き込み |
| util/nested_volumes.go | `pkg/volume/util/nested_volumes.go` | ソース | ネストされたマウントポイント作成 |
| emptydir/empty_dir.go | `pkg/volume/emptydir/empty_dir.go` | ソース | 内部ラッパー |
