mcpField
R
reconciler-logic

reconciler-logic

github.com/grafana開発2954.3k 回インストール1 日前に更新
リポジトリ

reconciler-logic について

grafana-app-sdk アプリの Reconciler 実装を支援するスキルです。型付き処理、イベントの絞り込み、ステータス更新、finalizer の動作を扱い、リソースのライフサイクルロジックを構築できます。

主な機能

  1. 01型付き Reconciler パターン
  2. 02イベントとアクションの処理
  3. 03ステータス更新の競合回避
  4. 04Namespace とラベルの絞り込み
  5. 05再キューと世代チェック

活用シーン

  1. 01リソース Reconciler の実装
  2. 02ステータス書き込み競合の回避
  3. 03未発火の reconcile イベントの調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/grafana/skills --skill reconciler-logic

インストール後、Claude Code で/reconciler-logicを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
informer-configuration.md
reconciler-patterns.md
registration.md
unmanaged-kinds.md
watchers.md

詳細

リコンサイラーのロジック

リコンサイラーは、grafana-app-sdk アプリの非同期ビジネスロジック層です。リソースが作成、更新、削除されると、SDK はリコンサイルイベントをキューに追加します。リコンサイラーは現在の状態を確認し、システムを望ましい状態へと導きます。

よくあるワークフロー

新しいリコンサイラーを最初から最後まで実装する
# 1. Generate operator stubs for a standalone app
grafana-app-sdk project component add operator

# 2. Implement the ReconcileFunc — see § TypedReconciler below for the pattern

# 3. Register the reconciler in app.go (see references/registration.md)

# 4. Generate, build, and verify it runs
grafana-app-sdk generate
go build ./...
go run ./cmd/operator   # tail logs — reconcile entries should appear when you kubectl-apply a resource

オペレーターは起動するものの、リソースを作成してもリコンサイルイベントが発生しない場合:

  • BasicReconcileOptions.Namespace がリソースの名前空間と一致していることを確認してください
  • BasicReconcileOptions.LabelFilters / FieldSelectors を確認してください。「イベントが発生しない」問題の多くはフィルターの不一致が原因です(ラベルを確認するには kubectl get <resource> -o yaml を実行してください)
  • リコンサイラーが、その kind の正しい(最新の)バージョンに関連付けられていることを確認してください

TypedReconciler — 推奨パターン

operator.TypedReconciler は型アサーションを処理し、型安全な ReconcileFunc を提供します。

type MyKindReconciler struct {
    operator.TypedReconciler[*v1alpha1.MyKind]
    client resource.Client
}

func NewMyKindReconciler(client resource.Client) *MyKindReconciler {
    r := &MyKindReconciler{client: client}
    r.ReconcileFunc = r.reconcile  // wire the typed func
    return r
}

func (r *MyKindReconciler) reconcile(
    ctx context.Context,
    req operator.TypedReconcileRequest[*v1alpha1.MyKind],
) (operator.ReconcileResult, error) {
    obj := req.Object

    // Skip if already reconciled this generation
    if obj.GetGeneration() == obj.Status.LastObservedGeneration &&
       req.Action != operator.ReconcileActionDeleted {
        return operator.ReconcileResult{}, nil
    }

    log := logging.FromContext(ctx).With("name", obj.GetName(), "namespace", obj.GetNamespace())
    log.Info("reconciling", "action", operator.ResourceActionFromReconcileAction(req.Action))

    if req.Action == operator.ReconcileActionDeleted {
        return operator.ReconcileResult{}, nil
    }

    // ... business logic ...

    // Atomic status update — see § Status updates below
    _, err := resource.UpdateObject(ctx, r.client, obj.GetStaticMetadata().Identifier(),
        func(obj *v1alpha1.MyKind, _ bool) (*v1alpha1.MyKind, error) {
            obj.Status.LastObservedGeneration = obj.GetGeneration()
            obj.Status.State = "Ready"
            return obj, nil
        },
        resource.UpdateOptions{Subresource: "status"},
    )
    return operator.ReconcileResult{}, err
}

ReconcileAction の値:ReconcileActionCreated、ReconcileActionUpdated、ReconcileActionDeleted、ReconcileActionResynced。

遅延後に再キューする場合(外部システムのポーリングなど):

return operator.ReconcileResult{RequeueAfter: 10 * time.Second}, nil

resource.UpdateObject によるステータスの更新

ステータスを書き込む際は、必ず resource.UpdateObject を使用してください。更新関数を適用する前に最新バージョンを取得するため、複数のリコンサイルイベントが競合したときの 409 Conflict エラーを回避できます。

_, err := resource.UpdateObject(ctx, r.client, identifier,
    func(obj *v1alpha1.MyKind, exists bool) (*v1alpha1.MyKind, error) {
        obj.Status.LastObservedGeneration = obj.GetGeneration()
        obj.Status.State = "Ready"
        obj.Status.Message = ""
        return obj, nil
    },
    resource.UpdateOptions{Subresource: "status"},
)

ステータスの更新に client.Update は使用しないでください。オブジェクト全体を送信するため、ユーザーが行った spec の変更と競合します。

世代に基づくスキップ

変更されていないリソースの再処理を避けるため、リコンサイル関数の先頭で LastObservedGeneration を確認します。

if obj.GetGeneration() == obj.Status.LastObservedGeneration {
    return operator.ReconcileResult{}, nil
}

ReconcileOptions

AppManagedKind エントリーの BasicReconcileOptions を使って、インフォーマーの動作を制御します。

{
    Kind:       mykindv1alpha1.MyKindKind(),
    Reconciler: reconciler,
    ReconcileOptions: simple.BasicReconcileOptions{
        Namespace:      "my-namespace",          // watch one namespace; default is all
        LabelFilters:   []string{"env=prod"},    // only reconcile matching resources
        FieldSelectors: []string{"status.phase=Running"},
        UsePlain:       false,                   // false = wrap in OpinionatedReconciler (default; manages finalizers)
    },
},

UsePlain: false(デフォルト)では、リコンサイラーが OpinionatedReconciler でラップされます。これによりファイナライザーが自動的に管理され、SDK が確実にクリーンな削除を行えます。

参考資料

  • references/watchers.md — Watcher の代替手段(イベント形式の Add/Update/Delete コールバック)と、Watcher とリコンサイラーの選択基準
  • references/unmanaged-kinds.md — アプリが所有していないリソースをリコンサイルするための UnmanagedKinds、UseOpinionated: false の推奨事項、およびよくある失敗例
  • references/registration.md — app.go の設定全般(クライアントのセットアップ、複数バージョンの登録、ValidateManifest)とよくある失敗例

外部リソース

コメント