# 通知設計書 23-ErrImageNeverPull

## 概要

本ドキュメントは、imagePullPolicyがNeverに設定されたコンテナにおいて、ローカルにイメージが存在しない場合に発行されるイベント通知「ErrImageNeverPull」の設計を定義する。

### 本通知の処理概要

Pod specでimagePullPolicyがNeverに設定されているにもかかわらず、ノードのローカルストレージに対象コンテナイメージが存在しない場合に、kubeletのimageManagerがWarningイベントとしてKubernetes Event APIに記録する通知である。

**業務上の目的・背景**：imagePullPolicy: Neverはエアギャップ環境やプリロード済みイメージの使用を前提とするデプロイ方式で利用される。本通知は、期待されるイメージがノードにプリロードされていないことを検知し、デプロイ前のイメージ準備漏れや、ノードの状態異常を運用者に伝達するために必要である。また、KubeletEnsureSecretPulledImagesフィーチャーゲートが有効な場合、イメージが存在していても提示された認証情報でアクセスできない場合にも同じエラーが発行される（イメージの存在を秘匿するため）。

**通知の送信タイミング**：kubeletがimagePullPrecheckにおいてPullNeverポリシーでGetImageRefを実行し、イメージが見つからなかった時点、またはKubeletEnsureSecretPulledImages機能有効時にイメージは存在するが認証情報でアクセスできないと判定された時点で発行される。

**通知の受信者**：Kubernetes Event APIを通じて、対象PodのObjectReferenceに紐づくイベントとして記録される。kubectl describe pod等で確認可能。

**通知内容の概要**：対象イメージ名とpull policy情報を含むメッセージが通知される。メッセージ形式は「Container image "イメージ名" is not present with pull policy of Never」となる。

**期待されるアクション**：受信者は(1)ノードにイメージがプリロードされているか確認、(2)imagePullPolicyの設定が意図通りか確認、(3)イメージ名・タグが正確か確認、(4)KubeletEnsureSecretPulledImages有効時はimagePullSecretの確認、などの対応を行う。

## 通知種別

Kubernetes Event（APIリソース）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（イメージプル前チェック処理内で即時実行） |
| 優先度 | 高（EventType: Warning） |
| リトライ | Pod起動リトライの一部として間接的にリトライ |

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

対象PodのObjectReference（`*v1.ObjectReference`）に対してイベントが記録される。ObjectReferenceがnilの場合はklogへのWarningログ出力のみとなる。

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A（Kubernetes Event） |
| 送信元名称 | kubelet |
| 件名 | N/A |
| 形式 | テキスト |

### 本文テンプレート

