Files
fleet/tools/gitops-auto-complete/extra_data.go
T
Jonathan Katz b99e5564e9 GitOps schema generator and IDE integration (#49717)
### Summary:
Generates a json schema for valid GitOps yaml files, to be used with
[yaml-language-server](https://github.com/redhat-developer/yaml-language-server)
for IDE integration.

This PR includes the actual generated file, so it can be used without
running the tool. All files are in `/tools/gitops-autto-complete`, so
nothing else gets affected.

#### What it adds:
- Complete json schema that defines valid GitOps yaml files and can be
integrated with
[yaml-language-server](https://github.com/redhat-developer/yaml-language-server).
- Auto-completion, error checking, type checking, descriptions.
- Defines all keys for osquery options/flags (based on
`server/fleet/agent_options_generated.go`).
- Additional validation: required keys, strings that must be enclosed in
quotation marks, path support.
- Additional data: descriptions from code comments, notices for fields
that don't reset if null or empty.

#### Limitations:
- Some structs and data are duplicated into the tool and will inevitebly
mismatch over time, because the structs used for gitops are not
sufficient for the schema generation:
- Some fields use an interface/any type that so can't be used for the
schema generation.
- Some important details are not encoded in the type or struct tags for
gitops fields at all.
- Some details (like required fields) are encoded in the Validate()
interface, but the IDE integration cannot run Go code.
- Doesn't work with all yaml file types used for gitops (like a yaml
file that specifies multiple software packages), only the default/fleet
level files. This will require having a subsection of the schema for
each type of file, and some way to detect what it actually is (maybe
specifying the schema in the file itself).
- Requires manual setup to integrate with IDE, it's not an easy to use
extension currently.

#### Dependencies:
- `invopop/jsonschema` reflects Fleet's GitOps structs into the schema.
- `santhosh-tekuri/jsonschema/v6` validates the test fixtures against
that schema.
- `ghodss/yaml` decodes the fixture YAML the way fleetctl does.
 


https://github.com/user-attachments/assets/b6ffacd0-e602-41a2-b46a-7d10300c6ea5



## Testing

- [x] QA'd all new/changed functionality manually
- I have been using and working on this for the past week so it's in a
pretty good state, but some descriptions or keys are probably still
missing.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added GitOps YAML auto-completion powered by a comprehensive JSON
Schema.
* Added validation for GitOps configuration structure, supported fields,
data types, required combinations, and unknown keys.
* Added support for external file references using `path` and `paths` in
supported sections.
* Added clearer guidance for deprecated fields and special field
behaviors.
* **Bug Fixes**
* Improved detection of incorrectly typed values and invalid
configuration shapes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 11:50:50 -04:00

121 lines
4.4 KiB
Go

package main
// Data tables that Fleet's Go structs don't express but GitOps YAML relies on:
// declarative apply notes, path-reference support, and installer-reference keys.
// gitops sends a fully materialized config, so omitting a key normally resets it.
// These hover notes cover the keys that instead keep their value.
const (
declarativeKeepAlways = "GitOps: kept unchanged when omitted, null, or empty."
declarativeKeepUnlessEmpty = "GitOps: kept unchanged when omitted or null; cleared when set to an empty value."
declarativeKeepOnOmit = "GitOps: kept unchanged when omitted; cleared when set to null or empty."
)
// declarativeExceptions maps a gitops key (dotted path) to its hover note. Only the
// exceptions to the reset-on-omit default are listed, verified against a live apply.
var declarativeExceptions = map[string]string{
// Google service-account credentials, preserved so a re-apply need not resend
// the secret. UI GitOps mode is merged onto the existing config.
"org_settings.integrations.google_calendar.api_key_json": declarativeKeepAlways,
"org_settings.integrations.google_workspace.api_key_json": declarativeKeepAlways,
"org_settings.gitops": declarativeKeepAlways,
// host_expiry is the documented exception that isn't reset when omitted, but
// an explicit empty object still resets it. Applies at team and org level.
"settings.host_expiry_settings": declarativeKeepUnlessEmpty,
"org_settings.host_expiry_settings": declarativeKeepUnlessEmpty,
// Label host membership: omitting keeps the current members, an explicit empty
// list clears them. From the docs and label parsing.
"labels.hosts": declarativeKeepOnOmit,
}
// pathReferenceDefinitions are the $defs that accept a `path` (one external file) in
// place of inline content. pathsReferenceDefinitions additionally accept `paths` (a
// single glob string). Defs whose Go type embeds fleet.BaseItem (reports, scripts,
// configuration_profiles) already get both from reflection, so they aren't listed.
var pathReferenceDefinitions = []string{
"GitOpsOrgSettings", "GitOpsFleetSettings", "AgentOptions", "ControlsWithTypes",
"SoftwarePackageSpec",
}
var pathsReferenceDefinitions = []string{
"GitOpsPolicySpec", "LabelSpec",
}
// requiredKeyRule gates a $def: an item is valid if it has all the keys of any one of
// its validKeyCombinations. So {{"a"},{"b"}} means "a or b" and {{"a","b"}} means
// "a and b". Fleet enforces these at gitops apply time in validation code rather than
// struct tags. Where an item can also be a file reference, path/paths are listed as
// their own combinations.
type requiredKeyRule struct {
definition string
message string
validKeyCombinations [][]string
}
var requiredKeys = []requiredKeyRule{
{
definition: "SoftwarePackageSpec",
message: "A package must set one of: url, hash_sha256, or path.",
validKeyCombinations: [][]string{
{"url"},
{"hash_sha256"},
{"path"},
},
},
{
definition: "TeamSpecAppStoreApp",
message: "An app_store_apps entry must set app_store_id.",
validKeyCombinations: [][]string{{"app_store_id"}},
},
{
definition: "MaintainedAppSpec",
message: "A fleet_maintained_apps entry must set slug.",
validKeyCombinations: [][]string{{"slug"}},
},
{
definition: "LabelSpec",
message: "A label must set name (or reference a file with path/paths).",
validKeyCombinations: [][]string{
{"name"},
{"path"},
{"paths"},
},
},
{
definition: "GitOpsPolicySpec",
message: "A policy must set name (or reference a file with path/paths).",
validKeyCombinations: [][]string{
{"name"},
{"path"},
{"paths"},
},
},
{
definition: "Query",
message: "A report must set name and query (or reference a file with path/paths).",
validKeyCombinations: [][]string{
{"name", "query"},
{"path"},
{"paths"},
},
},
{
definition: "YaraRule",
message: "A yara_rules entry must set path.",
validKeyCombinations: [][]string{{"path"}},
},
}
// strictStringKeys are the keys kept as strict strings, so a wrong-typed value like
// `url: 12345` is caught. `path`/`paths` are excluded so they stay nullable.
var strictStringKeys = map[string][]string{
"SoftwarePackageSpec": {"url", "hash_sha256"},
"TeamSpecAppStoreApp": {"app_store_id"},
"MaintainedAppSpec": {"slug"},
"LabelSpec": {"name"},
"GitOpsPolicySpec": {"name"},
"Query": {"name", "query"},
}