# 通知設計書 96-UnexpectedJob

## 概要

本ドキュメントは、CronJobコントローラが作成していない、または追跡を忘れたJobを検出した際に発行されるKubernetesイベント通知「UnexpectedJob」の設計を記述する。

### 本通知の処理概要

CronJobが親（OwnerReference）として設定されている未完了のJobが、CronJobのActiveListに登録されていない場合にWarningイベントとして発行される通知である。

**業務上の目的・背景**：CronJobコントローラはJobの作成後にActiveListを更新するが、コントローラのクラッシュや再起動により、Job作成後のステータス更新が失敗するケースがある。また、外部のアクターが意図的にCronJobをOwnerReferenceとするJobを作成した場合にも発生する。本通知はこのような予期しないJobの存在を検知し、運用者にActiveListの不整合を通知する。

**通知の送信タイミング**：`syncCronJob`メソッド内で管理対象Job一覧を走査し、ActiveListに存在しない未完了Jobを検出した場合に発行される（行447）。ただし、API Serverからの最新CronJobステータスでActiveListに含まれている場合はスキップされる。

**通知の受信者**：Kubernetesイベントとして該当CronJobオブジェクトに記録される。

**通知内容の概要**：EventTypeはWarning、Reasonは「UnexpectedJob」、メッセージには検出されたJob名が含まれる。フォーマットは「Saw a job that the controller did not create or forgot: {Job名}」である。

**期待されるアクション**：受信者はUnexpectedJobが発生した原因を調査する。コントローラ再起動直後の一時的な不整合であれば問題ないが、外部からの意図しないJob作成の場合はセキュリティ上の対処が必要な場合がある。

## 通知種別

Kubernetesイベント（Event API リソース）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（syncCronJob処理内で即座に発行） |
| 優先度 | 中（ActiveList不整合の検知） |
| リトライ | N/A（検出時に一度だけ発行） |

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

CronJobコントローラの`recorder.Eventf`メソッドにより、対象のCronJobオブジェクトに対してイベントが記録される。

## 通知テンプレート

### メール通知の場合

本通知はKubernetesイベントであり、メール送信は行わない。

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A（Kubernetesイベント） |
| 送信元名称 | cronjob-controller |
| 件名 | N/A |
| 形式 | Kubernetesイベントリソース |

### 本文テンプレート

```
Saw a job that the controller did not create or forgot: %s
```

変数`%s`には検出されたJob名（`j.Name`）が挿入される。

### 添付ファイル

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

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| j.Name | 予期しないJob名 | batchv1.Job.Name | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| コントローラ同期 | CronJob同期処理 | OwnerReferenceがCronJobを指す未完了JobがActiveListに存在しない場合 | syncCronJob内のJob状態チェック |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| ActiveListに存在する | inActiveList(cronJob, j.ObjectMeta.UID)がtrueの場合 |
| Jobが完了済み | jobutil.IsJobFinished(j)がtrueの場合はSawCompletedJob処理に入る |
| API Serverの最新CronJobでActiveListに存在 | cronJobControl.GetCronJobで再取得したCronJobのActiveListに含まれる場合（行443-446） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[syncCronJob Job一覧走査] --> B{inActiveList?}
    B -->|ActiveListに存在| C[通常処理]
    B -->|ActiveListに不在| D{IsJobFinished?}
    D -->|完了済み| E[SawCompletedJob処理]
    D -->|未完了| F[API Serverから最新CronJob取得]
    F --> G{最新ActiveListに存在?}
    G -->|存在する| H[cronJobを最新に更新して続行]
    G -->|存在しない| I[UnexpectedJobイベント発行]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| CronJob | ActiveList参照 | Informerキャッシュ + API Server直接取得 |
| Job | OwnerReference確認、完了状態確認 | Informerキャッシュ経由 |

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

