grafana/apps/provisioning/pkg/apis
Roberto Jiménez Sánchez adc5f2542e
Provisioning: Capability-based config-driven supported resources (#125856)
* Provisioning: Introduce flag-gated seam for supported resources

Add SupportedResources and SupportsFolderAnnotationResources constructors that
accept a featuremgmt.FeatureToggles checker and today return exactly the static
SupportedProvisioningResources / SupportsFolderAnnotation sets. This establishes
a single seam where future flag-gated resources can be appended, without changing
current behaviour and without adding any new resource or feature flag.

Migrate the pipeline consumers (export loop, export quota stats, migrate cleanup,
authorizer, folder-annotation write path) to call the constructors instead of
ranging over the package vars directly. The constructors tolerate a nil checker;
no flag is consulted yet, so the effective set is identical to today.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Make supported-resource set a ResourceClients concern

Move the supported-resource set behind the ResourceClients interface and
register extra types through the client factory instead of a flag-gated
package function.

- SupportedResources() and SupportsFolderAnnotationResources() are now
  methods on ResourceClients, returning the static base set plus any extra
  resources registered with the factory.
- NewClientFactory / NewClientFactoryForMultipleAPIServers accept variadic
  SupportedResource registrations ({GVR, SupportsFolderAnnotation}); the
  factory threads them into the namespaced clients.
- Consumers (authorizer, folder-annotation write path, export loop, quota
  stats, migrate cleanup) resolve the set through the clients they already
  hold; NewAuthorizer now takes a ResourceClients.

No behaviour change today: with no registered extras the effective set
equals the current static set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Collapse supported-resource accessors into one method

Address review: precompute the supported set and expose it through a single
interface method.

- SupportedResources() now returns []SupportedResource (each carrying its
  folder-annotation flag), replacing the separate SupportsFolderAnnotationResources.
- The merged base + registered set is computed once per factory and reused by
  every ResourceClients it produces, instead of merging on each call.
- Consumers read .GVR / .SupportsFolderAnnotation off the entries; the
  folder-annotation write path uses a supportsFolderAnnotation helper.

Adds a unit test for resources.go covering that the folder annotation is not
written for resources that do not support folders (and the contrasting case
that it is).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Drive the supported-resource set from config

Per the agreed design (git-ui-sync #1167), make the provisionable resource set
config-driven with a sensible default, instead of a package-level var. This
supersedes the per-resource feature-flag approach.

- New [provisioning] keys: resources and folder_resources (both default to
  "dashboards | folders"), parsed into cfg.ProvisioningResources /
  cfg.ProvisioningFolderResources, mirroring allowed_targets / repository_types.
- resources.BuildSupportedResources(resourceNames, folderResourceNames) maps the
  configured short names to the effective []SupportedResource (folder_resources
  is the subset that carries the folder annotation). Unknown names fail fast.
- register.go builds the set from cfg and threads it through NewAPIBuilder into
  NewClientFactory; ResourceClients.SupportedResources() exposes it. With no
  config the factory falls back to the static SupportedProvisioningResources.
- The effective set is surfaced on the settings endpoint (RepositoryViewList
  .availableResources) the way allowed_targets is, so the frontend can read it.

Adding a resource is now a config change (folder-contained kinds go in both
lists; org-scoped kinds only in resources) — no feature flag, no codegen.
Kind/version are still resolved at runtime via discovery; folder-scope is
supplied by folder_resources until it can be declared as kind metadata.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Config carries the group+kind+folder triplet per resource

Address review: drive the supported set entirely from config with no hardcoded
name→GVR map, and identify resources by GroupKind (the API version and plural
resource are resolved at runtime via discovery).

- Each resource is declared in its own [provisioning.resources.<kind>.<group>]
  section (mirroring the per-resource [unified_storage.<resource>.<group>]
  convention), with a folder=<bool> key (extensible for future per-kind settings).
  cfg.ProvisioningResources holds the parsed {Group, Kind, SupportsFolderAnnotation}
  triplets; defaults to folders + dashboards.
- resources.SupportedResource is now {schema.GroupKind, SupportsFolderAnnotation}.
  Consumers that need the plural resource (authorizer bulk checks + resolveFileGVR,
  export loop, migrate cleanup, export quota counter) resolve it via clients.ForKind.
  resolveFileGVR now matches on Group+Kind (a kind that shares a group is no longer
  mis-authorized as the first kind in that group).
- The folder-annotation write path matches on the parsed GVK; no discovery needed.
- The effective set is surfaced on the settings endpoint as "<kind>.<group>"
  identifiers (RepositoryViewList.availableResources), with an integration test.

Folder support is configured per kind because, although unified storage tracks it
(apistore.StorageOptions.EnableFolderSupport), it is server-side and not exposed via
the API/discovery, so a remote provisioning operator cannot read it. Declaring
folder-scope as discoverable kind metadata is a follow-up that would drop the config.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Regenerate frontend API client for availableResources

Run `yarn workspace @grafana/api-clients generate-apis` after adding
RepositoryViewList.availableResources to the OpenAPI: regenerates the processed
specs in @grafana/openapi and the RTK Query types in @grafana/api-clients, so
the frontend client exposes the new field.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Add extra-resources settings integration test + lint fix

- Simplify routes.go to r.String() (staticcheck QF1008: drop redundant embedded
  GroupKind selector).
- Add a ProvisioningResources option to testinfra.GrafanaOpts that writes
  [provisioning.resources.<kind>.<group>] sections.
- Add an integration test asserting a resource added purely through configuration
  is surfaced on the settings endpoint (availableResources).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Surface supported resources as enabled-aware triplets

Address review on the settings endpoint shape:

- availableResources is now a list of structured descriptors (SupportedResource)
  carrying group, kind, supportsFolderAnnotation and enabled — not opaque strings,
  and the folder field is clearly named.
- Each [provisioning.resources.<kind>.<group>] section gains an enabled key
  (default true). Disabled resources are still declared and surfaced, but the
  pipeline (authorizer/export/migrate/quota) acts only on enabled ones —
  ResourceClients.SupportedResources() returns the enabled subset, while the
  settings endpoint surfaces the full declared set.
- defaults.ini declares library panels and playlists as disabled by default
  (folders + dashboards remain enabled).

Regenerated deepcopy, OpenAPI (Go + snapshots + @grafana/openapi specs) and the
@grafana/api-clients RTK Query types. Adds/extends settings + client unit tests
and the settings-endpoint integration tests (default set incl. disabled, and an
extra resource added via config).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Thread supported resources into the operator; rename to EnableFolderSupport

- The operator (multi-API-server path) now builds the supported set from
  cfg.ProvisioningResources and passes it to NewClientFactoryForMultipleAPIServers,
  matching the in-process register.go wiring.
- Rename the folder field/setting/JSON to EnableFolderSupport / enableFolderSupport,
  matching unified storage's apistore.StorageOptions.EnableFolderSupport and its
  camelCase config-key convention. Config key folder -> enableFolderSupport.

Regenerated OpenAPI (Go + snapshots + @grafana/openapi) and the @grafana/api-clients
RTK Query types. Updated unit tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Config-driven strict-validation exemption + parser support gate; operator aggregated URL

- Strict-validation exemption is now config-driven (P0.5): per-resource
  skipStrictValidation key (default false), replacing the hardcoded
  `gvr == DashboardResource` check in the parser. Dashboards keep the exemption
  via defaults.ini / the static fallback set.
- The parser now rejects resources that are not enabled/supported with a
  ResourceValidationError, driven by the enabled supported set.
- Operator: add an aggregated_server_url [operator] key; enabled resources whose
  API group has no dedicated server URL are served by the aggregated API server
  (fails loudly if a resource needs it and it is unset). The operator also passes
  SkipStrictValidation through.

Unit tests: parser skip-strict-validation wiring and unsupported-resource
rejection. (folder field/key already renamed to EnableFolderSupport/enableFolderSupport.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Integration test for rejecting a disabled resource via files API

Writing a playlist (declared but disabled by default) through the files endpoint
is rejected as a client error referencing resource enablement/support.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Operator explicitly skips built-in groups for the aggregated server

Dashboards, folders and provisioning have dedicated server URLs, so skip those
groups explicitly when assigning the aggregated API server URL to extra resources.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Capability-based supported resources (shared grammar + parser)

Reshape the config-driven supported set to a single shared grammar, parser and
type, per the capability-based design:

- One INI key `[provisioning] resources` = comma-separated tokens
  `<group>/<Kind>[:cap...]`. Capabilities (all default off): folder (folder-scoped),
  skipvalidation (skip validation), disabled (declared but inert). No more
  per-resource sections or enabled/folder bools.
- resources.SupportedResource is now {schema.GroupKind; Capabilities sets.Set[string]}
  with IsActive()/IsValidated()/IsFolderScoped(); the shared
  resources.ParseSupportedResources([]string) parses+validates the whole list
  (strict, fails fast at startup) and is the single entry point for both OSS and
  the (future) enterprise MT side.
- pkg/setting holds the raw []string token list (parsed in register.go and the
  operator, avoiding a setting->resources import cycle).
- Settings endpoint surfaces availableResources as {group, kind, capabilities[]}.
- defaults.ini: folders + dashboards (folder), library panels (folder, disabled),
  playlists (disabled).

Regenerated deepcopy, OpenAPI (Go + snapshots + @grafana/openapi) and the
@grafana/api-clients RTK Query types. Updated unit + integration tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Revert parser support-gate and strict-validation changes

The parser support-gate and per-resource skip-validation belong to separate
draft PRs. Restore parser.go to its pre-PR state (dashboard exemption hack),
drop the now-unused isSupportedResource/skipsStrictValidation helpers, and
remove the tests that exercised the reverted gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Re-attach availableResources $ref in OpenAPI post-processing

The settings connector adds RepositoryViewList to the served spec via an empty
ReferenceCallback, which strips array-item $refs. RepositoryViewList.items is
manually re-attached; do the same for the new availableResources field so its
SupportedResource element type resolves instead of degrading to {default:{}}.
Regenerate the v0alpha1/v1beta1 OpenAPI snapshots to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Surface availableResources as enabled/disabled, not capabilities

The settings endpoint only needs to tell the frontend which declared resources
are active and which are disabled, not the full internal capability set. Replace
SupportedResource.capabilities[] with a single disabled bool on the API DTO
(derived from the internal capability model via IsActive). Drop the now-unused
CapabilityList helper, regenerate deepcopy/openapi/snapshots and the frontend
RTK Query client, and update the settings integration test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Provisioning: Regenerate deepcopy/openapi via codegen

Run hack/update-codegen.sh so the generated deepcopy and openapi for the
SupportedResource disabled-bool change match canonical generator output
(function ordering, shallow-copy for AvailableResources, violation list),
fixing the CODEGEN_VERIFY check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 13:57:38 +02:00
..
admission Provisioning: api block operations in pending delete resources (#120665) 2026-04-08 10:56:12 +02:00
apifmt Provisioning: Refactor admission/validation logic into app package (#116483) 2026-01-21 09:50:42 +01:00
auth Provisioning: Per-verb fallback for the files subresource (#123867) 2026-04-30 15:01:11 +02:00
manifestdata deps: update grafana-app-sdk to v0.52.1 (#120445) 2026-03-25 13:28:01 -06:00
provisioning/v0alpha1 Provisioning: Capability-based config-driven supported resources (#125856) 2026-06-08 13:57:38 +02:00