# 通知設計書 47-AlreadyMountedVolume

## 概要

本ドキュメントは、Kubernetesにおけるボリュームが既に異なるfsGroupでマウント済みの際に発行されるイベント通知「AlreadyMountedVolume」の設計を記述する。ローカルボリュームが他のPodにより異なるfsGroupで既にマウントされている場合に、Warningイベントとして記録される。

### 本通知の処理概要

本通知は、ローカルボリューム（local volume plugin）のSetUp処理中に、対象ボリュームが他のPodから異なるfsGroupで既にマウントされていることを検出した際に発行されるKubernetesイベントである。ボリュームの共有利用時にfsGroupの不一致が生じた場合の警告として機能する。

**業務上の目的・背景**：ローカルボリュームが複数のPodで共有される場合、異なるfsGroupでマウントされるとファイルの所有権の不整合が発生し、一方のPodからファイルにアクセスできなくなるリスクがある。この通知により、管理者にfsGroup設定の見直しを促す。

**通知の送信タイミング**：ローカルボリュームのSetUpメソッド内で、ボリュームがまだマウントされていない場合にマウントを試みる際、既存のマウント参照を確認し、他のPodのマウント参照が存在し、かつfsGroupが異なる場合に発行される。

**通知の受信者**：Podオブジェクトに対するイベントとして記録されるため、Pod所有者、Namespace管理者がkubectl describe podやイベント監視ツールを通じて確認できる。

**通知内容の概要**：「The requested fsGroup is {new}, but the volume {name} has GID {old}. The volume may not be shareable.」というメッセージが含まれ、要求されたfsGroupと現在のGIDの不一致を示す。

**期待されるアクション**：管理者は関連するPodのSecurityContext.fsGroup設定を確認し、同一ボリュームを共有するPod間でfsGroupを統一するか、ボリューム共有の設計を見直すべきである。

## 通知種別

Kubernetesイベント（Event API リソース）- EventType: Warning

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（EventRecorder経由） |
| 優先度 | 中（Warning） |
| リトライ | EventRecorderの内部リトライ機構に依存 |

### 送信先決定ロジック

Podオブジェクトに対してイベントが記録される。m.plugin.recorder.Eventf(m.pod, ...)により発行される。ローカルボリュームプラグインのrecorderフィールドが使用される。

## 通知テンプレート

### メール通知の場合

該当なし（Kubernetesネイティブイベント）

### 本文テンプレート

```
"The requested fsGroup is %d, but the volume %s has GID %d. The volume may not be shareable."
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| fsGroupNew | 要求されたfsGroup | int64(*mounterArgs.FsGroup) | Yes |
| volName | ボリューム名 | m.volName | Yes |
| fsGroupOld | 既存のGID | hostUtil.GetOwner(m.globalPath) | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 内部処理 | ローカルボリュームSetUp | 他PodのマウントありかつfsGroup不一致 | local.go 568行目 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| fsGroupがnil | mounterArgs.FsGroupがnilの場合はチェックスキップ |
| 他Podマウントなし | filterPodMounts後のrefsが空の場合はチェックスキップ |
| fsGroupが一致 | fsGroupNew == fsGroupOldの場合はイベント発行なし |
| 既にマウント済み | !notMnt（IsNotMountPointがfalse）の場合は早期リターン |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ローカルボリュームSetUp] --> B{既にマウント済み?}
    B -->|Yes| C[return nil]
    B -->|No| D{FsGroupが設定?}
    D -->|No| E[マウント処理へ]
    D -->|Yes| F[GetMountRefsで参照取得]
    F --> G[filterPodMountsで他Podフィルタ]
    G --> H{他Podマウントあり?}
    H -->|No| E
    H -->|Yes| I[GetOwnerでGID取得]
    I --> J{fsGroupNew != fsGroupOld?}
    J -->|Yes| K[AlreadyMountedVolume イベント発行]
    J -->|No| E
    K --> E
    E --> L[バインドマウント実行]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| Pod | イベント発行対象 | m.pod |
| ファイルシステム | 既存GID取得 | hostUtil.GetOwner(m.globalPath) |

### テーブル別参照項目詳細

#### ファイルシステム

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| GID | 既存のグループID | GetOwner(globalPath) |
| マウント参照 | 他Podからのマウント確認 | GetMountRefs(globalPath) |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Event | INSERT | AlreadyMountedVolumeイベント作成 |

#### 送信ログテーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | Event.Reason | "AlreadyMountedVolume" | Warningイベント |
| INSERT | Event.Message | fsGroup不一致メッセージ | 要求GIDと既存GIDを含む |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| GetOwner失敗 | globalPathのオーナー情報取得失敗 | エラー返却（SetUp失敗） |
| GetMountRefs失敗 | マウント参照取得失敗 | エラー返却（SetUp失敗） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | operationExecutorによるリトライ |
| リトライ間隔 | 指数バックオフ |
| リトライ対象エラー | GetOwner/GetMountRefs失敗時 |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | EventRecorderのデフォルト設定に従う |
| 1日あたり上限 | 制限なし（同一イベントは集約される） |

### 配信時間帯

制限なし（24時間365日）

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

イベントメッセージにはfsGroup値（GID）とボリューム名が含まれる。セキュリティ上の機密情報は含まれない。

## 備考

- AlreadyMountedVolumeはローカルボリュームプラグイン固有のイベントである
- 定数名はWarnAlreadyMountedVolumeだが、イベント文字列は"AlreadyMountedVolume"
- マウント処理自体はこのWarningイベントの後も続行される（エラーではなく警告）
- filterPodMountsにより、同一Pod内の他マウントは除外される

---

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

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.go | `pkg/kubelet/events/event.go` | 71行目: WarnAlreadyMountedVolume = "AlreadyMountedVolume" 定数定義 |

**読解のコツ**: 定数名WarnAlreadyMountedVolumeとイベント文字列"AlreadyMountedVolume"が異なる。先頭のWarnはコード内での区別用。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | local.go | `pkg/volume/local/local.go` | 540-589行目: localVolumeMounterのSetUpメソッド |

**主要処理フロー**:
1. **542行目**: IsNotMountPointチェック
2. **549行目**: 既にマウント済みならreturn nil
3. **552行目**: GetMountRefsでマウント参照取得
4. **553行目**: FsGroupがnilでないかチェック
5. **560行目**: filterPodMountsで自Pod除外
6. **561行目**: 他Podマウント存在チェック
7. **562-563行目**: fsGroupNewとfsGroupOld取得
8. **567行目**: GID比較
9. **568行目**: 不一致時にAlreadyMountedVolumeイベント発行

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

```
localVolumeMounter.SetUp()
    |
    +-- mount.IsNotMountPoint()
    +-- mounter.GetMountRefs()
    +-- filterPodMounts()
    +-- hostUtil.GetOwner()
    +-- [GID不一致時] plugin.recorder.Eventf(pod, Warning, WarnAlreadyMountedVolume, ...)
    +-- os.MkdirAll()
    +-- mounter.Mount() [バインドマウント]
```

### データフロー図

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

Pod.SecurityContext       localVolumeMounter.SetUp()         Kubernetes Event API
  .fsGroup           --> GetMountRefs()                 --> Event(Warning,
Volume globalPath        GetOwner()                          AlreadyMountedVolume)
                         fsGroup比較
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/kubelet/events/event.go` | ソース | WarnAlreadyMountedVolume定数定義（71行目） |
| local.go | `pkg/volume/local/local.go` | ソース | ローカルボリュームプラグイン実装、SetUpメソッド（568行目） |