#### Job

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| ObjectMeta.UID | ActiveList照合 | getJobsToBeReconciled結果 |
| ObjectMeta.Name | イベントメッセージ用 | 同上 |
| OwnerReferences | CronJobとの親子関係確認 | getJobsToBeReconciled内 |

### 更新テーブル一覧

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

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

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | Event.Type | Warning | イベント種別 |
| INSERT | Event.Reason | UnexpectedJob | イベント理由 |
| INSERT | Event.Message | Saw a job that the controller did not create or forgot: {j.Name} | 予期しないJob名 |
| INSERT | Event.Source.Component | cronjob-controller | 発行元コンポーネント |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| CronJob取得エラー | cronJobControl.GetCronJobが失敗 | error返却、ワークキューで再試行 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | N/A |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | Kubernetesイベントのデフォルト集約ルールに従う |
| 1日あたり上限 | 特に制限なし |

### 配信時間帯

制限なし。

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

予期しないJobの存在は、外部からの不正なJob作成の可能性を示唆する場合がある。セキュリティ監査の観点からこのイベントを監視することが推奨される。

## 備考

- ソースコードのコメント（行448-451）によると、以下の3つのケースが考えられる：
  1. Job作成直後にコントローラがクラッシュし、ステータス更新が失敗した
  2. relistによりJob一覧がCronJobステータスより新しい状態にある
  3. 外部のアクターが意図的にCronJobに養子縁組するJobを作成した
- API Serverからの最新CronJob取得（行439-446）により、キャッシュ遅延による誤検知を防いでいる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | utils.go | `pkg/controller/cronjob/utils.go` | inActiveList関数（行59-66）のUID照合ロジックを理解する |

**読解のコツ**: ActiveListはObjectReference配列であり、UIDでJobの一意性を判定する。

#### Step 2: 検出ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cronjob_controllerv2.go | `pkg/controller/cronjob/cronjob_controllerv2.go` | syncCronJob内のJob状態チェック（行434-472）を理解する。特に行437-451がUnexpectedJob検出の核心部分 |

**主要処理フロー**:
1. **行437**: inActiveListでActiveList存在チェック
2. **行438**: !foundかつ!IsJobFinishedの場合がUnexpectedJob候補
3. **行439-441**: API Serverから最新CronJobを取得
4. **行443-446**: 最新CronJobのActiveListにも存在しない場合
5. **行447**: recorder.Eventf "UnexpectedJob" 発行

#### Step 3: Job取得ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | cronjob_controllerv2.go | `pkg/controller/cronjob/cronjob_controllerv2.go` | getJobsToBeReconciled関数（行258-276）でControllerRefがCronJobを指すJobのみが対象であることを理解する |

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

```
ControllerV2.syncCronJob (行426)
    |
    +-- [Job一覧走査] (行435-472)
         |
         +-- inActiveList (行437)
         +-- jobutil.IsJobFinished (行438)
         |
         +-- [ActiveListに不在 & 未完了] (行438)
              |
              +-- cronJobControl.GetCronJob (行439)
              +-- inActiveList(cjCopy, ...) (行443)
              |
              +-- [最新ActiveListにも不在]
                   +-- recorder.Eventf "UnexpectedJob" (行447) ★本通知
```

### データフロー図

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

Job (OwnerRef=CronJob)     --> syncCronJob                  --> UnexpectedJob Event
CronJob.Status.Active           |                                (Warning)
  (ActiveList)                  +-- inActiveList
CronJob (API Server最新)        +-- cronJobControl.GetCronJob
                                +-- recorder.Eventf
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cronjob_controllerv2.go | `pkg/controller/cronjob/cronjob_controllerv2.go` | ソース | UnexpectedJobイベント発行（行447）、Job状態チェック（行434-472） |
| utils.go | `pkg/controller/cronjob/utils.go` | ソース | inActiveList関数（行59-66） |
| injection.go | `pkg/controller/cronjob/injection.go` | ソース | cronJobControl.GetCronJob（行44-46） |