```
Container image "{イメージ名}" is not present with pull policy of Never
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| N/A | - | - | Kubernetes Eventのため添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| requestedImage | 要求されたコンテナイメージ名 | EnsureImageExists引数 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 内部処理 | imagePullPrecheck内のイメージ不在判定 | pullPolicyがNeverかつイメージが存在しない | imagePullPrecheck 130-132行目 |
| 内部処理 | imagePullManager.MustAttemptImagePullでアクセス不可判定 | KubeletEnsureSecretPulledImages有効かつpullPolicyがNeverかつ認証情報でアクセス不可 | EnsureImageExists 269-273行目 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| pullPolicyがNever以外 | PullAlwaysまたはPullIfNotPresentの場合は本イベントは発行されない |
| イメージがローカルに存在する | PullNeverでもイメージが存在する場合は発行されない（KubeletEnsureSecretPulledImages無効時） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[EnsureImageExists呼び出し] --> B[imagePullPrecheck呼び出し]
    B --> C{pullPolicy?}
    C -->|PullNever| D[GetImageRef呼び出し]
    C -->|PullAlways/PullIfNotPresent| E[他の処理へ]
    D --> F{イメージ存在?}
    F -->|存在しない| G[imageNotPresentOnNeverPolicyError]
    G --> H[ErrImageNeverPullイベント発行]
    H --> I[ErrImageNeverPull返却]
    F -->|存在する| J{KubeletEnsureSecretPulledImages?}
    J -->|無効| K[正常終了]
    J -->|有効| L[MustAttemptImagePull]
    L -->|アクセス不可+PullNever| M[imageNotPresentOnNeverPolicyError]
    M --> H
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| N/A | - | CRIランタイムのイメージストアを参照 |

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

本通知はデータベースを直接参照しない。CRI API経由でローカルイメージストアを参照する。

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Event APIリソース（etcd） | INSERT | Warningイベントの記録 |

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

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | Type | Warning | EventType |
| INSERT | Reason | ErrImageNeverPull | イベント理由 |
| INSERT | Message | "Container image ... is not present with pull policy of Never" | メッセージ |
| INSERT | InvolvedObject | Pod ObjectReference | 対象Pod |
| INSERT | Source.Component | kubelet | 発行元コンポーネント |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ErrImageNeverPull | PullNeverポリシーでイメージが見つからない | ノードへのイメージプリロード、またはpullPolicyの変更 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Pod起動リトライの一部として間接的にリトライ |
| リトライ間隔 | kubeletのPod同期間隔に依存 |
| リトライ対象エラー | ErrImageNeverPull（イメージがプリロードされるまで継続的に失敗） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | EventRecorderのバースト設定に依存 |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（24時間365日）

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

- KubeletEnsureSecretPulledImagesフィーチャーゲート有効時、イメージが存在していても認証情報でアクセスできない場合は「存在しない」と同じメッセージが返される。これはイメージの存在情報の漏洩を防ぐためのセキュリティ設計である（image_manager.go 148-155行目のコメント参照）
- RBACによるEventリソースへのアクセス制御が適用される

## 備考

- 本イベントはimageNotPresentOnNeverPolicyError関数（image_manager.go 156-160行目）から発行される
- 2つの呼び出し元が存在：(1)imagePullPrecheck内、(2)EnsureImageExists内（KubeletEnsureSecretPulledImages有効時）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.go | `pkg/kubelet/images/types.go` | ErrImageNeverPull = errors.New("ErrImageNeverPull") 定義（38行目） |
| 1-2 | event.go | `pkg/kubelet/events/event.go` | ErrImageNeverPullPolicy = "ErrImageNeverPull" 定数定義（51行目） |

**読解のコツ**: エラー変数名とイベント理由名が若干異なるが、最終的なイベントReasonはErrImageNeverPullとなる。

#### Step 2: イベント発行関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | image_manager.go | `pkg/kubelet/images/image_manager.go` | imageNotPresentOnNeverPolicyError関数（156-160行目） |

**主要処理フロー**:
1. **157行目**: メッセージ生成 "Container image %q is not present with pull policy of Never"
2. **158行目**: logItによるWarningイベント発行（events.ErrImageNeverPullPolicy使用）
3. **159行目**: ErrImageNeverPullを返却

#### Step 3: 呼び出し元を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | image_manager.go | `pkg/kubelet/images/image_manager.go` | imagePullPrecheck関数（130-132行目）での呼び出し |
| 3-2 | image_manager.go | `pkg/kubelet/images/image_manager.go` | EnsureImageExists関数（269-273行目）でのKubeletEnsureSecretPulledImages時の呼び出し |

**主要処理フロー**:
- **130行目**: !*imageFound && pullPolicy == v1.PullNever の条件チェック
- **269行目**: pullPolicy == v1.PullNever の条件チェック（imagePullManager判定後）

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

```
kuberuntime_manager.go: startContainer()
    |
    +-- imagePuller.EnsureImageExists()              [image_manager.go:164]
            |
            +-- imagePullPrecheck()                   [image_manager.go:109]
            |       |
            |       +-- imageService.GetImageRef()
            |       +-- imageNotPresentOnNeverPolicyError()  [image_manager.go:156]
            |               +-- logIt() --> ErrImageNeverPullイベント発行
            |
            +-- imagePullManager.MustAttemptImagePull()  (KubeletEnsureSecretPulledImages有効時)
            +-- imageNotPresentOnNeverPolicyError()      [image_manager.go:156] (アクセス不可時)
```

### データフロー図

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

Pod spec               --> EnsureImageExists()             --> ErrImageNeverPull
  requestedImage            |
  pullPolicy: Never         +-- imagePullPrecheck()
                            |       +-- GetImageRef() --> not found
                            |       +-- imageNotPresentOnNeverPolicyError()
                            |
                            +-- logIt()                    --> Kubernetes Event API
                                recorder.Event()               (Warning/ErrImageNeverPull)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/kubelet/events/event.go` | ソース | ErrImageNeverPullPolicy定数定義 |
| types.go | `pkg/kubelet/images/types.go` | ソース | ErrImageNeverPullエラー定義 |
| image_manager.go | `pkg/kubelet/images/image_manager.go` | ソース | imageNotPresentOnNeverPolicyError関数、イベント発行ロジック |
| image_manager_test.go | `pkg/kubelet/images/image_manager_test.go` | テスト | ErrImageNeverPullシナリオのテスト |
