Agent Skillsgrafana/skills › reconciler-logic

reconciler-logic

GitHub

实现 grafana-app-sdk 的 Reconciler 和 Watcher,处理资源生命周期事件、状态更新及控制器注册。适用于编写异步业务逻辑、事件驱动处理和控制器开发任务。

skills/grafana-app-sdk/reconciler-logic/SKILL.md grafana/skills

Trigger Scenarios

编写 reconciler 或 controller 实现 reconcile 循环 处理资源创建/更新/删除事件 添加异步业务逻辑

Install

npx skills add grafana/skills --skill reconciler-logic -g -y
More Options

Non-standard path

npx skills add https://github.com/grafana/skills/tree/main/skills/grafana-app-sdk/reconciler-logic -g -y

Use without installing

npx skills use grafana/skills@reconciler-logic

指定 Agent (Claude Code)

npx skills add grafana/skills --skill reconciler-logic -a claude-code -g -y

安装 repo 全部 skill

npx skills add grafana/skills --all -g -y

预览 repo 内 skill

npx skills add grafana/skills --list

SKILL.md

Frontmatter
{
    "name": "reconciler-logic",
    "license": "Apache-2.0",
    "description": "Implement reconcilers and watchers for grafana-app-sdk apps — write `TypedReconciler[*MyKind]` reconcile functions, apply generation-based skip patterns, do conflict-safe status updates via `resource.UpdateObject`, configure `BasicReconcileOptions` (namespace, label\/field filters, finalizer management), use `Watcher` for event-style handling, reconcile `UnmanagedKinds` (resources your app doesn't own), and register the whole thing in `app.go`. Use when writing a reconciler, implementing the reconcile loop, adding async business logic, handling create\/update\/delete events, processing resource state changes, scheduling periodic resyncs with `RequeueAfter`, picking between Watcher and Reconciler, or wiring a controller into `app.go` — even when the user says \"process this resource\", \"handle X events\", or \"write a controller\" without saying \"reconciler\"."
}

Reconciler Logic

Reconcilers are the async business-logic layer of a grafana-app-sdk app. The SDK enqueues a reconcile event when a resource is created, updated, or deleted; the reconciler observes the current state and drives the system toward the desired state.

Common Workflows

Implementing a new reconciler end-to-end

# 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

If the operator starts but no reconcile events fire when you create a resource:

  • Check BasicReconcileOptions.Namespace matches the resource's namespace
  • Check BasicReconcileOptions.LabelFilters / FieldSelectors — most "no events" issues are filter mismatches (kubectl get <resource> -o yaml to see labels)
  • Confirm the reconciler was attached to the right (latest) version of the kind

TypedReconciler — preferred pattern

operator.TypedReconciler handles type assertion and provides a strongly-typed 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 values: ReconcileActionCreated, ReconcileActionUpdated, ReconcileActionDeleted, ReconcileActionResynced.

To requeue after a delay (e.g. polling an external system):

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

Status updates with resource.UpdateObject

Always use resource.UpdateObject for status writes — it fetches the latest version before applying your update function, avoiding 409 Conflict errors when multiple reconcile events race:

_, 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"},
)

Do not use client.Update for status — it sends the full object and races with spec changes made by users.

Generation-based skip

Check LastObservedGeneration at the top of the reconcile function to avoid re-processing unchanged resources:

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

ReconcileOptions

Control informer behavior via BasicReconcileOptions on the AppManagedKind entry:

{
    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 (the default) wraps your reconciler in OpinionatedReconciler, which manages finalizers automatically so the SDK can guarantee clean deletion.

References

  • references/watchers.mdWatcher alternative (event-style Add/Update/Delete callbacks) + decision matrix for watcher vs reconciler
  • references/unmanaged-kinds.mdUnmanagedKinds for reconciling resources your app doesn't own, with UseOpinionated: false guidance and common failure modes
  • references/registration.md — full app.go wiring (client setup, multi-version registration, ValidateManifest) + common failure modes

External resources

Version History

  • b583762 Current 2026-07-06 00:34

Same Skill Collection

skills/grafana-app-sdk/admission-control/SKILL.md
skills/grafana-cloud/loki-label-analyzer/SKILL.md
skills/grafana-cloud/send-data/SKILL.md
skills/grafana-datasources/datasources-provisioning/SKILL.md
skills/grafana-lgtm/loki/SKILL.md
skills/grafana-lgtm/prometheus/SKILL.md
skills/grafana-plugins/audit-and-reduce-dependencies/SKILL.md
skills/grafana-plugins/check-npm/SKILL.md
template/SKILL.md
skills/grafana-app-sdk/app-sdk-concepts/SKILL.md
skills/grafana-app-sdk/cue-kind-definition/SKILL.md
skills/grafana-cloud/adaptive-metrics/SKILL.md
skills/grafana-cloud/admin/SKILL.md
skills/grafana-cloud/app-observability/SKILL.md
skills/grafana-cloud/assistant-mcp/SKILL.md
skills/grafana-cloud/cloud-integrations/SKILL.md
skills/grafana-cloud/cost-management/SKILL.md
skills/grafana-cloud/database-observability/SKILL.md
skills/grafana-cloud/dpm-finder/SKILL.md
skills/grafana-cloud/fleet-management/SKILL.md
skills/grafana-cloud/infrastructure/SKILL.md
skills/grafana-cloud/ml-ai/SKILL.md
skills/grafana-cloud/oncall-irm/SKILL.md
skills/grafana-cloud/private-connectivity/SKILL.md
skills/grafana-cloud/prometheus-cardinality-troubleshooter/SKILL.md
skills/grafana-cloud/prometheus-label-strategy/SKILL.md
skills/grafana-cloud/synthetic-monitoring-checks/SKILL.md
skills/grafana-cloud/testing/SKILL.md
skills/grafana-core/alerting-irm/SKILL.md
skills/grafana-core/alloy/SKILL.md
skills/grafana-core/beyla/SKILL.md
skills/grafana-core/dashboarding/SKILL.md
skills/grafana-core/grafana-oss/SKILL.md
skills/grafana-core/opentelemetry/SKILL.md
skills/grafana-core/promql/SKILL.md
skills/grafana-core/skill-authoring/SKILL.md
skills/grafana-k6/k6-cloud-investigate-test/SKILL.md
skills/grafana-k6/k6-docs/SKILL.md
skills/grafana-k6/k6-manage/SKILL.md
skills/grafana-k6/k6-perf-test-website/SKILL.md
skills/grafana-k6/k6-test-maintenance/SKILL.md
skills/grafana-k6/k6-trend-analysis/SKILL.md
skills/grafana-k6/k6/SKILL.md
skills/grafana-lgtm/mimir/SKILL.md
skills/grafana-lgtm/pyroscope/SKILL.md
skills/grafana-lgtm/tempo/SKILL.md
skills/grafana-plugins/grafana-scenes/SKILL.md
skills/grafana-plugins/plugin-bundle-size/SKILL.md
skills/grafana-plugins/react-19-plugin-migration/SKILL.md

Metadata

Files
0
Version
80bb293
Hash
16298230
Indexed
2026-07-06 00:34

Главная - Вики-сайт
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-04 10:31
浙ICP备14020137号-1 $Гость$