reconciler-logic について
grafana-app-sdk アプリの Reconciler 実装を支援するスキルです。型付き処理、イベントの絞り込み、ステータス更新、finalizer の動作を扱い、リソースのライフサイクルロジックを構築できます。
主な機能
- 01型付き Reconciler パターン
- 02イベントとアクションの処理
- 03ステータス更新の競合回避
- 04Namespace とラベルの絞り込み
- 05再キューと世代チェック
活用シーン
- 01リソース Reconciler の実装
- 02ステータス書き込み競合の回避
- 03未発火の reconcile イベントの調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ grafana/ skills --skill reconciler-logicインストール後、Claude Code で/reconciler-logicを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/informer-configuration.mdreconciler-patterns.mdregistration.mdunmanaged-kinds.mdwatchers.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)とよくある失敗例