Documentation
¶
Overview ¶
Package agentlesspolicy: this file implements Task 5's conversion layer (openspec/changes/fleet-agentless-policy, "5. Resource: CRUD + import"): building the POST /api/fleet/agentless_policies request body from the Plugin Framework model (toCreateBody), and populating the model from the two distinct API response shapes this resource consumes -- the bundled create response (KibanaHTTPAPIsAgentlessPolicy, populateFromCreateResponse) and the package_policies read/update response (kbapi.PackagePolicy, populateFromPackagePolicy). See design.md Decision 4 for why there are two response shapes at all (no dedicated agentless GET/PUT endpoint exists).
Several kbapi request/response fields are anonymous Go structs (oapi-codegen emits an unnamed struct type per inline schema property, so e.g. KibanaHTTPAPIsAgentlessPolicy.Inputs and KibanaHTTPAPIsCreateAgentlessPolicyRequest.GlobalDataTags have no nameable Go type). Rather than hand-spelling those anonymous types at every call site (fragile, and liable to drift silently out of sync on the next kbapi regeneration), this file builds plain map[string]any/[]any trees matching the wire shape and converts via a JSON marshal/unmarshal round trip into the destination field (e.g. `json.Unmarshal(b, &body.Inputs)`) -- the same pattern already used elsewhere in this repo for anonymous API fields (see internal/kibana/dashboard/panel/*/api_conv.go) and by policyshape.VarsMapToTypedMap.
Package agentlesspolicy implements the elasticstack_fleet_agentless_policy resource (openspec/changes/fleet-agentless-policy). It mirrors the structure of internal/fleet/proxy: resource.go wires the entitycore Kibana resource envelope, models.go defines the Plugin Framework model, schema.go defines the schema, and create.go/read.go/update.go/delete.go implement the CRUD callbacks.
As of Task 3 of the OpenSpec change's tasks.md ("3. Resource: skeleton, model, and spike"), this package is a skeleton: the schema only carries identity attributes and the CRUD callbacks are stubs. The full schema lands in Task 4 and full CRUD lands in Task 5.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var MinVersion = version.Must(version.NewVersion("9.3.0"))
MinVersion is the minimum Kibana version required for the Fleet agentless policies API. The API is experimental and was added in Kibana 9.3.0 (see proposal.md and design.md of the fleet-agentless-policy OpenSpec change, and the spec.md "Version gating" requirement).
Functions ¶
func DetectCloudSignals ¶
func DetectCloudSignals(ctx context.Context, client *clients.KibanaScopedClient) (serverless bool, cloudProxied bool, ok bool)
DetectCloudSignals implements Task 6.2 of the fleet-agentless-policy OpenSpec change (design.md Decision 7; specs/fleet-agentless-policy/spec.md "Deployment topology preflight check"). It makes a single call to Kibana's own `GET /api/status` endpoint and reports the two independent signals that distinguish an Elastic Cloud Hosted or Serverless deployment from a self-managed one, plus whether the probe itself completed successfully.
This is the single shared implementation of the cloud/serverless probe in this package: checkDeploymentTopology below wraps it with a fail-open production policy, and acc_helpers_test.go's isConfirmedCloudOrServerless wraps it with the opposite, fail-closed policy for deciding whether to skip acceptance tests. It deliberately duplicates (rather than imports) the response-body shape used by internal/clients/kibanaoapi.GetKibanaStatus, whose DTO type is unexported and whose caching/version-gating machinery is out of scope for this narrowly-scoped preflight probe.
Detection approach chosen, and why (see design.md Open Question 5 for the alternatives considered):
- Serverless is already unambiguously reported by Kibana itself via `version.build_flavor == "serverless"` -- the same signal clients.KibanaScopedClient.EnforceMinVersion already relies on to short-circuit version checks for serverless deployments (see internal/clients/version_utils.go). This preflight reuses that same signal rather than re-deriving it.
- Cloud Hosted and self-managed both report build_flavor "traditional", so flavor alone cannot tell them apart (confirmed empirically below). The additional signal used here is the presence of the `X-Found-Handling-Cluster` / `X-Found-Handling-Instance` HTTP response headers on the `/api/status` response, injected by Elastic Cloud's edge proxy in front of every Cloud Hosted (and Serverless) deployment. This is an infrastructure-level signal external to Kibana's own plugins, so -- unlike a heuristic based on which Kibana plugins happen to be registered -- it is not sensitive to Kibana-version-specific plugin churn and cannot be toggled via kibana.yml.
- Two alternatives from Open Question 5 were considered and ruled out: (a) reading the `xpack.fleet.agentless.enabled` kibana.yml flag -- there is no read API for it (it is not an Elasticsearch cluster setting, and no Fleet settings endpoint echoes it); (b) a `dry_run` preflight POST to `/api/fleet/agentless_policies` -- there is no dry_run parameter on that endpoint in the generated kbapi client, and empirically POSTing malformed bodies to it returns byte-for-byte identical validation errors on a self-managed and a Cloud Hosted Kibana (schema validation runs before any topology-aware business logic), so the endpoint itself cannot be used as a non-destructive topology probe.
Empirical verification (2026-07-01, see the fleet-agentless-policy OpenSpec change's Task 6 report): against a live Kibana 9.4.3 Elastic Cloud Hosted deployment, `GET /api/status` returned build_flavor "traditional" with both X-Found-Handling-* headers present -- classified cloud, preflight passes. Against a self-managed docker-compose Kibana 9.4.0, the same call returned "traditional" with neither header present -- classified self-managed, preflight fails closed.
Returns:
- serverless: true if `version.build_flavor == "serverless"` was observed.
- cloudProxied: true if either X-Found-Handling-* header was observed.
- ok: false if the probe itself did not complete (network error, non-200, or a malformed response body) -- callers should treat this as "inconclusive", not as "self-managed", and apply their own fallback policy. When ok is false, serverless and cloudProxied are always false.
func NewResource ¶
NewResource is a helper function to simplify the provider implementation.
Types ¶
type Resource ¶
type Resource struct {
*entitycore.KibanaResource[agentlessPolicyModel]
*fleet.SpaceImporter
}
Resource implements the Fleet agentless policy resource.