Files
grafana/apps/example/pkg/app/reconciler.go
Austin Pond bf65c43783 Apps: Add Example App to ./apps (#112069)
* [API Server] Add Example App for reference use.

* Remove Printlns.

* Upgrade app-sdk to v0.46.0, update apps to handle breaking changes.

* Only start the reconciler for the example app if the v1alpha1 API version is enabled.

* Some comment doc updates.

* Run make update-workspace

* Set codeowner for /apps/example

* Run make gofmt and make update-workspace

* Run prettier on apps/example/README.md

* Add COPY apps/example to Dockerfile

* Add an authorizer to the example app.

* Fix import ordering.

* Update apps/example/kinds/manifest.cue

Co-authored-by: Owen Diehl <ow.diehl@gmail.com>

* Run make update-workspace

* Re-run make gen-go for enterprise import updates

* Run make update-workspace

---------

Co-authored-by: Owen Diehl <ow.diehl@gmail.com>
2025-10-27 12:01:10 -04:00

59 lines
2.3 KiB
Go

package app
import (
"context"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana-app-sdk/operator"
"github.com/grafana/grafana-app-sdk/resource"
"github.com/grafana/grafana/apps/example/pkg/apis/example/v1alpha1"
)
// ExampleReconciler wraps TypedReconciler to simplify some of our reconciliation logic,
// as TypedReconciler will handle type checking of the input object for us.
type ExampleReconciler struct {
operator.TypedReconciler[*v1alpha1.Example]
client resource.Client
}
func NewExampleReconciler(client resource.Client) *ExampleReconciler {
reconciler := ExampleReconciler{
TypedReconciler: operator.TypedReconciler[*v1alpha1.Example]{},
client: client,
}
reconciler.ReconcileFunc = reconciler.doReconcile
return &reconciler
}
// doReconcile is the main reconciliation loop for our app's Example reconciler.
// All it does is print a log message and then update the last observed generation in the status
// (if the request is a DELETE, it doesn't try to update the status, as the update would fail).
func (e *ExampleReconciler) doReconcile(ctx context.Context, req operator.TypedReconcileRequest[*v1alpha1.Example]) (operator.ReconcileResult, error) {
if req.Object.GetGeneration() == req.Object.Status.LastObservedGeneration {
// Skip if we've already processed this spec
return operator.ReconcileResult{}, nil
}
logging.FromContext(ctx).Info("reconciling example", "name", req.Object.GetName(), "namespace", req.Object.GetNamespace(), "action", operator.ResourceActionFromReconcileAction(req.Action))
// If this is a delete, we don't need to do anything
if req.Action == operator.ReconcileActionDeleted {
return operator.ReconcileResult{}, nil
}
// Update the status.
// We use resource.UpdateObject here to handle conflicts when doing the update,
// as it gets the current state, performs our update function, then pushes to the remote
_, err := resource.UpdateObject(ctx, e.client, req.Object.GetStaticMetadata().Identifier(), func(obj *v1alpha1.Example, _ bool) (*v1alpha1.Example, error) {
obj.Status.LastObservedGeneration = req.Object.GetGeneration()
return obj, nil
}, resource.UpdateOptions{
Subresource: "status",
})
if err != nil {
return operator.ReconcileResult{}, err
}
return operator.ReconcileResult{}, nil
}