0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetes の controller-runtime で Namespace == "" が発生するワケ ―― “空ネームスペース事件” を防ぐための整理&ベストプラクティス ――

Posted at

Kubernetes Controllerにおける

対象読者

  • Kubernetes Controller を Go (controller-runtime) で実装している方
  • tombstone(DeletedFinalStateUnknown)にハマったことがある/テストで妙な NULL を踏んだことがある方

TL;DR

起因 どんな時に起こる? 見分け方 取るべき対策
DeletedFinalStateUnknown オブジェクトがキャッシュに載る前に削除された Event の型が cache.DeletedFinalStateUnknown tombstone 専用ハンドラで Key 解析 or 無視
フェイククライアント テスト用 fake.NewClientBuilder() に渡したオブジェクトに metadata.namespace を書き忘れた テストだけで再現する/APIServerを経由していない obj.SetNamespace(...) or client.Create() で検証を通す
スコープ不一致 Cluster-scoped 型を誤って Watch している CRD の scope: Namespaced/Cluster を確認 スコープ専用 RBAC & unit test で早期検知
YAML の namespace 抜け POST /apis/... を直叩き or GitOps で namespace 省略 kubectl 経由では再現しない CI でスキーマ検証 or admission webhook

1. DeletedFinalStateUnknown ―― "無縁仏" Tombstone イベント

コントローラが Informer から受け取る削除イベントには2種類あります。

種類 キャッシュに実体がある? 典型的な流れ
DeleteEvent ある Add → Update → Delete
DeletedFinalStateUnknown ない Add が届く前に物理削除 → キャッシュに無いまま Delete だけ届く

DeletedFinalStateUnknown.Obj には「ゼロ値」のオブジェクトが挿入されるため、

if tomb, ok := ev.Object.(cache.DeletedFinalStateUnknown); ok {
    key := tomb.Key            // => "default/foo"
    // → key から namespace/name をパースする
}

のように Key 文字列 から情報を復元するか、"存在しない前提"で早期 return するのが安全です。


2. フェイククライアントで namespace を入れ忘れた

fake.NewClientBuilder() に渡すオブジェクトは APIServer バリデーションが走りません。
テストデータを手書きする際に metadata.namespace を忘れると、そのまま "" が保存されます。

obj := &ipamv1.Subnet{
  ObjectMeta: metav1.ObjectMeta{
    Name: "foo",
    // Namespace: "default", ← 忘れると空に!
  },
}
client := fake.NewClientBuilder().
  WithScheme(scheme).
  WithObjects(obj).           // → ここで検証されない
  Build()

ベストプラクティス

  1. 必ず obj.SetNamespace("default") などで埋める
  2. もしくは client.Create(ctx, obj) 経由で登録し、fakeClient 内部のスキーマ検証を利用する

3. スコープ不一致(Cluster-scoped 型を誤って扱う)

  • CRD を Namespaced → Cluster に変更した
  • Watch 対象の型と RBAC が食い違った

……といった場合、Informer から届くオブジェクトは当然 Namespace == "" です。
CI で スキーマ&RBAC テスト を走らせておくと早期に検知できます。

# controller-gen の lint
controller-gen paths=./... crd:crdVersions=v1 output:stdout \
  | kubeconform -strict -

4. YAML の metadata.namespace が抜けたまま直 API コール

kubectl -n default apply -f ... なら補完されますが、
GitOps で Raw Manifest を API に流し込む場合は 空のまま登録 されることがあります。

  • Admission Webhook (CEL / kyverno) で必須フィールドチェック
  • CI で kubeconform + OpenAPI 検証を推奨

5. 実装例:安全なイベントハンドリングの雛形

func (r *Reconciler) handleEvent(obj client.Object) {
    // tombstone を区別
    if tomb, ok := obj.(cache.DeletedFinalStateUnknown); ok {
        // (a) 無視する
        return

        // --- または (b) Key をパースする ---
        // ns, name := cache.SplitMetaNamespaceKey(tomb.Key)
        // ...
    }

    // Namespace を必ずチェック
    if obj.GetNamespace() == "" {
        // cluster-scoped? それとも異常?
        log.Info("empty namespace; skip", "name", obj.GetName())
        return
    }

    // 以降は安心して Reconcile
}

まとめ

  • "空 namespace" はバグではなく "例外イベント or テスト環境" が主犯
  • tombstone かどうかを判定し、Key 解析 or 無視で安全に捌く
  • フェイククライアント・GitOps マニフェストの namespace 欠落は自分で防御
  • スコープ不一致は CI と RBAC テストで早期発見

この4点を押さえておけば、controller-runtime の "空ネームスペース事件" でハマる確率はほぼゼロになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?