diff --git a/tools/gitops-auto-complete/README.md b/tools/gitops-auto-complete/README.md new file mode 100644 index 00000000000..fa4dcea5f6e --- /dev/null +++ b/tools/gitops-auto-complete/README.md @@ -0,0 +1,87 @@ +# gitops-auto-complete + +Generates a JSON schema from Fleet's GitOps Go structs so +[yaml-language-server](https://github.com/redhat-developer/yaml-language-server) can +offer completion, hover docs, and validation while you write GitOps YAML. + +## How to use + +### Build the schema + +The tool is a separate Go module, so run it from its own directory: + +```bash +cd tools/gitops-auto-complete +go run . generated-schema.json +``` + +The argument is the output file, or omit it to print to stdout. Re-run it whenever +the relevant Fleet structs change. + +### Set up with yaml-language-server + +Point yaml-language-server at the generated file. It's used by Neovim, the VS Code +YAML extension, and others. There are two ways to do this: + +- Map it to your GitOps files with the `yaml.schemas` setting, which maps a schema + path to file globs. +- Or add a modeline to the top of a single file: + + ```yaml + # yaml-language-server: $schema=/absolute/path/to/generated-schema.json + ``` + +### Neovim and lazy.nvim example + +```lua +{ + "neovim/nvim-lspconfig", + dependencies = { + { "mason-org/mason.nvim", opts = {} }, + "mason-org/mason-lspconfig.nvim", + }, + config = function() + vim.lsp.config("yamlls", { + settings = { + yaml = { + schemas = { + -- schema file -> which YAML files it applies to + ["/absolute/path/to/generated-schema.json"] = { + "**/default.yml", + "**/teams/*.yml", + "**/fleets/*.yml", + }, + }, + }, + }, + }) + vim.lsp.enable("yamlls") + end, +} +``` + +Install the server once (`:MasonInstall yaml-language-server`), reload, and open a +GitOps file. Hover a key with `K` to see its type and docs. + +## How it works + +Reflects a `GitOpsSpec` struct that mirrors the real top-level GitOps keys, such as +`org_settings`, `controls`, `software`, and `policies`, reusing Fleet's own types for +each section, via [`invopop/jsonschema`](https://github.com/invopop/jsonschema). It +then post-processes the result so the schema matches how GitOps files are written: +file-path references, legacy key aliases, required fields for an item, and field docs +pulled from Go comments. + +It's a separate Go module with a `replace` back to the repo, so it builds from inside +the repo without adding dependencies to the root `go.mod`. + +## Known limitations + +- The schema is filename-agnostic, but Fleet applies some keys differently by file. + For example, `agent_options` and `reports` are rejected in `no-team.yml` and the + unassigned file. The schema still accepts them there, so that mistake shows up at + `fleetctl` apply time, not in the editor. +- `GitOpsSpec` and `ControlsWithTypes` are hand-written mirrors of `spec.GitOps` and + `spec.GitOpsControls`, because those spec structs are untyped or untagged and reflect + poorly. `TestControlsKeysCoverSpec` catches a controls-key drift, but a new top-level + key has to be added to `GitOpsSpec` by hand, as `custom_host_vitals` was. diff --git a/tools/gitops-auto-complete/extra_data.go b/tools/gitops-auto-complete/extra_data.go new file mode 100644 index 00000000000..264c879888a --- /dev/null +++ b/tools/gitops-auto-complete/extra_data.go @@ -0,0 +1,120 @@ +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"}, +} diff --git a/tools/gitops-auto-complete/generated-schema.json b/tools/gitops-auto-complete/generated-schema.json new file mode 100644 index 00000000000..82126df1533 --- /dev/null +++ b/tools/gitops-auto-complete/generated-schema.json @@ -0,0 +1,6153 @@ +{ + "$defs": { + "ActivitiesWebhookSettings": { + "additionalProperties": false, + "properties": { + "destination_url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "enable_activities_webhook": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "ActivityExpirySettings": { + "additionalProperties": false, + "description": "ActivityExpirySettings contains settings pertaining to automatic activities cleanup.", + "properties": { + "activity_expiry_enabled": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "activity_expiry_window": { + "description": "type: `integer`" + }, + "preserve_host_activities_on_reenrollment": { + "description": "PreserveHostActivitiesOnReenrollment controls whether existing host\nactivities, MDM commands, etc. are kept when a managed host re-enrolls.\nDefaults to true for upgraded installs (preserves prior behavior) and\nfalse for fresh installs.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "AgentOptions": { + "additionalProperties": false, + "properties": { + "command_line_flags": { + "additionalProperties": false, + "description": "type: `object`", + "properties": { + "alarm_timeout": { + "type": [ + "integer", + "null" + ] + }, + "allow_unsafe": { + "type": [ + "boolean", + "null" + ] + }, + "alsologtostderr": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_accept_socket_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_apparmor_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_config": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_failed_socket_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_fim_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_fork_process_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_kill_process_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_null_accept_socket_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_process_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_seccomp_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_selinux_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_sockets": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_user_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_backlog_limit": { + "type": [ + "integer", + "null" + ] + }, + "audit_backlog_wait_time": { + "type": [ + "integer", + "null" + ] + }, + "audit_debug": { + "type": [ + "boolean", + "null" + ] + }, + "audit_fim_debug": { + "type": [ + "boolean", + "null" + ] + }, + "audit_fim_show_accesses": { + "type": [ + "boolean", + "null" + ] + }, + "audit_force_reconfigure": { + "type": [ + "boolean", + "null" + ] + }, + "audit_force_unconfigure": { + "type": [ + "boolean", + "null" + ] + }, + "audit_persist": { + "type": [ + "boolean", + "null" + ] + }, + "audit_show_partial_fim_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_show_untracked_res_warnings": { + "type": [ + "boolean", + "null" + ] + }, + "augeas_lenses": { + "type": [ + "string", + "null" + ] + }, + "aws_access_key_id": { + "type": [ + "string", + "null" + ] + }, + "aws_debug": { + "type": [ + "boolean", + "null" + ] + }, + "aws_disable_imdsv1_fallback": { + "type": [ + "boolean", + "null" + ] + }, + "aws_enable_proxy": { + "type": [ + "boolean", + "null" + ] + }, + "aws_enforce_fips": { + "type": [ + "boolean", + "null" + ] + }, + "aws_firehose_endpoint": { + "type": [ + "string", + "null" + ] + }, + "aws_firehose_period": { + "type": [ + "integer", + "null" + ] + }, + "aws_firehose_region": { + "type": [ + "string", + "null" + ] + }, + "aws_firehose_stream": { + "type": [ + "string", + "null" + ] + }, + "aws_imdsv2_request_attempts": { + "type": [ + "integer", + "null" + ] + }, + "aws_imdsv2_request_interval": { + "type": [ + "integer", + "null" + ] + }, + "aws_kinesis_disable_log_status": { + "type": [ + "boolean", + "null" + ] + }, + "aws_kinesis_endpoint": { + "type": [ + "string", + "null" + ] + }, + "aws_kinesis_period": { + "type": [ + "integer", + "null" + ] + }, + "aws_kinesis_random_partition_key": { + "type": [ + "boolean", + "null" + ] + }, + "aws_kinesis_region": { + "type": [ + "string", + "null" + ] + }, + "aws_kinesis_stream": { + "type": [ + "string", + "null" + ] + }, + "aws_profile_name": { + "type": [ + "string", + "null" + ] + }, + "aws_proxy_host": { + "type": [ + "string", + "null" + ] + }, + "aws_proxy_password": { + "type": [ + "string", + "null" + ] + }, + "aws_proxy_port": { + "type": [ + "integer", + "null" + ] + }, + "aws_proxy_scheme": { + "type": [ + "string", + "null" + ] + }, + "aws_proxy_username": { + "type": [ + "string", + "null" + ] + }, + "aws_region": { + "type": [ + "string", + "null" + ] + }, + "aws_secret_access_key": { + "type": [ + "string", + "null" + ] + }, + "aws_session_token": { + "type": [ + "string", + "null" + ] + }, + "aws_sts_arn_role": { + "type": [ + "string", + "null" + ] + }, + "aws_sts_region": { + "type": [ + "string", + "null" + ] + }, + "aws_sts_session_name": { + "type": [ + "string", + "null" + ] + }, + "aws_sts_timeout": { + "type": [ + "integer", + "null" + ] + }, + "bpf_buffer_storage_size": { + "type": [ + "integer", + "null" + ] + }, + "bpf_perf_event_array_exp": { + "type": [ + "integer", + "null" + ] + }, + "buffered_log_max": { + "type": [ + "integer", + "null" + ] + }, + "carver_block_size": { + "type": [ + "integer", + "null" + ] + }, + "carver_compression": { + "type": [ + "boolean", + "null" + ] + }, + "carver_continue_endpoint": { + "type": [ + "string", + "null" + ] + }, + "carver_disable_function": { + "type": [ + "boolean", + "null" + ] + }, + "carver_expiry": { + "type": [ + "integer", + "null" + ] + }, + "carver_start_endpoint": { + "type": [ + "string", + "null" + ] + }, + "config_accelerated_refresh": { + "type": [ + "integer", + "null" + ] + }, + "config_check": { + "type": [ + "boolean", + "null" + ] + }, + "config_dump": { + "type": [ + "boolean", + "null" + ] + }, + "config_enable_backup": { + "type": [ + "boolean", + "null" + ] + }, + "config_path": { + "type": [ + "string", + "null" + ] + }, + "config_plugin": { + "type": [ + "string", + "null" + ] + }, + "config_refresh": { + "type": [ + "integer", + "null" + ] + }, + "config_tls_endpoint": { + "type": [ + "string", + "null" + ] + }, + "config_tls_max_attempts": { + "type": [ + "integer", + "null" + ] + }, + "daemonize": { + "type": [ + "boolean", + "null" + ] + }, + "database_dump": { + "type": [ + "boolean", + "null" + ] + }, + "database_path": { + "type": [ + "string", + "null" + ] + }, + "decorations_top_level": { + "type": [ + "boolean", + "null" + ] + }, + "disable_audit": { + "type": [ + "boolean", + "null" + ] + }, + "disable_caching": { + "type": [ + "boolean", + "null" + ] + }, + "disable_carver": { + "type": [ + "boolean", + "null" + ] + }, + "disable_database": { + "type": [ + "boolean", + "null" + ] + }, + "disable_decorators": { + "type": [ + "boolean", + "null" + ] + }, + "disable_distributed": { + "type": [ + "boolean", + "null" + ] + }, + "disable_endpointsecurity": { + "type": [ + "boolean", + "null" + ] + }, + "disable_endpointsecurity_fim": { + "type": [ + "boolean", + "null" + ] + }, + "disable_enrollment": { + "type": [ + "boolean", + "null" + ] + }, + "disable_events": { + "type": [ + "boolean", + "null" + ] + }, + "disable_extensions": { + "type": [ + "boolean", + "null" + ] + }, + "disable_hash_cache": { + "type": [ + "boolean", + "null" + ] + }, + "disable_logging": { + "type": [ + "boolean", + "null" + ] + }, + "disable_memory": { + "type": [ + "boolean", + "null" + ] + }, + "disable_reenrollment": { + "type": [ + "boolean", + "null" + ] + }, + "disable_tables": { + "type": [ + "string", + "null" + ] + }, + "disable_watchdog": { + "type": [ + "boolean", + "null" + ] + }, + "distributed_denylist_duration": { + "type": [ + "integer", + "null" + ] + }, + "distributed_interval": { + "type": [ + "integer", + "null" + ] + }, + "distributed_loginfo": { + "type": [ + "boolean", + "null" + ] + }, + "distributed_plugin": { + "type": [ + "string", + "null" + ] + }, + "distributed_tls_max_attempts": { + "type": [ + "integer", + "null" + ] + }, + "distributed_tls_read_endpoint": { + "type": [ + "string", + "null" + ] + }, + "distributed_tls_write_endpoint": { + "type": [ + "string", + "null" + ] + }, + "dns_resolver_refresh_interval": { + "type": [ + "integer", + "null" + ] + }, + "docker_socket": { + "type": [ + "string", + "null" + ] + }, + "enable_bpf_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_dns_lookup_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_extensions_watchdog": { + "type": [ + "boolean", + "null" + ] + }, + "enable_file_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_foreign": { + "type": [ + "boolean", + "null" + ] + }, + "enable_keyboard_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_mouse_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_ntfs_event_publisher": { + "type": [ + "boolean", + "null" + ] + }, + "enable_numeric_monitoring": { + "type": [ + "boolean", + "null" + ] + }, + "enable_powershell_events_subscriber": { + "type": [ + "boolean", + "null" + ] + }, + "enable_process_etw_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_syslog": { + "type": [ + "boolean", + "null" + ] + }, + "enable_tables": { + "type": [ + "string", + "null" + ] + }, + "enable_watchdog_debug": { + "type": [ + "boolean", + "null" + ] + }, + "enable_windows_events_publisher": { + "type": [ + "boolean", + "null" + ] + }, + "enable_windows_events_subscriber": { + "type": [ + "boolean", + "null" + ] + }, + "enroll_always": { + "type": [ + "boolean", + "null" + ] + }, + "enroll_secret_env": { + "type": [ + "string", + "null" + ] + }, + "enroll_secret_path": { + "type": [ + "string", + "null" + ] + }, + "enroll_tls_endpoint": { + "type": [ + "string", + "null" + ] + }, + "ephemeral": { + "type": [ + "boolean", + "null" + ] + }, + "es_fim_enable_open_events": { + "type": [ + "boolean", + "null" + ] + }, + "es_fim_mute_path_literal": { + "type": [ + "string", + "null" + ] + }, + "es_fim_mute_path_prefix": { + "type": [ + "string", + "null" + ] + }, + "etw_kernel_trace_buffer_size": { + "type": [ + "integer", + "null" + ] + }, + "etw_kernel_trace_flush_timer": { + "type": [ + "integer", + "null" + ] + }, + "etw_kernel_trace_maximum_buffers": { + "type": [ + "integer", + "null" + ] + }, + "etw_kernel_trace_minimum_buffers": { + "type": [ + "integer", + "null" + ] + }, + "etw_userspace_trace_buffer_size": { + "type": [ + "integer", + "null" + ] + }, + "etw_userspace_trace_flush_timer": { + "type": [ + "integer", + "null" + ] + }, + "etw_userspace_trace_maximum_buffers": { + "type": [ + "integer", + "null" + ] + }, + "etw_userspace_trace_minimum_buffers": { + "type": [ + "integer", + "null" + ] + }, + "events_expiry": { + "type": [ + "integer", + "null" + ] + }, + "events_max": { + "type": [ + "integer", + "null" + ] + }, + "events_optimize": { + "type": [ + "boolean", + "null" + ] + }, + "events_streaming_plugin": { + "type": [ + "string", + "null" + ] + }, + "experiment_list": { + "type": [ + "string", + "null" + ] + }, + "experiments_linuxevents_circular_buffer_size": { + "type": [ + "integer", + "null" + ] + }, + "experiments_linuxevents_perf_output_size": { + "type": [ + "integer", + "null" + ] + }, + "extensions_autoload": { + "type": [ + "string", + "null" + ] + }, + "extensions_default_index": { + "type": [ + "boolean", + "null" + ] + }, + "extensions_interval": { + "type": [ + "string", + "null" + ] + }, + "extensions_require": { + "type": [ + "string", + "null" + ] + }, + "extensions_socket": { + "type": [ + "string", + "null" + ] + }, + "extensions_timeout": { + "type": [ + "string", + "null" + ] + }, + "force": { + "type": [ + "boolean", + "null" + ] + }, + "groups_service_delay": { + "type": [ + "integer", + "null" + ] + }, + "groups_service_interval": { + "type": [ + "integer", + "null" + ] + }, + "hardware_disabled_types": { + "type": [ + "string", + "null" + ] + }, + "hash_cache_max": { + "type": [ + "integer", + "null" + ] + }, + "host_identifier": { + "type": [ + "string", + "null" + ] + }, + "ignore_registry_exceptions": { + "type": [ + "boolean", + "null" + ] + }, + "ignore_table_exceptions": { + "type": [ + "boolean", + "null" + ] + }, + "install": { + "type": [ + "boolean", + "null" + ] + }, + "keep_container_worker_open": { + "type": [ + "boolean", + "null" + ] + }, + "keychain_access_cache": { + "type": [ + "boolean", + "null" + ] + }, + "keychain_access_interval": { + "type": [ + "integer", + "null" + ] + }, + "log_dir": { + "type": [ + "string", + "null" + ] + }, + "logbufsecs": { + "type": [ + "integer", + "null" + ] + }, + "logger_event_type": { + "type": [ + "boolean", + "null" + ] + }, + "logger_kafka_acks": { + "type": [ + "string", + "null" + ] + }, + "logger_kafka_brokers": { + "type": [ + "string", + "null" + ] + }, + "logger_kafka_compression": { + "type": [ + "string", + "null" + ] + }, + "logger_kafka_topic": { + "type": [ + "string", + "null" + ] + }, + "logger_min_status": { + "type": [ + "integer", + "null" + ] + }, + "logger_min_stderr": { + "type": [ + "integer", + "null" + ] + }, + "logger_mode": { + "type": [ + "string", + "null" + ] + }, + "logger_numerics": { + "type": [ + "boolean", + "null" + ] + }, + "logger_path": { + "type": [ + "string", + "null" + ] + }, + "logger_plugin": { + "type": [ + "string", + "null" + ] + }, + "logger_rotate": { + "type": [ + "boolean", + "null" + ] + }, + "logger_rotate_max_files": { + "type": [ + "integer", + "null" + ] + }, + "logger_rotate_size": { + "type": [ + "integer", + "null" + ] + }, + "logger_snapshot_event_type": { + "type": [ + "boolean", + "null" + ] + }, + "logger_stderr": { + "type": [ + "boolean", + "null" + ] + }, + "logger_syslog_facility": { + "type": [ + "integer", + "null" + ] + }, + "logger_syslog_prepend_cee": { + "type": [ + "boolean", + "null" + ] + }, + "logger_tls_backoff_max": { + "type": [ + "integer", + "null" + ] + }, + "logger_tls_compress": { + "type": [ + "boolean", + "null" + ] + }, + "logger_tls_endpoint": { + "type": [ + "string", + "null" + ] + }, + "logger_tls_max_lines": { + "type": [ + "integer", + "null" + ] + }, + "logger_tls_max_linesize": { + "type": [ + "integer", + "null" + ] + }, + "logger_tls_period": { + "type": [ + "integer", + "null" + ] + }, + "logtostderr": { + "type": [ + "boolean", + "null" + ] + }, + "lxd_socket": { + "type": [ + "string", + "null" + ] + }, + "malloc_trim_threshold": { + "type": [ + "integer", + "null" + ] + }, + "max_log_size": { + "type": [ + "integer", + "null" + ] + }, + "minloglevel": { + "type": [ + "integer", + "null" + ] + }, + "ntfs_event_publisher_debug": { + "type": [ + "boolean", + "null" + ] + }, + "nullvalue": { + "type": [ + "string", + "null" + ] + }, + "numeric_monitoring_filesystem_path": { + "type": [ + "string", + "null" + ] + }, + "numeric_monitoring_plugins": { + "type": [ + "string", + "null" + ] + }, + "numeric_monitoring_pre_aggregation_time": { + "type": [ + "integer", + "null" + ] + }, + "pack_delimiter": { + "type": [ + "string", + "null" + ] + }, + "pack_refresh_interval": { + "type": [ + "integer", + "null" + ] + }, + "pidfile": { + "type": [ + "string", + "null" + ] + }, + "proxy_hostname": { + "type": [ + "string", + "null" + ] + }, + "read_max": { + "type": [ + "integer", + "null" + ] + }, + "schedule_default_interval": { + "type": [ + "integer", + "null" + ] + }, + "schedule_epoch": { + "type": [ + "integer", + "null" + ] + }, + "schedule_lognames": { + "type": [ + "boolean", + "null" + ] + }, + "schedule_max_drift": { + "type": [ + "integer", + "null" + ] + }, + "schedule_reload": { + "type": [ + "integer", + "null" + ] + }, + "schedule_splay_percent": { + "type": [ + "integer", + "null" + ] + }, + "schedule_timeout": { + "type": [ + "integer", + "null" + ] + }, + "specified_identifier": { + "type": [ + "string", + "null" + ] + }, + "stderrthreshold": { + "type": [ + "integer", + "null" + ] + }, + "stop_logging_if_full_disk": { + "type": [ + "boolean", + "null" + ] + }, + "syslog_events_expiry": { + "type": [ + "integer", + "null" + ] + }, + "syslog_events_max": { + "type": [ + "integer", + "null" + ] + }, + "syslog_pipe_path": { + "type": [ + "string", + "null" + ] + }, + "syslog_rate_limit": { + "type": [ + "integer", + "null" + ] + }, + "table_delay": { + "type": [ + "integer", + "null" + ] + }, + "thrift_string_size_limit": { + "type": [ + "integer", + "null" + ] + }, + "thrift_timeout": { + "type": [ + "integer", + "null" + ] + }, + "thrift_verbose": { + "type": [ + "boolean", + "null" + ] + }, + "tls_accept_gzip": { + "type": [ + "boolean", + "null" + ] + }, + "tls_client_cert": { + "type": [ + "string", + "null" + ] + }, + "tls_client_key": { + "type": [ + "string", + "null" + ] + }, + "tls_disable_status_log": { + "type": [ + "boolean", + "null" + ] + }, + "tls_dump": { + "type": [ + "boolean", + "null" + ] + }, + "tls_enroll_max_attempts": { + "type": [ + "integer", + "null" + ] + }, + "tls_enroll_max_interval": { + "type": [ + "integer", + "null" + ] + }, + "tls_hostname": { + "type": [ + "string", + "null" + ] + }, + "tls_server_certs": { + "type": [ + "string", + "null" + ] + }, + "tls_session_reuse": { + "type": [ + "boolean", + "null" + ] + }, + "tls_session_timeout": { + "type": [ + "integer", + "null" + ] + }, + "uninstall": { + "type": [ + "boolean", + "null" + ] + }, + "users_service_delay": { + "type": [ + "integer", + "null" + ] + }, + "users_service_interval": { + "type": [ + "integer", + "null" + ] + }, + "usn_journal_reader_debug": { + "type": [ + "boolean", + "null" + ] + }, + "verbose": { + "type": [ + "boolean", + "null" + ] + }, + "vmodule": { + "type": [ + "string", + "null" + ] + }, + "watchdog_delay": { + "type": [ + "integer", + "null" + ] + }, + "watchdog_forced_shutdown_delay": { + "type": [ + "integer", + "null" + ] + }, + "watchdog_latency_limit": { + "type": [ + "integer", + "null" + ] + }, + "watchdog_level": { + "type": [ + "integer", + "null" + ] + }, + "watchdog_memory_limit": { + "type": [ + "integer", + "null" + ] + }, + "watchdog_utilization_limit": { + "type": [ + "integer", + "null" + ] + }, + "windows_event_channels": { + "type": [ + "string", + "null" + ] + }, + "yara_delay": { + "type": [ + "integer", + "null" + ] + }, + "yara_sigurl_authenticate": { + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "config": { + "description": "type: `object`", + "properties": { + "options": { + "additionalProperties": false, + "description": "type: `object`", + "properties": { + "allow_unsafe": { + "type": [ + "boolean", + "null" + ] + }, + "alsologtostderr": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_accept_socket_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_apparmor_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_config": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_failed_socket_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_fim_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_fork_process_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_kill_process_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_null_accept_socket_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_process_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_seccomp_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_selinux_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_sockets": { + "type": [ + "boolean", + "null" + ] + }, + "audit_allow_user_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_backlog_limit": { + "type": [ + "integer", + "null" + ] + }, + "audit_backlog_wait_time": { + "type": [ + "integer", + "null" + ] + }, + "audit_debug": { + "type": [ + "boolean", + "null" + ] + }, + "audit_fim_debug": { + "type": [ + "boolean", + "null" + ] + }, + "audit_fim_show_accesses": { + "type": [ + "boolean", + "null" + ] + }, + "audit_force_reconfigure": { + "type": [ + "boolean", + "null" + ] + }, + "audit_force_unconfigure": { + "type": [ + "boolean", + "null" + ] + }, + "audit_persist": { + "type": [ + "boolean", + "null" + ] + }, + "audit_show_partial_fim_events": { + "type": [ + "boolean", + "null" + ] + }, + "audit_show_untracked_res_warnings": { + "type": [ + "boolean", + "null" + ] + }, + "augeas_lenses": { + "type": [ + "string", + "null" + ] + }, + "aws_access_key_id": { + "type": [ + "string", + "null" + ] + }, + "aws_debug": { + "type": [ + "boolean", + "null" + ] + }, + "aws_disable_imdsv1_fallback": { + "type": [ + "boolean", + "null" + ] + }, + "aws_enable_proxy": { + "type": [ + "boolean", + "null" + ] + }, + "aws_firehose_endpoint": { + "type": [ + "string", + "null" + ] + }, + "aws_firehose_period": { + "type": [ + "integer", + "null" + ] + }, + "aws_firehose_region": { + "type": [ + "string", + "null" + ] + }, + "aws_firehose_stream": { + "type": [ + "string", + "null" + ] + }, + "aws_imdsv2_request_attempts": { + "type": [ + "integer", + "null" + ] + }, + "aws_imdsv2_request_interval": { + "type": [ + "integer", + "null" + ] + }, + "aws_kinesis_disable_log_status": { + "type": [ + "boolean", + "null" + ] + }, + "aws_kinesis_endpoint": { + "type": [ + "string", + "null" + ] + }, + "aws_kinesis_period": { + "type": [ + "integer", + "null" + ] + }, + "aws_kinesis_random_partition_key": { + "type": [ + "boolean", + "null" + ] + }, + "aws_kinesis_region": { + "type": [ + "string", + "null" + ] + }, + "aws_kinesis_stream": { + "type": [ + "string", + "null" + ] + }, + "aws_profile_name": { + "type": [ + "string", + "null" + ] + }, + "aws_proxy_host": { + "type": [ + "string", + "null" + ] + }, + "aws_proxy_password": { + "type": [ + "string", + "null" + ] + }, + "aws_proxy_port": { + "type": [ + "integer", + "null" + ] + }, + "aws_proxy_scheme": { + "type": [ + "string", + "null" + ] + }, + "aws_proxy_username": { + "type": [ + "string", + "null" + ] + }, + "aws_region": { + "type": [ + "string", + "null" + ] + }, + "aws_secret_access_key": { + "type": [ + "string", + "null" + ] + }, + "aws_session_token": { + "type": [ + "string", + "null" + ] + }, + "aws_sts_arn_role": { + "type": [ + "string", + "null" + ] + }, + "aws_sts_region": { + "type": [ + "string", + "null" + ] + }, + "aws_sts_session_name": { + "type": [ + "string", + "null" + ] + }, + "aws_sts_timeout": { + "type": [ + "integer", + "null" + ] + }, + "bpf_buffer_storage_size": { + "type": [ + "integer", + "null" + ] + }, + "bpf_perf_event_array_exp": { + "type": [ + "integer", + "null" + ] + }, + "buffered_log_max": { + "type": [ + "integer", + "null" + ] + }, + "decorations_top_level": { + "type": [ + "boolean", + "null" + ] + }, + "disable_audit": { + "type": [ + "boolean", + "null" + ] + }, + "disable_caching": { + "type": [ + "boolean", + "null" + ] + }, + "disable_database": { + "type": [ + "boolean", + "null" + ] + }, + "disable_decorators": { + "type": [ + "boolean", + "null" + ] + }, + "disable_distributed": { + "type": [ + "boolean", + "null" + ] + }, + "disable_endpointsecurity": { + "type": [ + "boolean", + "null" + ] + }, + "disable_endpointsecurity_fim": { + "type": [ + "boolean", + "null" + ] + }, + "disable_events": { + "type": [ + "boolean", + "null" + ] + }, + "disable_hash_cache": { + "type": [ + "boolean", + "null" + ] + }, + "disable_logging": { + "type": [ + "boolean", + "null" + ] + }, + "disable_memory": { + "type": [ + "boolean", + "null" + ] + }, + "distributed_denylist_duration": { + "type": [ + "integer", + "null" + ] + }, + "distributed_interval": { + "type": [ + "integer", + "null" + ] + }, + "distributed_loginfo": { + "type": [ + "boolean", + "null" + ] + }, + "distributed_plugin": { + "type": [ + "string", + "null" + ] + }, + "distributed_tls_max_attempts": { + "type": [ + "integer", + "null" + ] + }, + "distributed_tls_read_endpoint": { + "type": [ + "string", + "null" + ] + }, + "distributed_tls_write_endpoint": { + "type": [ + "string", + "null" + ] + }, + "dns_resolver_refresh_interval": { + "type": [ + "integer", + "null" + ] + }, + "docker_socket": { + "type": [ + "string", + "null" + ] + }, + "enable_bpf_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_dns_lookup_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_file_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_foreign": { + "type": [ + "boolean", + "null" + ] + }, + "enable_keyboard_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_mouse_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_ntfs_event_publisher": { + "type": [ + "boolean", + "null" + ] + }, + "enable_numeric_monitoring": { + "type": [ + "boolean", + "null" + ] + }, + "enable_powershell_events_subscriber": { + "type": [ + "boolean", + "null" + ] + }, + "enable_process_etw_events": { + "type": [ + "boolean", + "null" + ] + }, + "enable_syslog": { + "type": [ + "boolean", + "null" + ] + }, + "enable_windows_events_publisher": { + "type": [ + "boolean", + "null" + ] + }, + "enable_windows_events_subscriber": { + "type": [ + "boolean", + "null" + ] + }, + "ephemeral": { + "type": [ + "boolean", + "null" + ] + }, + "es_fim_enable_open_events": { + "type": [ + "boolean", + "null" + ] + }, + "es_fim_mute_path_literal": { + "type": [ + "string", + "null" + ] + }, + "es_fim_mute_path_prefix": { + "type": [ + "string", + "null" + ] + }, + "etw_kernel_trace_buffer_size": { + "type": [ + "integer", + "null" + ] + }, + "etw_kernel_trace_flush_timer": { + "type": [ + "integer", + "null" + ] + }, + "etw_kernel_trace_maximum_buffers": { + "type": [ + "integer", + "null" + ] + }, + "etw_kernel_trace_minimum_buffers": { + "type": [ + "integer", + "null" + ] + }, + "etw_userspace_trace_buffer_size": { + "type": [ + "integer", + "null" + ] + }, + "etw_userspace_trace_flush_timer": { + "type": [ + "integer", + "null" + ] + }, + "etw_userspace_trace_maximum_buffers": { + "type": [ + "integer", + "null" + ] + }, + "etw_userspace_trace_minimum_buffers": { + "type": [ + "integer", + "null" + ] + }, + "events_expiry": { + "type": [ + "integer", + "null" + ] + }, + "events_max": { + "type": [ + "integer", + "null" + ] + }, + "events_optimize": { + "type": [ + "boolean", + "null" + ] + }, + "events_streaming_plugin": { + "type": [ + "string", + "null" + ] + }, + "experiment_list": { + "type": [ + "string", + "null" + ] + }, + "experiments_linuxevents_circular_buffer_size": { + "type": [ + "integer", + "null" + ] + }, + "experiments_linuxevents_perf_output_size": { + "type": [ + "integer", + "null" + ] + }, + "extensions_default_index": { + "type": [ + "boolean", + "null" + ] + }, + "groups_service_delay": { + "type": [ + "integer", + "null" + ] + }, + "groups_service_interval": { + "type": [ + "integer", + "null" + ] + }, + "hardware_disabled_types": { + "type": [ + "string", + "null" + ] + }, + "hash_cache_max": { + "type": [ + "integer", + "null" + ] + }, + "host_identifier": { + "type": [ + "string", + "null" + ] + }, + "ignore_registry_exceptions": { + "type": [ + "boolean", + "null" + ] + }, + "ignore_table_exceptions": { + "type": [ + "boolean", + "null" + ] + }, + "keep_container_worker_open": { + "type": [ + "boolean", + "null" + ] + }, + "keychain_access_cache": { + "type": [ + "boolean", + "null" + ] + }, + "keychain_access_interval": { + "type": [ + "integer", + "null" + ] + }, + "log_dir": { + "type": [ + "string", + "null" + ] + }, + "logbufsecs": { + "type": [ + "integer", + "null" + ] + }, + "logger_event_type": { + "type": [ + "boolean", + "null" + ] + }, + "logger_kafka_acks": { + "type": [ + "string", + "null" + ] + }, + "logger_kafka_brokers": { + "type": [ + "string", + "null" + ] + }, + "logger_kafka_compression": { + "type": [ + "string", + "null" + ] + }, + "logger_kafka_topic": { + "type": [ + "string", + "null" + ] + }, + "logger_min_status": { + "type": [ + "integer", + "null" + ] + }, + "logger_min_stderr": { + "type": [ + "integer", + "null" + ] + }, + "logger_numerics": { + "type": [ + "boolean", + "null" + ] + }, + "logger_path": { + "type": [ + "string", + "null" + ] + }, + "logger_rotate": { + "type": [ + "boolean", + "null" + ] + }, + "logger_rotate_max_files": { + "type": [ + "integer", + "null" + ] + }, + "logger_rotate_size": { + "type": [ + "integer", + "null" + ] + }, + "logger_snapshot_event_type": { + "type": [ + "boolean", + "null" + ] + }, + "logger_syslog_facility": { + "type": [ + "integer", + "null" + ] + }, + "logger_syslog_prepend_cee": { + "type": [ + "boolean", + "null" + ] + }, + "logger_tls_backoff_max": { + "type": [ + "integer", + "null" + ] + }, + "logger_tls_compress": { + "type": [ + "boolean", + "null" + ] + }, + "logger_tls_endpoint": { + "type": [ + "string", + "null" + ] + }, + "logger_tls_max_lines": { + "type": [ + "integer", + "null" + ] + }, + "logger_tls_max_linesize": { + "type": [ + "integer", + "null" + ] + }, + "logger_tls_period": { + "type": [ + "integer", + "null" + ] + }, + "lxd_socket": { + "type": [ + "string", + "null" + ] + }, + "malloc_trim_threshold": { + "type": [ + "integer", + "null" + ] + }, + "max_log_size": { + "type": [ + "integer", + "null" + ] + }, + "minloglevel": { + "type": [ + "integer", + "null" + ] + }, + "ntfs_event_publisher_debug": { + "type": [ + "boolean", + "null" + ] + }, + "nullvalue": { + "type": [ + "string", + "null" + ] + }, + "numeric_monitoring_filesystem_path": { + "type": [ + "string", + "null" + ] + }, + "numeric_monitoring_plugins": { + "type": [ + "string", + "null" + ] + }, + "numeric_monitoring_pre_aggregation_time": { + "type": [ + "integer", + "null" + ] + }, + "pack_delimiter": { + "type": [ + "string", + "null" + ] + }, + "pack_refresh_interval": { + "type": [ + "integer", + "null" + ] + }, + "read_max": { + "type": [ + "integer", + "null" + ] + }, + "schedule_default_interval": { + "type": [ + "integer", + "null" + ] + }, + "schedule_epoch": { + "type": [ + "integer", + "null" + ] + }, + "schedule_lognames": { + "type": [ + "boolean", + "null" + ] + }, + "schedule_max_drift": { + "type": [ + "integer", + "null" + ] + }, + "schedule_reload": { + "type": [ + "integer", + "null" + ] + }, + "schedule_splay_percent": { + "type": [ + "integer", + "null" + ] + }, + "schedule_timeout": { + "type": [ + "integer", + "null" + ] + }, + "specified_identifier": { + "type": [ + "string", + "null" + ] + }, + "stop_logging_if_full_disk": { + "type": [ + "boolean", + "null" + ] + }, + "syslog_events_expiry": { + "type": [ + "integer", + "null" + ] + }, + "syslog_events_max": { + "type": [ + "integer", + "null" + ] + }, + "syslog_pipe_path": { + "type": [ + "string", + "null" + ] + }, + "syslog_rate_limit": { + "type": [ + "integer", + "null" + ] + }, + "table_delay": { + "type": [ + "integer", + "null" + ] + }, + "thrift_string_size_limit": { + "type": [ + "integer", + "null" + ] + }, + "thrift_timeout": { + "type": [ + "integer", + "null" + ] + }, + "thrift_verbose": { + "type": [ + "boolean", + "null" + ] + }, + "tls_disable_status_log": { + "type": [ + "boolean", + "null" + ] + }, + "tls_dump": { + "type": [ + "boolean", + "null" + ] + }, + "users_service_delay": { + "type": [ + "integer", + "null" + ] + }, + "users_service_interval": { + "type": [ + "integer", + "null" + ] + }, + "usn_journal_reader_debug": { + "type": [ + "boolean", + "null" + ] + }, + "verbose": { + "type": [ + "boolean", + "null" + ] + }, + "vmodule": { + "type": [ + "string", + "null" + ] + }, + "windows_event_channels": { + "type": [ + "string", + "null" + ] + }, + "yara_delay": { + "type": [ + "integer", + "null" + ] + }, + "yara_sigurl_authenticate": { + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "extensions": { + "description": "Extensions are the orbit managed extensions\n\ntype: `object`", + "type": [ + "object", + "null" + ] + }, + "orbit": { + "$ref": "#/$defs/OrbitAgentOptions", + "description": "Orbit-agent options. Kept separate from osquery so they bypass the\nosquery schema validator.\n\ntype: `OrbitAgentOptions`" + }, + "overrides": { + "$ref": "#/$defs/AgentOptionsOverrides", + "description": "Overrides includes any platform-based overrides.\n\ntype: `AgentOptionsOverrides`" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "script_execution_timeout": { + "description": "ScriptExecutionTimeout is the maximum time in seconds that a script can run.\n\ntype: `integer`" + }, + "update_channels": { + "description": "UpdateChannels holds the configured channels for fleetd components.\n\ntype: `object`", + "type": [ + "object", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "AgentOptionsOverrides": { + "additionalProperties": false, + "properties": { + "platforms": { + "additionalProperties": { + "type": [ + "object", + "null" + ] + }, + "description": "Platforms is a map from platform name to the config override.\n\ntype: `object`", + "type": [ + "object", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "AndroidSettings": { + "additionalProperties": false, + "properties": { + "certificates": { + "description": "type: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "configuration_profiles": { + "description": "NOTE: These are only present here for informational purposes.\n(The source of truth for profiles is in MySQL.)\n\ntype: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "custom_settings": { + "deprecated": true, + "deprecationMessage": "'custom_settings' is deprecated, use 'configuration_profiles' instead", + "description": "NOTE: These are only present here for informational purposes.\n(The source of truth for profiles is in MySQL.)\n\ntype: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "AppleAccountProvisioning": { + "additionalProperties": false, + "description": "AppleAccountProvisioning is the macOS local account provisioning / Platform SSO password sync configuration stored on AppConfig.MDM.", + "properties": { + "oauth_idp_client_id": { + "description": "OAuthIdPClientID is the client/application ID registered with the upstream IdP.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "oauth_idp_client_secret": { + "description": "OAuthIdPClientSecret is the client secret registered with the upstream IdP.\nStored in mdm_config_assets, not here; this field carries the masked value\nin API responses and the caller-supplied value on writes.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "oauth_idp_token_url": { + "description": "OAuthIdPTokenURL is the upstream OIDC token endpoint used for the ROPG\n(grant_type=password) flow at sign-in.\nOkta example: https://dev-12345.okta.com/oauth2/default/v1/token\nEntra example: https://login.microsoftonline.com/\u003ctenant\u003e/oauth2/v2.0/token\n\ntype: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "AppleOSUpdateSettings": { + "additionalProperties": false, + "description": "AppleOSUpdateSettings is the common type that contains the settings for OS updates on Apple devices.", + "properties": { + "deadline": { + "description": "Deadline the required installation date for Nudge to enforce the required\noperating system version.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "minimum_version": { + "description": "MinimumVersion is the required minimum operating system version.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "update_new_hosts": { + "description": "UpdateNewHosts if true, only enforce the latest macOS version for new hosts (during enrollment)\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "BaseItem": { + "additionalProperties": false, + "description": "BaseItem provides path/paths fields for types that can reference external files in GitOps YAML configurations.", + "properties": { + "path": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "paths": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "ConditionalAccessSettings": { + "additionalProperties": false, + "description": "ConditionalAccessSettings holds the global settings for the \"Conditional access\" feature.", + "properties": { + "bypass_disabled": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "microsoft_entra_connection_configured": { + "description": "MicrosoftEntraConnectionConfigured is true when the tenant has been configured\nfor \"Conditional access\" on Entra and Fleet.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "microsoft_entra_tenant_id": { + "description": "MicrosoftEntraTenantID is the Entra's tenant ID.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "okta_assertion_consumer_service_url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "okta_audience_uri": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "okta_certificate": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "okta_idp_id": { + "description": "Okta conditional access settings - using optjson for partial updates\nAll four fields must be set together or all must be empty.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "ControlsWithTypes": { + "additionalProperties": false, + "properties": { + "android_enabled_and_configured": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "android_settings": { + "$ref": "#/$defs/AndroidSettings", + "description": "type: `AndroidSettings`" + }, + "apple_account_provisioning": { + "$ref": "#/$defs/AppleAccountProvisioning", + "description": "type: `AppleAccountProvisioning`" + }, + "apple_require_hardware_attestation": true, + "apple_settings": { + "$ref": "#/$defs/MacOSSettings", + "description": "type: `MacOSSettings`" + }, + "enable_disk_encryption": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_recovery_lock_password": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_turn_on_windows_mdm_manually": true, + "ios_updates": { + "$ref": "#/$defs/AppleOSUpdateSettings", + "description": "type: `AppleOSUpdateSettings`" + }, + "ipados_updates": { + "$ref": "#/$defs/AppleOSUpdateSettings", + "description": "type: `AppleOSUpdateSettings`" + }, + "macos_migration": true, + "macos_settings": { + "$ref": "#/$defs/MacOSSettings", + "deprecated": true, + "deprecationMessage": "'macos_settings' is deprecated, use 'apple_settings' instead", + "description": "type: `MacOSSettings`" + }, + "macos_setup": { + "$ref": "#/$defs/MacOSSetup", + "deprecated": true, + "deprecationMessage": "'macos_setup' is deprecated, use 'setup_experience' instead", + "description": "type: `MacOSSetup`" + }, + "macos_updates": { + "$ref": "#/$defs/AppleOSUpdateSettings", + "description": "type: `AppleOSUpdateSettings`" + }, + "name_template": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "scripts": { + "description": "type: `array\u003cBaseItem\u003e`", + "items": { + "$ref": "#/$defs/BaseItem" + }, + "type": [ + "array", + "null" + ] + }, + "setup_experience": { + "$ref": "#/$defs/MacOSSetup", + "description": "type: `MacOSSetup`" + }, + "windows_enabled_and_configured": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "windows_entra_client_ids": true, + "windows_entra_tenant_ids": true, + "windows_migration_enabled": true, + "windows_require_bitlocker_pin": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "windows_settings": { + "$ref": "#/$defs/WindowsSettings", + "description": "type: `WindowsSettings`" + }, + "windows_updates": { + "$ref": "#/$defs/WindowsUpdates", + "description": "type: `WindowsUpdates`" + } + }, + "type": [ + "object", + "null" + ] + }, + "FailingPoliciesWebhookSettings": { + "additionalProperties": false, + "description": "FailingPoliciesWebhookSettings holds the settings for failing policy webhooks.", + "properties": { + "destination_url": { + "description": "DestinationURL is the webhook's URL.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "enable_failing_policies_webhook": { + "description": "Enable indicates whether the webhook for failing policies is enabled.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "host_batch_size": { + "description": "HostBatchSize allows sending multiple requests in batches of hosts for each policy.\nA value of 0 means no batching.\n\ntype: `integer`" + }, + "policy_ids": { + "description": "PolicyIDs is a list of policy IDs for which the webhook will be configured.\n\ntype: `array\u003cinteger\u003e`", + "items": {}, + "type": [ + "array", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "Features": { + "additionalProperties": false, + "properties": { + "additional_queries": { + "description": "type: `object`", + "type": [ + "object", + "null" + ] + }, + "detail_query_overrides": { + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "type: `object`", + "type": [ + "object", + "null" + ] + }, + "enable_host_users": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_software_inventory": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "historical_data": { + "$ref": "#/$defs/HistoricalDataSettings", + "description": "type: `HistoricalDataSettings`" + }, + "vulnerability_exposure_historical_reporting": { + "$ref": "#/$defs/VulnExposureFilterSettings", + "description": "VulnerabilityExposureHistoricalReporting holds the GitOps-managed default\nfilter state for the Vulnerability exposure dashboard chart. It is a\ndisplay-only concern: it seeds the chart's filter controls on load and\ndoes NOT affect what vulnerability data is collected. Premium-only.\n\nAll fields are pointers so the config has sparse/PATCH semantics: a field\npresent in YAML is persisted and respected by the frontend, while an\nomitted field stays nil and the frontend falls back to its own built-in\ndefault for that control.\n\ntype: `VulnExposureFilterSettings`" + } + }, + "type": [ + "object", + "null" + ] + }, + "FleetDesktopSettings": { + "additionalProperties": false, + "description": "FleetDesktopSettings contains settings used to configure Fleet Desktop.", + "properties": { + "alternative_browser_host": { + "description": "AlternativeBrowserHost if set, Fleet Desktop will use this to open any links;\nthis is used in scenarios where we want Fleet Desktop traffic to use a custom proxy, for security reasons.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "transparency_url": { + "description": "TransparencyURL is the URL used for the “About Fleet” link in the Fleet Desktop menu.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "GitOpsConfig": { + "additionalProperties": false, + "properties": { + "exceptions": { + "$ref": "#/$defs/GitOpsExceptions", + "description": "type: `GitOpsExceptions`" + }, + "gitops_mode_enabled": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "repository_url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "GitOpsCustomHostVital": { + "additionalProperties": false, + "description": "GitOpsCustomHostVital defines the valid keys for an item in the top-level `custom_host_vitals:` list.", + "properties": { + "name": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "GitOpsExceptions": { + "additionalProperties": false, + "properties": { + "labels": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "secrets": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "software": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "GitOpsFleetSettings": { + "additionalProperties": false, + "description": "GitOpsFleetSettings defines the valid keys for the top-level `settings:` section (fleet-level).", + "properties": { + "agent_options": { + "description": "AgentOptions is the options for osquery and Orbit.\n\ntype: `object`", + "type": [ + "object", + "null" + ] + }, + "features": { + "$ref": "#/$defs/Features", + "description": "the below aren't serialized as-is into config JSON column in the teams table\n\ntype: `Features`" + }, + "host_expiry_settings": { + "$ref": "#/$defs/HostExpirySettings", + "description": "type: `HostExpirySettings`\n\nGitOps: kept unchanged when omitted or null; cleared when set to an empty value." + }, + "integrations": { + "$ref": "#/$defs/TeamIntegrations", + "description": "type: `TeamIntegrations`" + }, + "mdm": { + "$ref": "#/$defs/TeamMDM", + "description": "type: `TeamMDM`" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "scripts": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "secrets": true, + "software": { + "$ref": "#/$defs/SoftwareSpec", + "description": "type: `SoftwareSpec`" + }, + "webhook_settings": { + "$ref": "#/$defs/TeamWebhookSettings", + "description": "type: `TeamWebhookSettings`" + } + }, + "type": [ + "object", + "null" + ] + }, + "GitOpsOrgSettings": { + "additionalProperties": false, + "description": "GitOpsOrgSettings defines the valid keys for the top-level `org_settings:` section.", + "properties": { + "activity_expiry_settings": { + "$ref": "#/$defs/ActivityExpirySettings", + "description": "type: `ActivityExpirySettings`" + }, + "agent_options": { + "description": "AgentOptions holds osquery configuration.\n\nThis field is a pointer to avoid returning this information to non-global-admins.\n\ntype: `object`", + "type": [ + "object", + "null" + ] + }, + "certificate_authorities": true, + "conditional_access": { + "$ref": "#/$defs/ConditionalAccessSettings", + "description": "ConditionalAccess holds the Okta conditional access settings that are stored in AppConfig.\nNote: In API responses, this is combined with Microsoft Entra settings from the database.\n\ntype: `ConditionalAccessSettings`" + }, + "features": { + "$ref": "#/$defs/Features", + "description": "Features allows to globally enable or disable features\n\ntype: `Features`" + }, + "fleet_desktop": { + "$ref": "#/$defs/FleetDesktopSettings", + "description": "FleetDesktop holds settings for Fleet Desktop that can be changed via the API.\n\ntype: `FleetDesktopSettings`" + }, + "gitops": { + "$ref": "#/$defs/GitOpsConfig", + "description": "type: `GitOpsConfig`\n\nGitOps: kept unchanged when omitted, null, or empty." + }, + "host_expiry_settings": { + "$ref": "#/$defs/HostExpirySettings", + "description": "type: `HostExpirySettings`\n\nGitOps: kept unchanged when omitted or null; cleared when set to an empty value." + }, + "host_settings": { + "$ref": "#/$defs/Features", + "description": "type: `Features`" + }, + "integrations": { + "$ref": "#/$defs/Integrations", + "description": "type: `Integrations`" + }, + "mdm": { + "$ref": "#/$defs/MDM", + "description": "type: `MDM`" + }, + "org_info": { + "$ref": "#/$defs/OrgInfo", + "description": "type: `OrgInfo`" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "scripts": { + "description": "Scripts is a slice of script file paths.\n\nNOTE: These are only present here for informational purposes.\n(The source of truth for scripts is in MySQL.)\n\ntype: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "secrets": true, + "server_settings": { + "$ref": "#/$defs/ServerSettings", + "description": "type: `ServerSettings`" + }, + "smtp_settings": { + "$ref": "#/$defs/SMTPSettings", + "description": "SMTPSettings holds the SMTP integration settings.\n\nThis field is a pointer to avoid returning this information to non-global-admins.\n\ntype: `SMTPSettings`" + }, + "smtp_test": { + "description": "SMTPTest is a flag that if set will cause the server to test email configuration\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "sso_settings": { + "$ref": "#/$defs/SSOSettings", + "description": "SSOSettings is single sign on integration settings.\n\nThis field is a pointer to avoid returning this information to non-global-admins.\n\ntype: `SSOSettings`" + }, + "vulnerability_settings": { + "$ref": "#/$defs/VulnerabilitySettings", + "description": "VulnerabilitySettings defines how fleet will behave while scanning for vulnerabilities in the host software\n\ntype: `VulnerabilitySettings`" + }, + "webhook_settings": { + "$ref": "#/$defs/WebhookSettings", + "description": "type: `WebhookSettings`" + }, + "yara_rules": { + "description": "type: `array\u003cYaraRule\u003e`", + "items": { + "$ref": "#/$defs/YaraRule" + }, + "type": [ + "array", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "GitOpsPolicySpec": { + "additionalProperties": false, + "anyOf": [ + { + "errorMessage": "A policy must set name (or reference a file with path/paths).", + "required": [ + "name" + ] + }, + { + "errorMessage": "A policy must set name (or reference a file with path/paths).", + "required": [ + "path" + ] + }, + { + "errorMessage": "A policy must set name (or reference a file with path/paths).", + "required": [ + "paths" + ] + } + ], + "properties": { + "calendar_events_enabled": { + "description": "CalendarEventsEnabled indicates whether calendar events are enabled for the policy.\n\nOnly applies to team policies.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "conditional_access_enabled": { + "description": "ConditionalAccessEnabled indicates whether this is a policy used for Microsoft conditional access.\n\nOnly applies to team policies.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "continuous_automations_enabled": { + "description": "ContinuousAutomationsEnabled indicates whether software/script automations\nshould run on every failing policy result, not just on pass→fail transitions.\n\nOnly applies to team policies.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "critical": { + "description": "Critical marks the policy as high impact.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "description": { + "description": "Description describes the policy.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "fleet": { + "description": "Team is the name of the team.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "fleet_maintained_app_slug": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "install_software": { + "anyOf": [ + { + "type": [ + "boolean", + "null" + ] + }, + { + "type": [ + "object", + "null" + ] + } + ], + "description": "type: `boolean or object`" + }, + "labels_exclude_all": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_exclude_any": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_all": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_any": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "name": { + "description": "Name is the name of the policy.\n\ntype: `string`", + "type": "string" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "paths": { + "type": [ + "string", + "null" + ] + }, + "platform": { + "description": "Platform is a comma-separated string to indicate the target platforms.\n\nEmpty string targets all platforms.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "query": { + "description": "Query is the policy's SQL query.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "resolution": { + "description": "Resolution describes how to solve a failing policy.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "run_script": { + "$ref": "#/$defs/PolicyRunScript", + "description": "type: `PolicyRunScript`" + }, + "script_id": { + "description": "ScriptID is the ID of the script associated with this policy (team policies only).\nWhen editing a policy, if this is nil or 0 then the script ID is unset from the policy.\n\ntype: `integer`" + }, + "software_title_id": { + "description": "SoftwareTitleID is the title ID of the installer associated with this policy (team policies only).\nWhen editing a policy, if this is nil or 0 then the title ID is unset from the policy.\n\ntype: `integer`" + }, + "team": { + "deprecated": true, + "deprecationMessage": "'team' is deprecated, use 'fleet' instead", + "description": "Team is the name of the team.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "type": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "webhooks_and_tickets_enabled": { + "description": "WebhooksAndTicketsEnabled indicates whether failing policy webhooks/tickets\nshould be enabled for this policy. This is a gitops-only convenience that\ntranslates to adding the policy's ID to the failing_policies_webhook.policy_ids list.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "GitOpsSoftware": { + "additionalProperties": false, + "properties": { + "app_store_apps": { + "description": "type: `array\u003cTeamSpecAppStoreApp\u003e`", + "items": { + "$ref": "#/$defs/TeamSpecAppStoreApp" + }, + "type": [ + "array", + "null" + ] + }, + "fleet_maintained_apps": { + "description": "type: `array\u003cMaintainedAppSpec\u003e`", + "items": { + "$ref": "#/$defs/MaintainedAppSpec" + }, + "type": [ + "array", + "null" + ] + }, + "packages": { + "description": "type: `array\u003cSoftwarePackageSpec\u003e`", + "items": { + "$ref": "#/$defs/SoftwarePackageSpec" + }, + "type": [ + "array", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "GoogleCalendarApiKey": { + "additionalProperties": false, + "description": "GoogleCalendarApiKey is a custom type for the Google Calendar API key JSON.", + "properties": { + "values": { + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Values contains the actual API key fields when not masked\n\ntype: `object`", + "type": [ + "object", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "GoogleCalendarIntegration": { + "additionalProperties": false, + "properties": { + "api_key_json": { + "$ref": "#/$defs/GoogleCalendarApiKey", + "description": "type: `GoogleCalendarApiKey`\n\nGitOps: kept unchanged when omitted, null, or empty." + }, + "domain": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "GoogleWorkspaceIntegration": { + "additionalProperties": false, + "description": "GoogleWorkspaceIntegration configures syncing IdP host vitals (users, groups, and departments) from Google Workspace via the Admin SDK Directory API, using a service account with domain-wide delegation.", + "properties": { + "api_key_json": { + "$ref": "#/$defs/GoogleCalendarApiKey", + "description": "ApiKey holds the service account JSON (client_email, private_key). It reuses\nthe GoogleCalendarApiKey masking type because the credential format and the\nmasking/preserve-on-update behavior are identical.\n\ntype: `GoogleCalendarApiKey`\n\nGitOps: kept unchanged when omitted, null, or empty." + }, + "domain": { + "description": "Domain is the Google Workspace primary domain whose directory is synced.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "impersonated_user_email": { + "description": "ImpersonatedUserEmail is the Google Workspace admin user that the service\naccount impersonates via domain-wide delegation. The Admin SDK Directory API\nonly accepts requests on behalf of a real admin user (the JWT Subject).\n\ntype: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "HistoricalDataSettings": { + "additionalProperties": false, + "description": "HistoricalDataSettings controls per-dataset collection of the time-series rollups that drive the dashboard charts.", + "properties": { + "uptime": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "vulnerabilities": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "HostExpirySettings": { + "additionalProperties": false, + "description": "HostExpirySettings contains settings pertaining to automatic host expiry.", + "properties": { + "host_expiry_enabled": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "host_expiry_window": { + "description": "type: `integer`" + } + }, + "type": [ + "object", + "null" + ] + }, + "HostStatusWebhookSettings": { + "additionalProperties": false, + "properties": { + "days_count": { + "description": "type: `integer`" + }, + "destination_url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "enable_host_status_webhook": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "host_percentage": { + "description": "type: `number`" + } + }, + "type": [ + "object", + "null" + ] + }, + "HostsSlice": { + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "Integrations": { + "additionalProperties": false, + "description": "Integrations configures the integrations with external systems.", + "properties": { + "conditional_access_enabled": { + "description": "ConditionalAccessEnabled indicates whether conditional access is enabled/disabled for \"No team\".\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "google_calendar": { + "description": "type: `array\u003cGoogleCalendarIntegration\u003e`", + "items": { + "$ref": "#/$defs/GoogleCalendarIntegration" + }, + "type": [ + "array", + "null" + ] + }, + "google_workspace": { + "description": "type: `array\u003cGoogleWorkspaceIntegration\u003e`", + "items": { + "$ref": "#/$defs/GoogleWorkspaceIntegration" + }, + "type": [ + "array", + "null" + ] + }, + "jira": { + "description": "type: `array\u003cJiraIntegration\u003e`", + "items": { + "$ref": "#/$defs/JiraIntegration" + }, + "type": [ + "array", + "null" + ] + }, + "zendesk": { + "description": "type: `array\u003cZendeskIntegration\u003e`", + "items": { + "$ref": "#/$defs/ZendeskIntegration" + }, + "type": [ + "array", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "JiraIntegration": { + "additionalProperties": false, + "description": "JiraIntegration configures an instance of an integration with the Jira system.", + "properties": { + "api_token": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "enable_failing_policies": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_software_vulnerabilities": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "project_key": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "username": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "LabelSpec": { + "additionalProperties": false, + "anyOf": [ + { + "errorMessage": "A label must set name (or reference a file with path/paths).", + "required": [ + "name" + ] + }, + { + "errorMessage": "A label must set name (or reference a file with path/paths).", + "required": [ + "path" + ] + }, + { + "errorMessage": "A label must set name (or reference a file with path/paths).", + "required": [ + "paths" + ] + } + ], + "properties": { + "criteria": { + "description": "type: `object`", + "type": [ + "object", + "null" + ] + }, + "description": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "fleet_id": { + "description": "type: `integer`" + }, + "hosts": { + "$ref": "#/$defs/HostsSlice", + "description": "type: `HostsSlice`\n\nGitOps: kept unchanged when omitted; cleared when set to null or empty." + }, + "id": { + "description": "type: `integer`" + }, + "label_membership_type": { + "description": "type: `integer`" + }, + "label_type": { + "description": "type: `integer`" + }, + "name": { + "description": "type: `string`", + "type": "string" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "paths": { + "type": [ + "string", + "null" + ] + }, + "platform": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "query": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "team_id": { + "deprecated": true, + "deprecationMessage": "'team_id' is deprecated, use 'fleet_id' instead", + "description": "type: `integer`" + } + }, + "type": [ + "object", + "null" + ] + }, + "MDM": { + "additionalProperties": false, + "description": "MDM is part of AppConfig and defines the mdm settings.", + "properties": { + "android_enabled_and_configured": { + "description": "AndroidEnabledAndConfigured is set to true if Fleet successfully bound to an Android Management Enterprise\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "android_settings": { + "$ref": "#/$defs/AndroidSettings", + "description": "type: `AndroidSettings`" + }, + "apple_account_provisioning": { + "$ref": "#/$defs/AppleAccountProvisioning", + "description": "AppleAccountProvisioning holds the macOS local account provisioning /\nPlatform SSO password sync configuration. The IdP client secret is stored\nin mdm_config_assets, not in this JSON; only the masked value is returned.\n\ntype: `AppleAccountProvisioning`" + }, + "apple_bm_default_team": { + "description": "Deprecated: use AppleBusinessManager instead\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "apple_bm_enabled_and_configured": { + "description": "AppleBMEnabledAndConfigured is set to true if Fleet has been\nconfigured with the required Apple BM key pair or token. It can't be set\nmanually via the PATCH /config API, it's only set automatically when\nthe server starts.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "apple_bm_terms_expired": { + "description": "AppleBMTermsExpired is set to true if an Apple Business request\nfailed due to Apple's terms and conditions having changed and need the\nuser to explicitly accept them. It cannot be set manually via the\nPATCH /config API, it is only set automatically, internally, by detecting\nthe 403 Forbidden error with body T_C_NOT_SIGNED returned by the Apple BM\nAPI.\n\nIt is set to true as soon as one of the ABM tokens receives this error\ncode, and is set to false only once all ABM tokens have agreed to the new\nterms.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "apple_business": { + "description": "AppleBusinessManager defines the associations between AB tokens\nand the fleets used to assign hosts when they're ingested from Apple\nBusiness.\n\ntype: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "apple_business_manager": { + "deprecated": true, + "deprecationMessage": "'apple_business_manager' is deprecated, use 'apple_business' instead", + "description": "AppleBusinessManager defines the associations between AB tokens\nand the fleets used to assign hosts when they're ingested from Apple\nBusiness.\n\ntype: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "apple_require_hardware_attestation": { + "description": "AppleRequireHardwareAttestation indicates whether to require Managed Device Attestation via ACME(including hardware bound keys) for\ncertain Apple MDM enrollments.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "apple_server_url": { + "description": "AppleServerURL is an alternate URL to be used in MDM configuration profiles to differentiate MDM\nrequests from fleetd requests on customer networks. AppleServerURL DNS should resolve to the\nsame IP as the Fleet Server URL.\nIf not set, the server will use Fleet server URL (recommended).\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "apple_settings": { + "$ref": "#/$defs/MacOSSettings", + "description": "type: `MacOSSettings`" + }, + "enable_disk_encryption": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_recovery_lock_password": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_turn_on_windows_mdm_manually": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enabled_and_configured": { + "description": "EnabledAndConfigured is set to true if Fleet has been\nconfigured with the required APNS and SCEP certificates. It can't be set\nmanually via the PATCH /config API, it's only set automatically when\nthe server starts.\n\nTODO: should ideally be renamed to AppleEnabledAndConfigured, but it\nimplies a lot of changes to existing code across both frontend and\nbackend, should be done only after careful analysis.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "end_user_authentication": { + "$ref": "#/$defs/MDMEndUserAuthentication", + "description": "type: `MDMEndUserAuthentication`" + }, + "end_user_license_agreement": true, + "ios_updates": { + "$ref": "#/$defs/AppleOSUpdateSettings", + "description": "IOSUpdates defines the OS update settings for iOS devices.\n\ntype: `AppleOSUpdateSettings`" + }, + "ipados_updates": { + "$ref": "#/$defs/AppleOSUpdateSettings", + "description": "IPadOSUpdates defines the OS update settings for iPadOS devices.\n\ntype: `AppleOSUpdateSettings`" + }, + "macos_migration": { + "$ref": "#/$defs/MacOSMigration", + "description": "type: `MacOSMigration`" + }, + "macos_settings": { + "$ref": "#/$defs/MacOSSettings", + "deprecated": true, + "deprecationMessage": "'macos_settings' is deprecated, use 'apple_settings' instead", + "description": "type: `MacOSSettings`" + }, + "macos_setup": { + "$ref": "#/$defs/MacOSSetup", + "deprecated": true, + "deprecationMessage": "'macos_setup' is deprecated, use 'setup_experience' instead", + "description": "type: `MacOSSetup`" + }, + "macos_updates": { + "$ref": "#/$defs/AppleOSUpdateSettings", + "description": "MacOSUpdates defines the OS update settings for macOS devices.\n\ntype: `AppleOSUpdateSettings`" + }, + "name_template": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "setup_experience": { + "$ref": "#/$defs/MacOSSetup", + "description": "type: `MacOSSetup`" + }, + "volume_purchasing_program": { + "description": "type: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "windows_enabled_and_configured": { + "description": "WindowsEnabledAndConfigured indicates if Fleet MDM is enabled for Windows.\nThere is no other configuration required for Windows other than enabling\nthe support, but it is still called \"EnabledAndConfigured\" for consistency\nwith the similarly named macOS-specific fields.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "windows_entra_client_ids": { + "description": "WindowsEntraClientIDs is the allowlist of Entra application client IDs (GUIDs) whose tokens are accepted for\nWindows automatic enrollment.\n\ntype: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "windows_entra_tenant_ids": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "windows_migration_enabled": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "windows_require_bitlocker_pin": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "windows_settings": { + "$ref": "#/$defs/WindowsSettings", + "description": "type: `WindowsSettings`" + }, + "windows_updates": { + "$ref": "#/$defs/WindowsUpdates", + "description": "WindowsUpdates defines the OS update settings for Windows devices.\n\ntype: `WindowsUpdates`" + } + }, + "type": [ + "object", + "null" + ] + }, + "MDMEndUserAuthentication": { + "additionalProperties": false, + "description": "MDMEndUserAuthentication contains settings related to end user authentication to gate certain MDM features (eg: enrollment)", + "properties": { + "entity_id": { + "description": "EntityID is a uri that identifies this service provider\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "idp_name": { + "description": "IDPName is a human friendly name for the IDP\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "issuer_uri": { + "description": "IssuerURI is the uri that identifies the identity provider\n\nDeprecated: Not used, only left here to not break the API\n(\"unsupported key provided\" error)\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "metadata": { + "description": "Metadata contains IDP metadata XML\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "metadata_url": { + "description": "MetadataURL is a URL provided by the IDP which can be used to download\nmetadata\n\ntype: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "MDMProfileSpec": { + "additionalProperties": false, + "description": "MDMProfileSpec represents the spec used to define configuration profiles via yaml files.", + "properties": { + "labels": { + "description": "Deprecated: the Labels field is now deprecated, it is superseded by\nLabelsIncludeAll, so any value set via this field will be transferred to\nLabelsIncludeAll.\n\ntype: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_exclude_any": { + "description": "LabelsExcludeAll is a list of label names that the host must not be a\nmember of in order to receive the profile. It must not be a member of any\nof the listed labels.\n\ntype: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_all": { + "description": "LabelsIncludeAll is a list of label names that the host must be a member\nof in order to receive the profile. It must be a member of all listed\nlabels.\n\ntype: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_any": { + "description": "LabelsIncludeAny is a list of label names that the host must be a member\nof in order to receive the profile. It may be a member of\nany listed labels.\n\ntype: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "path": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "paths": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "MacOSMigration": { + "additionalProperties": false, + "description": "MacOSMigration contains settings related to the MDM migration work flow.", + "properties": { + "enable": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "mode": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "webhook_url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "MacOSSettings": { + "additionalProperties": false, + "description": "MacOSSettings contains settings specific to macOS.", + "properties": { + "assets": { + "description": "Assets is a slice of Apple DDM asset (com.apple.asset) declaration file\npaths. Unlike CustomSettings, assets are not stored on the AppConfig/team\nspec: this field is only populated while parsing a GitOps file so the\nassets can be applied via their own batch endpoint. It is intentionally\nomitted from ToMap/FromMap.\n\ntype: `array\u003cMDMProfileSpec\u003e`", + "items": { + "$ref": "#/$defs/MDMProfileSpec" + }, + "type": [ + "array", + "null" + ] + }, + "configuration_profiles": { + "description": "CustomSettings is a slice of configuration profile file paths.\n\nNOTE: These are only present here for informational purposes.\n(The source of truth for profiles is in MySQL.)\n\ntype: `array\u003cMDMProfileSpec\u003e`", + "items": { + "$ref": "#/$defs/MDMProfileSpec" + }, + "type": [ + "array", + "null" + ] + }, + "custom_settings": { + "deprecated": true, + "deprecationMessage": "'custom_settings' is deprecated, use 'configuration_profiles' instead", + "description": "CustomSettings is a slice of configuration profile file paths.\n\nNOTE: These are only present here for informational purposes.\n(The source of truth for profiles is in MySQL.)\n\ntype: `array\u003cMDMProfileSpec\u003e`", + "items": { + "$ref": "#/$defs/MDMProfileSpec" + }, + "type": [ + "array", + "null" + ] + }, + "enable_disk_encryption": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "MacOSSetup": { + "additionalProperties": false, + "description": "MacOSSetup contains settings related to the setup of DEP enrolled devices.", + "properties": { + "apple_enable_release_device_manually": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "apple_setup_assistant": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "bootstrap_package": { + "deprecated": true, + "deprecationMessage": "'bootstrap_package' is deprecated, use 'macos_bootstrap_package' instead", + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "enable_create_local_admin_account": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_end_user_authentication": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_managed_local_account": { + "deprecated": true, + "deprecationMessage": "'enable_managed_local_account' is deprecated, use 'enable_create_local_admin_account' instead", + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_release_device_manually": { + "deprecated": true, + "deprecationMessage": "'enable_release_device_manually' is deprecated, use 'apple_enable_release_device_manually' instead", + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "end_user_local_account_type": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "lock_end_user_info": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "macos_bootstrap_package": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "macos_manual_agent_install": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "macos_script": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "macos_setup_assistant": { + "deprecated": true, + "deprecationMessage": "'macos_setup_assistant' is deprecated, use 'apple_setup_assistant' instead", + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "manual_agent_install": { + "deprecated": true, + "deprecationMessage": "'manual_agent_install' is deprecated, use 'macos_manual_agent_install' instead", + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "require_all_software_macos": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "require_all_software_windows": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "script": { + "deprecated": true, + "deprecationMessage": "'script' is deprecated, use 'macos_script' instead", + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "software": { + "description": "type: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "MaintainedAppSpec": { + "additionalProperties": false, + "anyOf": [ + { + "errorMessage": "A fleet_maintained_apps entry must set slug.", + "required": [ + "slug" + ] + } + ], + "properties": { + "categories": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "icon": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "install_script": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "labels_exclude_any": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_all": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_any": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "post_install_script": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "pre_install_query": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "self_service": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "setup_experience": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "setup_experience_platform": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "slug": { + "description": "type: `string`", + "type": "string" + }, + "uninstall_script": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "version": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "OrbitAgentOptions": { + "additionalProperties": false, + "properties": { + "debug_logging_on_enroll_duration": { + "description": "DebugLoggingOnEnrollDuration is the number of seconds (0 to\nMaxOrbitDebugLoggingOnEnrollDurationSeconds) that every host enrolling\nunder this scope is stamped with orbit_debug_until = now() + duration.\n\ntype: `integer`" + } + }, + "type": [ + "object", + "null" + ] + }, + "OrgInfo": { + "additionalProperties": false, + "description": "OrgInfo contains general info about the organization using Fleet.", + "properties": { + "contact_url": { + "description": "ContactURL is the URL displayed for users to contact support. By default,\nhttps://fleetdm.com/company/contact is used.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "org_logo_url": { + "description": "Deprecated: use OrgLogoURLDarkMode.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "org_logo_url_dark_mode": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "org_logo_url_light_background": { + "description": "Deprecated: use OrgLogoURLLightMode.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "org_logo_url_light_mode": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "org_name": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "PolicyRunScript": { + "additionalProperties": false, + "properties": { + "path": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "Query": { + "additionalProperties": false, + "anyOf": [ + { + "errorMessage": "A report must set name and query (or reference a file with path/paths).", + "required": [ + "name", + "query" + ] + }, + { + "errorMessage": "A report must set name and query (or reference a file with path/paths).", + "required": [ + "path" + ] + }, + { + "errorMessage": "A report must set name and query (or reference a file with path/paths).", + "required": [ + "paths" + ] + } + ], + "properties": { + "automations_enabled": { + "description": "AutomationsEnabled is set to false if not set.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "description": { + "description": "Description is the description of the query.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "discard_data": { + "description": "DiscardData indicates if the scheduled query results should be discarded (true)\nor kept (false) in a query report.\n\nIf not set, then the default value is false.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "fleet": { + "description": "TeamName is the team's name, the default \"\" means the query will be\ncreated globally. This field is only used when creating a query,\nwhen editing a query this field is ignored.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "interval": { + "description": "Interval is set to 0 if not set.\n\ntype: `integer`" + }, + "labels_include_all": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_any": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "logging": { + "description": "Logging is set to \"snapshot\" if not set.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "min_osquery_version": { + "description": "MinOsqueryVersion is set to empty if not set.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "name": { + "description": "Name is the name of the query (which is unique in its team or globally).\nThis field must be non-empty.\n\ntype: `string`", + "type": "string" + }, + "observer_can_run": { + "description": "ObserverCanRun is set to false if not set.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "path": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "paths": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "platform": { + "description": "Platform is set to empty if not set when creating a query.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "query": { + "description": "Query is the actual osquery SQL query. This field must be non-empty.\n\ntype: `string`", + "type": "string" + }, + "team": { + "deprecated": true, + "deprecationMessage": "'team' is deprecated, use 'fleet' instead", + "description": "TeamName is the team's name, the default \"\" means the query will be\ncreated globally. This field is only used when creating a query,\nwhen editing a query this field is ignored.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "SMTPSettings": { + "additionalProperties": false, + "description": "SMTPSettings is part of the AppConfig which defines the wire representation of the app config endpoints", + "properties": { + "authentication_method": { + "description": "SMTPAuthenticationMethod authentication method smtp server will use\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "authentication_type": { + "description": "SMTPAuthenticationType type of authentication for SMTP\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "configured": { + "description": "SMTPConfigured is a flag that indicates if smtp has been successfully\ntested with the settings provided by an admin user.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "domain": { + "description": "SMTPDomain optional domain for SMTP\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "enable_smtp": { + "description": "SMTPEnabled indicates whether the user has selected that SMTP is\nenabled in the UI.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_ssl_tls": { + "description": "SMTPEnableSSLTLS whether to use SSL/TLS for SMTP\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_start_tls": { + "description": "SMTPEnableStartTLS detects of TLS is enabled on mail server and starts to use it (default true)\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "password": { + "description": "SMTPPassword must be provided if SMTPAuthenticationType is UserNamePassword\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "port": { + "description": "SMTPPort port SMTP server will use\n\ntype: `integer`" + }, + "sender_address": { + "description": "SMTPSenderAddress is the email address that will appear in emails sent\nfrom Fleet\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "server": { + "description": "SMTPServer is the host name of the SMTP server Fleet will use to send mail\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "user_name": { + "description": "SMTPUserName must be provided if SMTPAuthenticationType is UserNamePassword\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "verify_ssl_certs": { + "description": "SMTPVerifySSLCerts defaults to true but can be turned off if self signed\nSSL certs are used by the SMTP server\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "SSOSettings": { + "additionalProperties": false, + "description": "SSOSettings wire format for SSO settings", + "properties": { + "enable_jit_provisioning": { + "description": "EnableJITProvisioning allows user accounts to be created the first time\nusers try to log in\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_jit_role_sync": { + "description": "EnableJITRoleSync is deprecated.\n\nEnableJITRoleSync sets whether the roles of existing accounts will be updated\nevery time SSO users log in (does not have effect if EnableJITProvisioning is false).\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_sso": { + "description": "EnableSSO flag to determine whether or not to enable SSO\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_sso_idp_login": { + "description": "EnableSSOIdPLogin flag to determine whether or not to allow IdP-initiated\nlogin.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "entity_id": { + "description": "EntityID is a uri that identifies this service provider\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "idp_image_url": { + "description": "IDPImageURL is a link to a logo or other image that is used for UX\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "idp_name": { + "description": "IDPName is a human friendly name for the IDP\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "issuer_uri": { + "description": "IssuerURI is the uri that identifies the identity provider\n\nDeprecated: Not used, only left here to not break the API\n(\"unsupported key provided\" error)\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "metadata": { + "description": "Metadata contains IDP metadata XML\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "metadata_url": { + "description": "MetadataURL is a URL provided by the IDP which can be used to download\nmetadata\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "sso_server_url": { + "description": "SSOServerURL is an optional URL to use for SSO authentication.\nWhen set, SSO will only work from this URL, not from the server URL.\nThis is useful for organizations with separate URLs for admin access vs agent/API access.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "ServerSettings": { + "additionalProperties": false, + "description": "ServerSettings contains general settings about the Fleet application.", + "properties": { + "ai_features_disabled": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "debug_host_ids": { + "description": "type: `array\u003cinteger\u003e`", + "items": {}, + "type": [ + "array", + "null" + ] + }, + "deferred_save_host": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "discard_reports_data": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_analytics": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "live_query_disabled": { + "deprecated": true, + "deprecationMessage": "'live_query_disabled' is deprecated, use 'live_reporting_disabled' instead", + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "live_reporting_disabled": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "query_report_cap": { + "deprecated": true, + "deprecationMessage": "'query_report_cap' is deprecated, use 'report_cap' instead", + "description": "type: `integer`" + }, + "query_reports_disabled": { + "deprecated": true, + "deprecationMessage": "'query_reports_disabled' is deprecated, use 'discard_reports_data' instead", + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "report_cap": { + "description": "type: `integer`" + }, + "scripts_disabled": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "server_url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "SoftwarePackageSpec": { + "additionalProperties": false, + "anyOf": [ + { + "errorMessage": "A package must set one of: url, hash_sha256, or path.", + "required": [ + "url" + ] + }, + { + "errorMessage": "A package must set one of: url, hash_sha256, or path.", + "required": [ + "hash_sha256" + ] + }, + { + "errorMessage": "A package must set one of: url, hash_sha256, or path.", + "required": [ + "path" + ] + } + ], + "properties": { + "always_download": { + "description": "AlwaysDownload disables conditional HTTP downloads using ETag headers.\nWhen false (the default), Fleet sends If-None-Match with the stored ETag\non subsequent downloads. If the server returns 304 Not Modified, the\ndownload is skipped entirely.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "categories": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "configuration": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "Configuration is the managed app configuration file path; only meaningful for .ipa packages.\n\ntype: `TeamSpecSoftwareAsset`" + }, + "display_name": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "hash_sha256": { + "description": "type: `string`", + "type": "string" + }, + "icon": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "install_script": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "labels_exclude_any": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_all": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_any": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "post_install_script": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "pre_install_query": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "referenced_yaml_path": { + "description": "ReferencedYamlPath is the resolved path of the file used to fill the\nsoftware package. Only present after parsing a GitOps file on the fleetctl\nside of processing. This is required to match a setup_experience.software to\nits corresponding software package, as we do this matching by yaml path.\n\nIt must be JSON-marshaled because it gets set during gitops file processing,\nwhich is then re-marshaled to JSON from this struct and later re-unmarshaled\nduring ApplyGroup...\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "self_service": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "setup_experience": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "setup_experience_platform": { + "description": "SetupExperiencePlatform selects the installer for the setup experience,\nas a comma-separated string of platforms (e.g. \"darwin,linux\"),\nconsistent with the query/policy `platform` field. Additive with\nInstallDuringSetup: the native platform is controlled by that bool, the\nnon-native entries feed the setup_experience_software_installers\ncross-table. Only meaningful for packages whose file can run on more than\none platform (today: .sh).\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "slug": { + "description": "FMA\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "uninstall_script": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "url": { + "description": "type: `string`", + "type": "string" + }, + "version": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "SoftwareSpec": { + "additionalProperties": false, + "properties": { + "app_store_apps": { + "description": "type: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "fleet_maintained_apps": { + "description": "type: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "packages": { + "description": "type: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "TeamGoogleCalendarIntegration": { + "additionalProperties": false, + "properties": { + "enable_calendar_events": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "webhook_url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "TeamIntegrations": { + "additionalProperties": false, + "description": "TeamIntegrations contains the configuration for external services' integrations for a specific team.", + "properties": { + "conditional_access_enabled": { + "description": "ConditionalAccessEnabled indicates whether the conditional access feature is enabled on this team.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "google_calendar": { + "$ref": "#/$defs/TeamGoogleCalendarIntegration", + "description": "type: `TeamGoogleCalendarIntegration`" + }, + "jira": { + "description": "type: `array\u003cTeamJiraIntegration\u003e`", + "items": { + "$ref": "#/$defs/TeamJiraIntegration" + }, + "type": [ + "array", + "null" + ] + }, + "zendesk": { + "description": "type: `array\u003cTeamZendeskIntegration\u003e`", + "items": { + "$ref": "#/$defs/TeamZendeskIntegration" + }, + "type": [ + "array", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "TeamJiraIntegration": { + "additionalProperties": false, + "description": "TeamJiraIntegration configures an instance of an integration with the Jira system for a team.", + "properties": { + "enable_failing_policies": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "project_key": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "TeamMDM": { + "additionalProperties": false, + "properties": { + "android_settings": { + "$ref": "#/$defs/AndroidSettings", + "description": "type: `AndroidSettings`" + }, + "apple_settings": { + "$ref": "#/$defs/MacOSSettings", + "description": "type: `MacOSSettings`" + }, + "enable_disk_encryption": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_recovery_lock_password": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "ios_updates": { + "$ref": "#/$defs/AppleOSUpdateSettings", + "description": "type: `AppleOSUpdateSettings`" + }, + "ipados_updates": { + "$ref": "#/$defs/AppleOSUpdateSettings", + "description": "type: `AppleOSUpdateSettings`" + }, + "macos_settings": { + "$ref": "#/$defs/MacOSSettings", + "deprecated": true, + "deprecationMessage": "'macos_settings' is deprecated, use 'apple_settings' instead", + "description": "type: `MacOSSettings`" + }, + "macos_setup": { + "$ref": "#/$defs/MacOSSetup", + "deprecated": true, + "deprecationMessage": "'macos_setup' is deprecated, use 'setup_experience' instead", + "description": "type: `MacOSSetup`" + }, + "macos_updates": { + "$ref": "#/$defs/AppleOSUpdateSettings", + "description": "type: `AppleOSUpdateSettings`" + }, + "name_template": { + "description": "HostNameTemplate is the template used to compute a host's display name from\nhost-identity Fleet variables (e.g. $FLEET_VAR_HOST_HARDWARE_SERIAL).\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "setup_experience": { + "$ref": "#/$defs/MacOSSetup", + "description": "type: `MacOSSetup`" + }, + "windows_require_bitlocker_pin": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "windows_settings": { + "$ref": "#/$defs/WindowsSettings", + "description": "type: `WindowsSettings`" + }, + "windows_updates": { + "$ref": "#/$defs/WindowsUpdates", + "description": "type: `WindowsUpdates`" + } + }, + "type": [ + "object", + "null" + ] + }, + "TeamSpecAppStoreApp": { + "additionalProperties": false, + "anyOf": [ + { + "errorMessage": "An app_store_apps entry must set app_store_id.", + "required": [ + "app_store_id" + ] + } + ], + "properties": { + "app_store_id": { + "description": "type: `string`", + "type": "string" + }, + "auto_update_enabled": { + "description": "Auto-update fields for VPP apps\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "auto_update_window_end": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "auto_update_window_start": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "categories": { + "description": "Categories is the list of names of software categories associated with this VPP app.\n\ntype: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "configuration": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "display_name": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "icon": { + "$ref": "#/$defs/TeamSpecSoftwareAsset", + "description": "type: `TeamSpecSoftwareAsset`" + }, + "labels_exclude_any": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_all": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "labels_include_any": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "platform": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "self_service": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "setup_experience": { + "description": "InstallDuringSetup indicates whether a package should be incorporated into setup experience;\nif not supplied (Valid field is false) then the server-side value for setup experience membership\nis not changed, for compatibility with the old fleetctl apply format\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "TeamSpecSoftwareAsset": { + "additionalProperties": false, + "properties": { + "path": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "TeamWebhookSettings": { + "additionalProperties": false, + "properties": { + "failing_policies_webhook": { + "$ref": "#/$defs/FailingPoliciesWebhookSettings", + "description": "type: `FailingPoliciesWebhookSettings`" + }, + "host_status_webhook": { + "$ref": "#/$defs/HostStatusWebhookSettings", + "description": "HostStatusWebhook can be nil to match the TeamSpec webhook settings\n\ntype: `HostStatusWebhookSettings`" + } + }, + "type": [ + "object", + "null" + ] + }, + "TeamZendeskIntegration": { + "additionalProperties": false, + "description": "TeamZendeskIntegration configures an instance of an integration with the external Zendesk service for a team.", + "properties": { + "enable_failing_policies": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "group_id": { + "description": "type: `integer`" + }, + "url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "VulnExposureFilterSettings": { + "additionalProperties": false, + "description": "VulnExposureFilterSettings is the persisted default filter state for the Vulnerability exposure (CVE) dashboard chart.", + "properties": { + "cvss_max": { + "description": "type: `number`" + }, + "cvss_min": { + "description": "type: `number`" + }, + "epss_max": { + "description": "type: `number`" + }, + "epss_min": { + "description": "type: `number`" + }, + "exclude_vulnerabilities": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "has_known_exploit": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "software_filters": { + "description": "type: `array\u003cstring\u003e`", + "items": { + "type": [ + "string", + "null" + ] + }, + "type": [ + "array", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "VulnerabilitiesWebhookSettings": { + "additionalProperties": false, + "description": "VulnerabilitiesWebhookSettings holds the settings for vulnerabilities webhooks.", + "properties": { + "destination_url": { + "description": "DestinationURL is the webhook's URL.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "enable_vulnerabilities_webhook": { + "description": "Enable indicates whether the webhook for vulnerabilities is enabled.\n\ntype: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "host_batch_size": { + "description": "HostBatchSize allows sending multiple requests in batches of hosts for each vulnerable software found.\nA value of 0 means no batching.\n\ntype: `integer`" + } + }, + "type": [ + "object", + "null" + ] + }, + "VulnerabilitySettings": { + "additionalProperties": false, + "description": "VulnerabilitySettings is part of the AppConfig which defines how fleet will behave while scanning for vulnerabilities in the host software", + "properties": { + "databases_path": { + "description": "DatabasesPath is the directory where fleet will store the different databases\n\ntype: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "WebhookSettings": { + "additionalProperties": false, + "properties": { + "activities_webhook": { + "$ref": "#/$defs/ActivitiesWebhookSettings", + "description": "type: `ActivitiesWebhookSettings`" + }, + "failing_policies_webhook": { + "$ref": "#/$defs/FailingPoliciesWebhookSettings", + "description": "type: `FailingPoliciesWebhookSettings`" + }, + "host_status_webhook": { + "$ref": "#/$defs/HostStatusWebhookSettings", + "description": "type: `HostStatusWebhookSettings`" + }, + "interval": { + "description": "Interval is the interval for running the webhooks.\n\nThis value currently configures both the host status and failing policies webhooks.\n\ntype: `string`", + "type": [ + "string", + "null" + ] + }, + "vulnerabilities_webhook": { + "$ref": "#/$defs/VulnerabilitiesWebhookSettings", + "description": "type: `VulnerabilitiesWebhookSettings`" + } + }, + "type": [ + "object", + "null" + ] + }, + "WindowsSettings": { + "additionalProperties": false, + "properties": { + "configuration_profiles": { + "description": "NOTE: These are only present here for informational purposes.\n(The source of truth for profiles is in MySQL.)\n\ntype: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + }, + "custom_settings": { + "deprecated": true, + "deprecationMessage": "'custom_settings' is deprecated, use 'configuration_profiles' instead", + "description": "NOTE: These are only present here for informational purposes.\n(The source of truth for profiles is in MySQL.)\n\ntype: `array\u003cobject\u003e`", + "items": { + "type": [ + "object", + "null" + ] + }, + "type": [ + "array", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "WindowsUpdates": { + "additionalProperties": false, + "description": "WindowsUpdates is part of AppConfig and defines the Windows update settings.", + "properties": { + "deadline_days": { + "description": "type: `integer`" + }, + "grace_period_days": { + "description": "type: `integer`" + } + }, + "type": [ + "object", + "null" + ] + }, + "YaraRule": { + "additionalProperties": false, + "anyOf": [ + { + "errorMessage": "A yara_rules entry must set path.", + "required": [ + "path" + ] + } + ], + "properties": { + "path": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + }, + "ZendeskIntegration": { + "additionalProperties": false, + "description": "ZendeskIntegration configures an instance of an integration with the external Zendesk service.", + "properties": { + "api_token": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "email": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "enable_failing_policies": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "enable_software_vulnerabilities": { + "description": "type: `boolean`", + "type": [ + "boolean", + "null" + ] + }, + "group_id": { + "description": "type: `integer`" + }, + "url": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + } + }, + "type": [ + "object", + "null" + ] + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "agent_options": { + "$ref": "#/$defs/AgentOptions", + "description": "type: `AgentOptions`" + }, + "controls": { + "$ref": "#/$defs/ControlsWithTypes", + "description": "type: `ControlsWithTypes`" + }, + "custom_host_vitals": { + "description": "type: `array\u003cGitOpsCustomHostVital\u003e`", + "items": { + "$ref": "#/$defs/GitOpsCustomHostVital" + }, + "type": [ + "array", + "null" + ] + }, + "labels": { + "description": "type: `array\u003cLabelSpec\u003e`", + "items": { + "$ref": "#/$defs/LabelSpec" + }, + "type": [ + "array", + "null" + ] + }, + "name": { + "description": "type: `string`", + "type": [ + "string", + "null" + ] + }, + "org_settings": { + "$ref": "#/$defs/GitOpsOrgSettings", + "description": "type: `GitOpsOrgSettings`" + }, + "policies": { + "description": "type: `array\u003cGitOpsPolicySpec\u003e`", + "items": { + "$ref": "#/$defs/GitOpsPolicySpec" + }, + "type": [ + "array", + "null" + ] + }, + "reports": { + "description": "type: `array\u003cQuery\u003e`", + "items": { + "$ref": "#/$defs/Query" + }, + "type": [ + "array", + "null" + ] + }, + "settings": { + "$ref": "#/$defs/GitOpsFleetSettings", + "description": "type: `GitOpsFleetSettings`" + }, + "software": { + "$ref": "#/$defs/GitOpsSoftware", + "description": "type: `GitOpsSoftware`" + } + }, + "type": [ + "object", + "null" + ] +} diff --git a/tools/gitops-auto-complete/go.mod b/tools/gitops-auto-complete/go.mod new file mode 100644 index 00000000000..1aac3bc2670 --- /dev/null +++ b/tools/gitops-auto-complete/go.mod @@ -0,0 +1,153 @@ +module fleetdm.local/gitops-auto-complete + +go 1.26.5 + +require ( + github.com/fleetdm/fleet/v4 v4.0.0 + github.com/ghodss/yaml v1.0.0 + github.com/invopop/jsonschema v0.14.0 + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 +) + +require ( + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.18.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/iam v1.5.3 // indirect + cloud.google.com/go/pubsub v1.50.1 // indirect + cloud.google.com/go/pubsub/v2 v2.0.0 // indirect + filippo.io/edwards25519 v1.2.0 // indirect + github.com/Azure/go-ntlmssp v0.1.1 // indirect + github.com/Masterminds/semver/v3 v3.5.0 // indirect + github.com/agnivade/levenshtein v1.2.1 // indirect + github.com/andygrunwald/go-jira v1.16.0 // indirect + github.com/armon/go-radix v1.0.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/firehose v1.37.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect + github.com/aws/aws-sdk-go-v2/service/kinesis v1.43.5 // indirect + github.com/aws/aws-sdk-go-v2/service/lambda v1.88.5 // indirect + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.8 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect + github.com/aws/smithy-go v1.24.2 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect + github.com/buger/jsonparser v1.1.2 // indirect + github.com/cenkalti/backoff v2.2.1+incompatible // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/elastic/go-sysinfo v1.11.2 // indirect + github.com/elastic/go-windows v1.0.1 // indirect + github.com/expr-lang/expr v1.17.7 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/fatih/structs v1.1.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/getsentry/sentry-go v0.18.0 // indirect + github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 // indirect + github.com/go-kit/kit v0.12.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-sql-driver/mysql v1.9.3 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gomodule/oauth1 v0.2.0 // indirect + github.com/gomodule/redigo v1.8.9 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.12 // indirect + github.com/googleapis/gax-go/v2 v2.17.0 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/igm/sockjs-go/v3 v3.0.2 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmoiron/sqlx v1.3.5 // indirect + github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect + github.com/klauspost/compress v1.18.6 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/micromdm/micromdm v1.9.0 // indirect + github.com/micromdm/nanolib v0.2.0 // indirect + github.com/micromdm/plist v0.2.3-0.20260123201933-667adaf87d87 // indirect + github.com/mna/redisc v1.3.2 // indirect + github.com/nats-io/nats.go v1.49.0 // indirect + github.com/nats-io/nkeys v0.4.15 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/nukosuke/go-zendesk v0.13.1 // indirect + github.com/oschwald/geoip2-golang v1.8.0 // indirect + github.com/oschwald/maxminddb-golang v1.10.0 // indirect + github.com/pb33f/ordered-map/v2 v2.3.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/realclientip/realclientip-go v1.0.0 // indirect + github.com/rs/zerolog v1.32.0 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/smallstep/pkcs7 v0.0.0-20240723090913-5e2c6a136dfa // indirect + github.com/smallstep/scep v0.0.0-20240214080410-892e41795b99 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/spf13/viper v1.21.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/throttled/throttled/v2 v2.8.0 // indirect + github.com/trivago/tgo v1.0.7 // indirect + go.elastic.co/apm/v2 v2.7.0 // indirect + go.elastic.co/fastjson v1.1.0 // indirect + go.mozilla.org/pkcs7 v0.9.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/bridges/otelslog v0.15.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/log v0.16.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/sdk v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/image v0.42.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/oauth2 v0.35.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/text v0.38.0 // indirect + golang.org/x/time v0.15.0 // indirect + google.golang.org/api v0.269.0 // indirect + google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 // indirect + google.golang.org/grpc v1.79.3 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/guregu/null.v3 v3.5.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + howett.net/plist v1.0.1 // indirect + software.sslmate.com/src/go-pkcs12 v0.7.1 // indirect +) + +replace github.com/fleetdm/fleet/v4 => ../.. diff --git a/tools/gitops-auto-complete/go.sum b/tools/gitops-auto-complete/go.sum new file mode 100644 index 00000000000..bd5edb7922a --- /dev/null +++ b/tools/gitops-auto-complete/go.sum @@ -0,0 +1,615 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= +cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= +cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= +cloud.google.com/go/kms v1.25.0 h1:gVqvGGUmz0nYCmtoxWmdc1wli2L1apgP8U4fghPGSbQ= +cloud.google.com/go/kms v1.25.0/go.mod h1:XIdHkzfj0bUO3E+LvwPg+oc7s58/Ns8Nd8Sdtljihbk= +cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= +cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= +cloud.google.com/go/pubsub v1.50.1 h1:fzbXpPyJnSGvWXF1jabhQeXyxdbCIkXTpjXHy7xviBM= +cloud.google.com/go/pubsub v1.50.1/go.mod h1:6YVJv3MzWJUVdvQXG081sFvS0dWQOdnV+oTo++q/xFk= +cloud.google.com/go/pubsub/v2 v2.0.0 h1:0qS6mRJ41gD1lNmM/vdm6bR7DQu6coQcVwD+VPf0Bz0= +cloud.google.com/go/pubsub/v2 v2.0.0/go.mod h1:0aztFxNzVQIRSZ8vUr79uH2bS3jwLebwK6q1sgEub+E= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +github.com/Azure/go-ntlmssp v0.1.1 h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDPw= +github.com/Azure/go-ntlmssp v0.1.1/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= +github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/MicahParks/jwkset v0.11.0 h1:yc0zG+jCvZpWgFDFmvs8/8jqqVBG9oyIbmBtmjOhoyQ= +github.com/MicahParks/jwkset v0.11.0/go.mod h1:U2oRhRaLgDCLjtpGL2GseNKGmZtLs/3O7p+OZaL5vo0= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f h1:HR5nRmUQgXrwqZOwZ2DAc/aCi3Bu3xENpspW935vxu0= +github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f/go.mod h1:f3HiCrHjHBdcm6E83vGaXh1KomZMA2P6aeo3hKx/wg0= +github.com/WatchBeam/clock v0.0.0-20170901150240-b08e6b4da7ea h1:C9Xwp9fZf9BFJMsTqs8P+4PETXwJPUOuJZwBfVci+4A= +github.com/WatchBeam/clock v0.0.0-20170901150240-b08e6b4da7ea/go.mod h1:N5eJIl14rhNCrE5I3O10HIyhZ1HpjaRHT9WDg1eXxtI= +github.com/XSAM/otelsql v0.39.0 h1:4o374mEIMweaeevL7fd8Q3C710Xi2Jh/c8G4Qy9bvCY= +github.com/XSAM/otelsql v0.39.0/go.mod h1:uMOXLUX+wkuAuP0AR3B45NXX7E9lJS2mERa8gqdU8R0= +github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= +github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= +github.com/andygrunwald/go-jira v1.16.0 h1:PU7C7Fkk5L96JvPc6vDVIrd99vdPnYudHu4ju2c2ikQ= +github.com/andygrunwald/go-jira v1.16.0/go.mod h1:UQH4IBVxIYWbgagc0LF/k9FRs9xjIiQ8hIcC6HfLwFU= +github.com/antithesishq/antithesis-sdk-go v0.6.0-default-no-op h1:kpBdlEPbRvff0mDD1gk7o9BhI16b9p5yYAXRlidpqJE= +github.com/antithesishq/antithesis-sdk-go v0.6.0-default-no-op/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= +github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.16 h1:LFB4eCU2S9wpFAkEnSqtP8CgdOk0cjMIzuXas1+rbWM= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.16/go.mod h1:Q7hjCcQzFZ9QgZ+xeJhO4X1rv7uKAl4aoBEjab6MS8k= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/firehose v1.37.7 h1:rDNxf0CQboBMqzm6WmhGL58pYpKMjU6Qs3/BfY3Em4Y= +github.com/aws/aws-sdk-go-v2/service/firehose v1.37.7/go.mod h1:E1yDRkUMwlVGmDYcu5UJuwfznGNuVW29sjr2xxM2Y0w= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= +github.com/aws/aws-sdk-go-v2/service/kinesis v1.43.5 h1:LxgRVyuY+5DEPSX7kmin/V7toE8MWZ9U8n2dqRtX+RE= +github.com/aws/aws-sdk-go-v2/service/kinesis v1.43.5/go.mod h1:eUebEBEqVfOwEyDDDbGauH4PNqDCuepRvTaNbJeWr5w= +github.com/aws/aws-sdk-go-v2/service/lambda v1.88.5 h1:HWN7xwaV7Zwrn3Jlauio4u4aTMFgRzG2fblHWQeir/k= +github.com/aws/aws-sdk-go-v2/service/lambda v1.88.5/go.mod h1:6HBXRyFFqOw+ALkJ6YGHfrr20/YXYv6X9pcZErXRvCA= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.8 h1:HD6R8K10gPbN9CNqRDOs42QombXlYeLOr4KkIxe2lQs= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.8/go.mod h1:x66GdH8qjYTr6Kb4ik38Ewl6moLsg8igbceNsmxVxeA= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= +github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= +github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= +github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk= +github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/cavaliergopher/rpm v1.2.0 h1:s0h+QeVK252QFTolkhGiMeQ1f+tMeIMhGl8B1HUmGUc= +github.com/cavaliergopher/rpm v1.2.0/go.mod h1:R0q3vTqa7RUvPofAZYrnjJ63hh2vngjFfphuXiExVos= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= +github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/docker/docker v28.0.0+incompatible h1:Olh0KS820sJ7nPsBKChVhk5pzqcwDR15fumfAd/p9hM= +github.com/docker/docker v28.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/doug-martin/goqu/v9 v9.18.0 h1:/6bcuEtAe6nsSMVK/M+fOiXUNfyFF3yYtE07DBPFMYY= +github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ= +github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= +github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= +github.com/elastic/go-sysinfo v1.11.2 h1:mcm4OSYVMyws6+n2HIVMGkln5HOpo5Ie1ZmbbNn0jg4= +github.com/elastic/go-sysinfo v1.11.2/go.mod h1:GKqR8bbMK/1ITnez9NIsIfXQr25aLhRJa7AfT8HpBFQ= +github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0= +github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= +github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8= +github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= +github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 h1:vymEbVwYFP/L05h5TKQxvkXoKxNvTpjxYKdF1Nlwuao= +github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg= +github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/oauth1 v0.2.0 h1:/nNHAD99yipOEspQFbAnNmwGTZ1UNXiD/+JLxwx79fo= +github.com/gomodule/oauth1 v0.2.0/go.mod h1:4r/a8/3RkhMBxJQWL5qzbOEcaQmNPIkNoI7P8sXeI08= +github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= +github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= +github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.12 h1:Fg+zsqzYEs1ZnvmcztTYxhgCBsx3eEhEwQ1W/lHq/sQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.12/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc= +github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda h1:5ikpG9mYCMFiZX0nkxoV6aU2IpCHPdws3gCNgdZeEV0= +github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= +github.com/groob/plist v0.0.0-20220217120414-63fa881b19a5 h1:saaSiB25B1wgaxrshQhurfPKUGJ4It3OxNJUy0rdOjU= +github.com/groob/plist v0.0.0-20220217120414-63fa881b19a5/go.mod h1:itkABA+w2cw7x5nYUS/pLRef6ludkZKOigbROmCTaFw= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/igm/sockjs-go/v3 v3.0.2 h1:2m0k53w0DBiGozeQUIEPR6snZFmpFpYvVsGnfLPNXbE= +github.com/igm/sockjs-go/v3 v3.0.2/go.mod h1:UqchsOjeagIBFHvd+RZpLaVRbCwGilEC08EDHsD1jYE= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/jsonschema v0.14.0 h1:MHQqLhvpNUZfw+hM3AZDYK7jxO8FZoQeQM77g8iyZjg= +github.com/invopop/jsonschema v0.14.0/go.mod h1:ygm6C2EaVNMBDPpaPlnOA2pFAxBnxGjFlMZABxm9n2I= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/micromdm/micromdm v1.9.0 h1:FAsIKOpnGcq21UQCrHCUxZwSW4NwBLGOoUtzbURxds8= +github.com/micromdm/micromdm v1.9.0/go.mod h1:YsAtsEvfEIwpjYTUPpWkJXSfH0hhp9mMHW1BgIZgRt8= +github.com/micromdm/nanolib v0.2.0 h1:g5GHQuUpS82WIAB15LyenjF/0/WSUNJMe5XZfCJSXq4= +github.com/micromdm/nanolib v0.2.0/go.mod h1:FwBKCvvphgYvbdUZ+qw5kay7NHJcg6zPi8W7kXNajmE= +github.com/micromdm/plist v0.2.3-0.20260123201933-667adaf87d87 h1:U9A+0ZED3cPxb5ufiTzyn2kyo6UFoR5bMggCR0Q/DOg= +github.com/micromdm/plist v0.2.3-0.20260123201933-667adaf87d87/go.mod h1:flkfm0od6GzyXBqI28h5sgEyi3iPO28W2t1Zm9LpwWs= +github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76 h1:KGuD/pM2JpL9FAYvBrnBBeENKZNh6eNtjqytV6TYjnk= +github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/mna/redisc v1.3.2 h1:sc9C+nj6qmrTFnsXb70xkjAHpXKtjjBuE6v2UcQV0ZE= +github.com/mna/redisc v1.3.2/go.mod h1:CplIoaSTDi5h9icnj4FLbRgHoNKCHDNJDVRztWDGeSQ= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/nats-io/jwt/v2 v2.8.1 h1:V0xpGuD/N8Mi+fQNDynXohVvp7ZztevW5io8CUWlPmU= +github.com/nats-io/jwt/v2 v2.8.1/go.mod h1:nWnOEEiVMiKHQpnAy4eXlizVEtSfzacZ1Q43LIRavZg= +github.com/nats-io/nats-server/v2 v2.12.6 h1:Egbx9Vl7Ch8wTtpXPGqbehkZ+IncKqShUxvrt1+Enc8= +github.com/nats-io/nats-server/v2 v2.12.6/go.mod h1:4HPlrvtmSO3yd7KcElDNMx9kv5EBJBnJJzQPptXlheo= +github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE= +github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw= +github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4= +github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/ngrok/sqlmw v0.0.0-20211220175533-9d16fdc47b31 h1:FFHgfAIoAXCCL4xBoAugZVpekfGmZ/fBBueneUKBv7I= +github.com/ngrok/sqlmw v0.0.0-20211220175533-9d16fdc47b31/go.mod h1:E26fwEtRNigBfFfHDWsklmo0T7Ixbg0XXgck+Hq4O9k= +github.com/nukosuke/go-zendesk v0.13.1 h1:EdYpn+FxROLguADEJK5reOHcpysM8wyWPOWO96SIc0A= +github.com/nukosuke/go-zendesk v0.13.1/go.mod h1:86Cg7RhSvPfOqZOtQXteJEV9yIQVQsy2HVDk++Yf3jA= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/open-policy-agent/opa v1.4.2 h1:ag4upP7zMsa4WE2p1pwAFeG4Pn3mNwfAx9DLhhJfbjU= +github.com/open-policy-agent/opa v1.4.2/go.mod h1:DNzZPKqKh4U0n0ANxcCVlw8lCSv2c+h5G/3QvSYdWZ8= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= +github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= +github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= +github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= +github.com/pb33f/ordered-map/v2 v2.3.1 h1:5319HDO0aw4DA4gzi+zv4FXU9UlSs3xGZ40wcP1nBjY= +github.com/pb33f/ordered-map/v2 v2.3.1/go.mod h1:qxFQgd0PkVUtOMCkTapqotNgzRhMPL7VvaHKbd1HnmQ= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= +github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/realclientip/realclientip-go v1.0.0 h1:+yPxeC0mEaJzq1BfCt2h4BxlyrvIIBzR6suDc3BEF1U= +github.com/realclientip/realclientip-go v1.0.0/go.mod h1:CXnUdVwFRcXFJIRb/dTYqbT7ud48+Pi2pFm80bxDmcI= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/saferwall/pe v1.5.5 h1:GGbzKjXDm7i+1K6riOgtgblyTdRmTbr3r11IzjovAK8= +github.com/saferwall/pe v1.5.5/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/sassoftware/relic/v8 v8.0.1 h1:uYUoaoTQMs67up8/46NgrSxSftgfY4VWBusDVg56k7I= +github.com/sassoftware/relic/v8 v8.0.1/go.mod h1:s/MwugRcovgYcNJNOyvLfqRHDX7iArHtFtUR9kEodz8= +github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d h1:RQqyEogx5J6wPdoxqL132b100j8KjcVHO1c0KLRoIhc= +github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d/go.mod h1:PegD7EVqlN88z7TpCqH92hHP+GBpfomGCCnw1PFtNOA= +github.com/shogo82148/rdsmysql/v2 v2.5.0 h1:lNU8bKYqIMIOQPh3dI4UORXzSFWpnldXF67kPV6rpiY= +github.com/shogo82148/rdsmysql/v2 v2.5.0/go.mod h1:r5DuS0dJuoa8tLmN6B8UmDKoyuTnq03JgrpAWB6kkWo= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81/go.mod h1:SoUAr/4M46rZ3WaLstHxGhLEgoYIDRqxQEXLOmOEB0Y= +github.com/smallstep/pkcs7 v0.0.0-20240723090913-5e2c6a136dfa h1:FtxzVccOwaK+bK4bnWBPGua0FpCOhrVyeo6Fy9nxdlo= +github.com/smallstep/pkcs7 v0.0.0-20240723090913-5e2c6a136dfa/go.mod h1:SoUAr/4M46rZ3WaLstHxGhLEgoYIDRqxQEXLOmOEB0Y= +github.com/smallstep/scep v0.0.0-20240214080410-892e41795b99 h1:e85HuLX5/MW15yJ7yWb/PMNFW1Kx1N+DeQtpQnlMUbw= +github.com/smallstep/scep v0.0.0-20240214080410-892e41795b99/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tchap/go-patricia/v2 v2.3.2 h1:xTHFutuitO2zqKAQ5rCROYgUb7Or/+IC3fts9/Yc7nM= +github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/throttled/throttled/v2 v2.8.0 h1:B5VfdM8BE+ClI2Ji238SbNOTWfYcocvuAhgT27lvwrE= +github.com/throttled/throttled/v2 v2.8.0/go.mod h1:q1QyZVQXxb2NUfJ+Hjucmlrsrz9s/jt2ilMwSMo7a2I= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= +github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM= +github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= +github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.einride.tech/aip v0.73.0 h1:bPo4oqBo2ZQeBKo4ZzLb1kxYXTY1ysJhpvQyfuGzvps= +go.einride.tech/aip v0.73.0/go.mod h1:Mj7rFbmXEgw0dq1dqJ7JGMvYCZZVxmGOR3S4ZcV5LvQ= +go.elastic.co/apm/v2 v2.7.0 h1:fbsy3BmTTedIbj7+1Ay9Zpdfuztd8RUk7Dm0JvxRW/M= +go.elastic.co/apm/v2 v2.7.0/go.mod h1:f1Sr3rVJju5winTjsJtKzofdU32L7+Mw/c23cVcn3Io= +go.elastic.co/fastjson v1.1.0 h1:3MrGBWWVIxe/xvsbpghtkFoPciPhOCmjsR/HfwEeQR4= +go.elastic.co/fastjson v1.1.0/go.mod h1:boNGISWMjQsUPy/t6yqt2/1Wx4YNPSe+mZjlyw9vKKI= +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= +go.mozilla.org/pkcs7 v0.9.0 h1:yM4/HS9dYv7ri2biPtxt8ikvB37a980dg69/pKmS+eI= +go.mozilla.org/pkcs7 v0.9.0/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/bridges/otelslog v0.15.0 h1:yOYhGNPZseueTTvWp5iBD3/CthrmvayUXYEX862dDi4= +go.opentelemetry.io/contrib/bridges/otelslog v0.15.0/go.mod h1:CvaNVqIfcybc+7xqZNubbE+26K6P7AKZF/l0lE2kdCk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/log v0.16.0 h1:DeuBPqCi6pQwtCK0pO4fvMB5eBq6sNxEnuTs88pjsN4= +go.opentelemetry.io/otel/log v0.16.0/go.mod h1:rWsmqNVTLIA8UnwYVOItjyEZDbKIkMxdQunsIhpUMes= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s= +go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/image v0.42.0 h1:1gSs6ehNWXLbkHBIPcWztk3D/6aIA/8hauiAYtlodVY= +golang.org/x/image v0.42.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= +golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/api v0.269.0 h1:qDrTOxKUQ/P0MveH6a7vZ+DNHxJQjtGm/uvdbdGXCQg= +google.golang.org/api v0.269.0/go.mod h1:N8Wpcu23Tlccl0zSHEkcAZQKDLdquxK+l9r2LkwAauE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM= +google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/guregu/null.v3 v3.5.0 h1:xTcasT8ETfMcUHn0zTvIYtQud/9Mx5dJqD554SZct0o= +gopkg.in/guregu/null.v3 v3.5.0/go.mod h1:E4tX2Qe3h7QdL+uZ3a0vqvYwKQsRSQKM5V4YltdgH9Y= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= +howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +software.sslmate.com/src/go-pkcs12 v0.7.1 h1:bxkUPRsvTPNRBZa4M/aSX4PyMOEbq3V8I6hbkG4F4Q8= +software.sslmate.com/src/go-pkcs12 v0.7.1/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= diff --git a/tools/gitops-auto-complete/main.go b/tools/gitops-auto-complete/main.go new file mode 100644 index 00000000000..62037fd465f --- /dev/null +++ b/tools/gitops-auto-complete/main.go @@ -0,0 +1,671 @@ +// Command gitops-auto-complete generates a JSON schema from Fleet's GitOps Go +// structs so editors (yamlls) can offer completion/validation for GitOps YAML. +package main + +import ( + "encoding/json" + "fmt" + "go/ast" + "go/parser" + "go/token" + "maps" + "os" + "path/filepath" + "reflect" + "runtime" + "strings" + "unicode" + + "github.com/fleetdm/fleet/v4/pkg/spec" + "github.com/invopop/jsonschema" +) + +// generatedOsqueryOptions is the Fleet-generated file defining the osqueryOptions and +// osqueryCommandLineFlags structs, which back config.options and command_line_flags. +const generatedOsqueryOptions = "server/fleet/agent_options_generated.go" + +// agentOptionsBase holds the hand-written per-OS structs that both of those structs +// embed, so it's parsed alongside the generated file. +const agentOptionsBase = "server/fleet/agent_options.go" + +func main() { + if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help") { + fmt.Println(`Usage: gitops-auto-complete [output-file] + +Generates a JSON schema from Fleet's GitOps structs for yaml-language-server. +With an output-file, writes the schema there; otherwise prints to stdout.`) + return + } + + // Resolve Fleet source paths from this file's own location so the tool works + // from any working directory, not just the module root. + repoRoot := "" + if _, thisFile, _, ok := runtime.Caller(0); ok { + repoRoot = filepath.Join(filepath.Dir(thisFile), "..", "..") + } + + renames := map[string]string{} + collectRenames(reflect.TypeFor[GitOpsSpec](), map[reflect.Type]bool{}, renames) + + reflector := &jsonschema.Reflector{ + RequiredFromJSONSchemaTags: true, + Mapper: typeMapper, + KeyNamer: toSnake, + // Inline the root struct's properties instead of hiding them behind a + // single top-level $ref, so yamlls offers root-level key completion. + ExpandedStruct: true, + } + addFleetGoComments(reflector, repoRoot) + + raw, err := json.Marshal(reflector.Reflect(&GitOpsSpec{})) + if err != nil { + fmt.Fprintln(os.Stderr, "marshal schema:", err) + os.Exit(1) + } + + var schemaKeys map[string]any + err = json.Unmarshal(raw, &schemaKeys) + if err != nil { + fmt.Fprintln(os.Stderr, "unmarshal schema:", err) + os.Exit(1) + } + + // Merges run first so the injected keys get the same treatment as the rest. If a + // schema can't be built, generation continues without it. + osquerySources := []string{ + filepath.Join(repoRoot, generatedOsqueryOptions), + filepath.Join(repoRoot, agentOptionsBase), + } + + osqueryOptions, err := osqueryStructSchema("osqueryOptions", osquerySources...) + if err != nil { + fmt.Fprintln(os.Stderr, "warning: could not type config.options:", err) + } + mergeOsqueryOptions(schemaKeys, osqueryOptions) + + commandLineFlags, err := osqueryStructSchema("osqueryCommandLineFlags", osquerySources...) + if err != nil { + fmt.Fprintln(os.Stderr, "warning: could not type command_line_flags:", err) + } + mergeCommandLineFlags(schemaKeys, commandLineFlags) + + mergeMissingMDMKeys(schemaKeys, spec.GitOpsMDM{}) + fixYaraRules(schemaKeys) + + // Order matters. annotate and addGitOpsKeyNotes read types that relaxNulls + // later strips, so they run first. addPathReferences also runs before relaxNulls + // so the path keys it adds get relaxed too. typeStrictStringKeys runs after + // relaxNulls to restore the string types it drops. + nodes := collectNodes(schemaKeys) + annotate(nodes, renames) + addGitOpsKeyNotes(schemaKeys) + addPathReferences(schemaKeys) + addRequiredKeys(schemaKeys) + + // Collect again so relaxNulls reaches the aliases and path keys added above. + nodes = collectNodes(schemaKeys) + relaxNulls(nodes) + typeStrictStringKeys(schemaKeys) + + out, err := json.MarshalIndent(schemaKeys, "", " ") + if err != nil { + fmt.Fprintln(os.Stderr, "marshal schema:", err) + os.Exit(1) + } + + if len(os.Args) <= 1 { + fmt.Println(string(out)) + return + } + + path := os.Args[1] + err = os.WriteFile(path, append(out, '\n'), 0o644) + if err != nil { + fmt.Fprintln(os.Stderr, "write file:", err) + os.Exit(1) + } + fmt.Fprintln(os.Stderr, "wrote", path) +} + +// --- building the base schema from Go types --- + +// addFleetGoComments pulls Fleet's Go doc comments into field descriptions (shown +// on hover). AddGoComments derives package paths from the walk dir relative to cwd, +// so it runs from the repo root and restores cwd afterward. +func addFleetGoComments(reflector *jsonschema.Reflector, repoRoot string) { + workingDir, err := os.Getwd() + if err != nil { + return + } + + err = os.Chdir(repoRoot) + if err != nil { + return + } + defer func() { _ = os.Chdir(workingDir) }() + + const base = "github.com/fleetdm/fleet/v4" + _ = reflector.AddGoComments(base, "server/fleet") + _ = reflector.AddGoComments(base, "pkg/spec") +} + +// osqueryStructSchema parses one of Fleet's generated osquery structs (osqueryOptions +// for config.options, osqueryCommandLineFlags for command_line_flags) into a strict +// object schema, so its keys get completion, types, and unknown-key validation matching +// what Fleet enforces. Both structs embed per-OS structs that live in the hand-written +// agent_options.go, so every source file is parsed and the embeds are pulled up into one +// flat set of keys. The structs are unexported, so we parse the AST rather than reflect. +func osqueryStructSchema(rootStruct string, paths ...string) (map[string]any, error) { + fileSet := token.NewFileSet() + structsByName := map[string]*ast.StructType{} + for _, path := range paths { + parsedFile, err := parser.ParseFile(fileSet, path, nil, 0) + if err != nil { + return nil, err + } + ast.Inspect(parsedFile, func(astNode ast.Node) bool { + typeSpec, ok := astNode.(*ast.TypeSpec) + if !ok { + return true + } + if structType, ok := typeSpec.Type.(*ast.StructType); ok { + structsByName[typeSpec.Name.Name] = structType + } + return true + }) + } + + root, ok := structsByName[rootStruct] + if !ok { + return nil, fmt.Errorf("%s struct not found", rootStruct) + } + + properties := map[string]any{} + var addFields func(structType *ast.StructType) + addFields = func(structType *ast.StructType) { + for _, field := range structType.Fields.List { + fieldType, ok := field.Type.(*ast.Ident) + if !ok { + continue + } + + // An anonymous field is an embedded per-OS struct: pull its keys up. + if len(field.Names) == 0 { + if embedded, ok := structsByName[fieldType.Name]; ok { + addFields(embedded) + } + continue + } + + if field.Tag == nil { + continue + } + tag := reflect.StructTag(strings.Trim(field.Tag.Value, "`")) + name, _, _ := strings.Cut(tag.Get("json"), ",") + jsonType := goTypeToJSON(fieldType.Name) + if name == "" || name == "-" || jsonType == "" { + continue + } + + // [type, null] keeps the key typed while allowing an empty value, and + // relaxNulls leaves the union alone so numeric keys keep their type. + properties[name] = map[string]any{"type": []any{jsonType, "null"}} + } + } + addFields(root) + + if len(properties) == 0 { + return nil, fmt.Errorf("%s produced no keys", rootStruct) + } + + return map[string]any{"type": "object", "additionalProperties": false, "properties": properties}, nil +} + +// reflectProperties reflects a struct and returns its top-level property schemas. +func reflectProperties(v any) map[string]any { + reflector := &jsonschema.Reflector{RequiredFromJSONSchemaTags: true, Mapper: typeMapper, KeyNamer: toSnake, ExpandedStruct: true} + raw, err := json.Marshal(reflector.Reflect(v)) + if err != nil { + return nil + } + + var reflected map[string]any + err = json.Unmarshal(raw, &reflected) + if err != nil { + return nil + } + + properties, _ := reflected["properties"].(map[string]any) + return properties +} + +// toSnake converts a Go field name to snake_case. invopop applies KeyNamer to the +// json-tag name when a tag is present, so already-snake tags pass through unchanged. +// Untagged Fleet fields like GitOpsSoftware.Packages get fixed. +func toSnake(name string) string { + runes := []rune(name) + result := make([]rune, 0, len(runes)+4) + + for i, char := range runes { + if !unicode.IsUpper(char) { + result = append(result, char) + continue + } + + if i > 0 { + previous := runes[i-1] + nextIsLower := i+1 < len(runes) && unicode.IsLower(runes[i+1]) + atBoundary := unicode.IsLower(previous) || unicode.IsDigit(previous) || (unicode.IsUpper(previous) && nextIsLower) + if atBoundary { + result = append(result, '_') + } + } + + result = append(result, unicode.ToLower(char)) + } + + return string(result) +} + +// collectRenames walks the type tree recording json-tag -> renameto name. Fleet +// aliases many config keys with a `renameto` tag for the new fleets/reports +// terminology, and GitOps YAML uses the renamed key, but invopop only reads json. +func collectRenames(goType reflect.Type, visited map[reflect.Type]bool, renames map[string]string) { + for goType.Kind() == reflect.Pointer || goType.Kind() == reflect.Slice || goType.Kind() == reflect.Array || goType.Kind() == reflect.Map { + goType = goType.Elem() + } + + if goType.Kind() != reflect.Struct || visited[goType] { + return + } + visited[goType] = true + + for field := range goType.Fields() { + renameTo := field.Tag.Get("renameto") + if renameTo != "" { + jsonName, _, _ := strings.Cut(field.Tag.Get("json"), ",") + renameName, _, _ := strings.Cut(renameTo, ",") + if jsonName != "" && renameName != "" { + renames[jsonName] = renameName + } + } + + collectRenames(field.Type, visited, renames) + } +} + +// mergeOsqueryOptions types agent_options.config.options with the generated osquery +// option list. config keeps its other keys (schedule, decorators, ...) open. +func mergeOsqueryOptions(schemaKeys map[string]any, osqueryOptions map[string]any) { + // If the options couldn't be built, leave config open rather than pinning its + // options to an empty or null schema. + if len(osqueryOptions) == 0 { + return + } + + agentOptions, ok := definitionProperties(schemaKeys, "AgentOptions") + if !ok { + return + } + + agentOptions["config"] = map[string]any{ + "type": "object", + "properties": map[string]any{"options": osqueryOptions}, + } +} + +// mergeCommandLineFlags types agent_options.command_line_flags with the generated +// osquery CLI flag list, at the AgentOptions root only since it isn't valid in overrides. +func mergeCommandLineFlags(schemaKeys map[string]any, commandLineFlags map[string]any) { + if len(commandLineFlags) == 0 { + return + } + + agentOptions, ok := definitionProperties(schemaKeys, "AgentOptions") + if !ok { + return + } + + agentOptions["command_line_flags"] = commandLineFlags +} + +// mergeMissingMDMKeys copies gitops-only MDM keys into the "MDM" def, whose base +// fleet.MDM (org_settings.mdm) omits them. spec.GitOpsMDM embeds fleet.MDM and adds +// them (e.g. end_user_license_agreement), so add whichever the def is missing. +func mergeMissingMDMKeys(schemaKeys map[string]any, gitOpsMDM spec.GitOpsMDM) { + extraProperties := reflectProperties(&gitOpsMDM) + if extraProperties == nil { + return + } + + properties, ok := definitionProperties(schemaKeys, "MDM") + if !ok { + return + } + + for key, value := range extraProperties { + _, exists := properties[key] + if !exists { + properties[key] = value + } + } +} + +// --- tree helpers --- + +// collectNodes walks the schema iteratively and returns every object node, +// parents always before their children. Collecting once lets the passes below be +// plain loops instead of repeated recursive tree walks. +func collectNodes(schemaKeys any) []map[string]any { + var nodes []map[string]any + stack := []any{schemaKeys} + + for len(stack) > 0 { + // Pop the next value off the stack. + current := stack[len(stack)-1] + stack = stack[:len(stack)-1] + + switch node := current.(type) { + case map[string]any: + // Add the object to nodes, then push its values to visit next. + nodes = append(nodes, node) + for _, child := range node { + stack = append(stack, child) + } + case []any: + // Walk through arrays without collecting them. + stack = append(stack, node...) + } + } + + return nodes +} + +// definitionByName returns a named $def object and whether it was found. +func definitionByName(schemaKeys map[string]any, name string) (map[string]any, bool) { + definitions, _ := schemaKeys["$defs"].(map[string]any) + definition, ok := definitions[name].(map[string]any) + return definition, ok +} + +// definitionProperties returns the properties of a named $def and whether it was found. +func definitionProperties(schemaKeys map[string]any, name string) (map[string]any, bool) { + definition, ok := definitionByName(schemaKeys, name) + if !ok { + return nil, false + } + + properties, ok := definition["properties"].(map[string]any) + return properties, ok +} + +// appendDescription puts text below node's existing description, if any. The blank +// line matters, since yamlls renders the two parts as separate paragraphs on hover. +func appendDescription(node map[string]any, text string) { + existing, ok := node["description"].(string) + if ok && existing != "" { + node["description"] = existing + "\n\n" + text + return + } + + node["description"] = text +} + +// typeLabel returns a short type name for a schema node, for hover text. +func typeLabel(node map[string]any) string { + ref, ok := node["$ref"].(string) + if ok { + return strings.TrimPrefix(ref, "#/$defs/") + } + + schemaType, ok := node["type"].(string) + if !ok { + _, isAnyOf := node["anyOf"] + if isAnyOf { + return "boolean or object" + } + return "" + } + + if schemaType != "array" { + return schemaType + } + + items, ok := node["items"].(map[string]any) + if !ok { + return "array" + } + + innerLabel := typeLabel(items) + if innerLabel == "" { + return "array" + } + return "array<" + innerLabel + ">" +} + +// resolveReference follows a chain of $ref links through definitions to the concrete +// schema object. Each iteration replaces node with the definition its $ref points at, +// and returns when node has no $ref, the ref is unknown, or it was already seen (a +// cycle), so it visits each definition at most once. +func resolveReference(definitions map[string]any, node map[string]any) map[string]any { + seen := map[string]bool{} + for { + ref, isRef := node["$ref"].(string) + if !isRef { + return node // reached a concrete node + } + + name := strings.TrimPrefix(ref, "#/$defs/") + if seen[name] { + return node // cycle: stop where we are + } + seen[name] = true + + definition, isObject := definitions[name].(map[string]any) + if !isObject { + return node // dangling ref: nothing to follow + } + node = definition + } +} + +// --- post-processing passes --- + +// annotate walks the collected nodes once and, per node, does two things: label +// each property with its type (shown on hover), then add an alias for any renamed +// key alongside the deprecated original. Labeling comes first so an alias, a shallow +// copy of the property, inherits the label. +func annotate(nodes []map[string]any, renames map[string]string) { + for _, node := range nodes { + properties, ok := node["properties"].(map[string]any) + if !ok { + continue + } + + for _, value := range properties { + property, ok := value.(map[string]any) + if !ok { + continue + } + + label := typeLabel(property) + if label == "" { + continue + } + + appendDescription(property, "type: `"+label+"`") + } + + for jsonName, renameName := range renames { + original, present := properties[jsonName] + if !present { + continue + } + + property, isObject := original.(map[string]any) + _, aliasExists := properties[renameName] + + switch { + case aliasExists: + // Keep an alias that's already present rather than overwriting it. + case isObject: + properties[renameName] = maps.Clone(property) + default: + properties[renameName] = original + } + + if isObject { + property["deprecated"] = true + property["deprecationMessage"] = "'" + jsonName + "' is deprecated, use '" + renameName + "' instead" + } + } + } +} + +// addGitOpsKeyNotes appends each declarativeExceptions note to the schema node at +// its dotted key path. It descends the path from the root, following $refs and +// stepping transparently through array items, and attaches the note to the property +// node itself (so shared $defs aren't affected). Missing paths are skipped. +func addGitOpsKeyNotes(schemaKeys map[string]any) { + definitions, _ := schemaKeys["$defs"].(map[string]any) + + for path, note := range declarativeExceptions { + node := schemaKeys + found := true + + for segment := range strings.SplitSeq(path, ".") { + container := resolveReference(definitions, node) + items, isArray := container["items"].(map[string]any) + if isArray { + container = resolveReference(definitions, items) + } + + properties, hasProperties := container["properties"].(map[string]any) + if !hasProperties { + found = false + break + } + + next, isObject := properties[segment].(map[string]any) + if !isObject { + found = false + break + } + node = next + } + + if found { + appendDescription(node, note) + } + } +} + +// fixYaraRules rewrites the reflected YaraRule shape. AppConfig.YaraRules reflects to +// {name, contents}, but gitops org_settings.yara_rules items are {path} file references +// (fleet.YaraRuleSpec), so swap the properties to match what gitops accepts. +func fixYaraRules(schemaKeys map[string]any) { + definition, ok := definitionByName(schemaKeys, "YaraRule") + if !ok { + return + } + + definition["properties"] = map[string]any{"path": map[string]any{"type": "string"}} + definition["additionalProperties"] = false + delete(definition, "required") +} + +// addPathReferences adds the file-reference keys the Go structs don't model, so a real +// GitOps file using e.g. `- path: ./lib/foo.yml` doesn't light up with "Property path +// is not allowed". `path` is one external file, and `paths` is a single glob string. +func addPathReferences(schemaKeys map[string]any) { + for _, name := range pathReferenceDefinitions { + addStringProperty(schemaKeys, name, "path") + } + for _, name := range pathsReferenceDefinitions { + addStringProperty(schemaKeys, name, "path") + addStringProperty(schemaKeys, name, "paths") + } +} + +func addStringProperty(schemaKeys map[string]any, definitionName string, key string) { + properties, ok := definitionProperties(schemaKeys, definitionName) + if !ok { + return + } + + if _, exists := properties[key]; !exists { + properties[key] = map[string]any{"type": "string"} + } +} + +// addRequiredKeys injects an anyOf of single-key required branches (each with the +// same errorMessage) so an item is valid when any one of the required keys is present. +func addRequiredKeys(schemaKeys map[string]any) { + for _, rule := range requiredKeys { + definition, ok := definitionByName(schemaKeys, rule.definition) + if !ok { + continue + } + + anyOf := make([]any, 0, len(rule.validKeyCombinations)) + for _, combination := range rule.validKeyCombinations { + required := make([]any, len(combination)) + for i, key := range combination { + required[i] = key + } + anyOf = append(anyOf, map[string]any{ + "required": required, + "errorMessage": rule.message, + }) + } + + definition["anyOf"] = anyOf + } +} + +// relaxNulls makes empty placeholder keys valid. GitOps files routinely leave keys +// empty, like `minimum_version:` or `scripts:`, which YAML parses as null, so every +// leaf has to accept null. How it does that depends on the type. +func relaxNulls(nodes []map[string]any) { + for _, node := range nodes { + schemaType, ok := node["type"].(string) + if !ok || node["enum"] != nil { + continue + } + + switch schemaType { + case "integer", "number": + // Left untyped. Some Fleet ints marshal as string enums, like + // label_membership_type (a uint that marshals as "dynamic"), so a number + // check would reject a value fleetctl accepts. Reflection can't tell those + // apart from real ints, so numeric leaves stay unchecked. + delete(node, "type") + case "string", "boolean", "object", "array": + // Keep the type as [type, null] so a wrong type is still caught while an + // empty null placeholder validates. This makes yamlls offer null in value + // completion, a limitation we accept so a real error like an unquoted + // version: 13.0 shows up in the editor, not at apply time. fleetctl rejects + // that value too, since ghodss decodes it as a number into a Go string. + node["type"] = []any{schemaType, "null"} + } + } +} + +// typeStrictStringKeys re-applies a strict string type to the keys in strictStringKeys, +// undoing the relaxNulls pass for them. +func typeStrictStringKeys(schemaKeys map[string]any) { + for definitionName, keys := range strictStringKeys { + properties, ok := definitionProperties(schemaKeys, definitionName) + if !ok { + continue + } + + for _, key := range keys { + node, ok := properties[key].(map[string]any) + if !ok { + continue + } + node["type"] = "string" + } + } +} diff --git a/tools/gitops-auto-complete/schema_test.go b/tools/gitops-auto-complete/schema_test.go new file mode 100644 index 00000000000..80dc3b13db2 --- /dev/null +++ b/tools/gitops-auto-complete/schema_test.go @@ -0,0 +1,333 @@ +package main + +import ( + "bytes" + "encoding/json" + "os" + "os/exec" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/fleetdm/fleet/v4/pkg/spec" + ghodss "github.com/ghodss/yaml" + "github.com/santhosh-tekuri/jsonschema/v6" +) + +const schemaFile = "generated-schema.json" + +// compileSchema loads the committed schema and compiles it. The test validates +// against the committed artifact. TestSchemaUpToDate separately guarantees that +// artifact matches what the generator currently produces. +func compileSchema(t *testing.T) *jsonschema.Schema { + t.Helper() + b, err := os.ReadFile(schemaFile) + if err != nil { + t.Fatalf("read %s: %v", schemaFile, err) + } + doc, err := jsonschema.UnmarshalJSON(bytes.NewReader(b)) + if err != nil { + t.Fatalf("parse schema: %v", err) + } + c := jsonschema.NewCompiler() + if err := c.AddResource("schema.json", doc); err != nil { + t.Fatalf("add schema resource: %v", err) + } + schema, err := c.Compile("schema.json") + if err != nil { + t.Fatalf("compile schema: %v", err) + } + return schema +} + +// loadInstance reads a YAML fixture and decodes it into the JSON-compatible value +// the validator expects (ghodss converts YAML->JSON so numbers/bools are typed). +func loadInstance(t *testing.T, path string) any { + t.Helper() + y, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read %s: %v", path, err) + } + j, err := ghodss.YAMLToJSON(y) + if err != nil { + t.Fatalf("yaml->json %s: %v", path, err) + } + inst, err := jsonschema.UnmarshalJSON(bytes.NewReader(j)) + if err != nil { + t.Fatalf("parse %s: %v", path, err) + } + return inst +} + +// TestValidFixtures asserts every comprehensive valid gitops file validates +// cleanly against the schema (so all the keys they use stay covered). +func TestValidFixtures(t *testing.T) { + schema := compileSchema(t) + files, err := filepath.Glob("testdata/valid/*.yml") + if err != nil || len(files) == 0 { + t.Fatalf("no valid fixtures found: %v", err) + } + for _, file := range files { + t.Run(filepath.Base(file), func(t *testing.T) { + if err := schema.Validate(loadInstance(t, file)); err != nil { + t.Errorf("expected %s to validate, got:\n%v", file, err) + } + }) + } +} + +// TestInvalidFixtures asserts the schema still rejects the specific mistakes the +// tool is designed to catch (unknown keys, wrong-typed required keys, an item +// missing its required key). +func TestInvalidFixtures(t *testing.T) { + schema := compileSchema(t) + files, err := filepath.Glob("testdata/invalid/*.yml") + if err != nil || len(files) == 0 { + t.Fatalf("no invalid fixtures found: %v", err) + } + for _, file := range files { + t.Run(filepath.Base(file), func(t *testing.T) { + if err := schema.Validate(loadInstance(t, file)); err == nil { + t.Errorf("expected %s to fail validation, but it passed", file) + } + }) + } +} + +// TestInvariants pins the post-processing that a refactor could silently break. +func TestInvariants(t *testing.T) { + b, err := os.ReadFile(schemaFile) + if err != nil { + t.Fatalf("read %s: %v", schemaFile, err) + } + var doc map[string]any + if err := json.Unmarshal(b, &doc); err != nil { + t.Fatalf("unmarshal schema: %v", err) + } + defs, _ := doc["$defs"].(map[string]any) + if defs == nil { + t.Fatal("schema has no $defs") + } + def := func(name string) map[string]any { + d, _ := defs[name].(map[string]any) + if d == nil { + t.Fatalf("missing $def %q", name) + } + return d + } + props := func(node map[string]any) map[string]any { + p, _ := node["properties"].(map[string]any) + return p + } + + // Declarative notes: the exact set from declarativeExceptions must appear in + // the schema, once each. Counting by note string keeps this independent of the + // walker that placed them. + t.Run("declarative notes", func(t *testing.T) { + want := map[string]int{} + for _, note := range declarativeExceptions { + want[note]++ + } + got := map[string]int{} + for _, desc := range collectDescriptions(doc) { + for note := range want { + if containsNote(desc, note) { + got[note]++ + } + } + } + for note, count := range want { + if got[note] != count { + t.Errorf("note %q: want %d occurrence(s), got %d", note, count, got[note]) + } + } + }) + + // Required keys: these defs gate on an anyOf of single-key branches. + t.Run("required keys", func(t *testing.T) { + for _, name := range []string{"SoftwarePackageSpec", "TeamSpecAppStoreApp", "MaintainedAppSpec"} { + if _, ok := def(name)["anyOf"].([]any); !ok { + t.Errorf("%s: expected an anyOf of required-key branches", name) + } + } + }) + + // Typed strict-string keys survive relaxNulls as strict strings. + t.Run("typed strict-string keys", func(t *testing.T) { + for def, keys := range map[string][]string{ + "SoftwarePackageSpec": {"url", "hash_sha256"}, + "TeamSpecAppStoreApp": {"app_store_id"}, + "MaintainedAppSpec": {"slug"}, + } { + p := props(defs[def].(map[string]any)) + for _, key := range keys { + keyNode, _ := p[key].(map[string]any) + if keyNode["type"] != "string" { + t.Errorf("%s.%s: want type string, got %v", def, key, keyNode["type"]) + } + } + } + }) + + // agent_options.config.options is populated and closed, while config stays open. + t.Run("config.options", func(t *testing.T) { + cfg, _ := props(def("AgentOptions"))["config"].(map[string]any) + if cfg == nil { + t.Fatal("AgentOptions.config missing") + } + opts, _ := props(cfg)["options"].(map[string]any) + if opts == nil { + t.Fatal("AgentOptions.config.options missing") + } + if opts["additionalProperties"] != false { + t.Errorf("options should be closed (additionalProperties:false), got %v", opts["additionalProperties"]) + } + optKeys := props(opts) + if len(optKeys) == 0 { + t.Error("options has no properties") + } + // allow_unsafe comes from an embedded per-OS struct, so it guards embed handling. + if _, ok := optKeys["allow_unsafe"]; !ok { + t.Error("options missing embedded per-OS option 'allow_unsafe'") + } + if _, closed := cfg["additionalProperties"]; closed { + t.Error("config should stay open (no additionalProperties)") + } + }) + + // command_line_flags is populated and closed, including per-OS flags pulled up from + // the embedded structs (users_service_delay isn't in the base flag struct). + t.Run("command_line_flags", func(t *testing.T) { + clf, _ := props(def("AgentOptions"))["command_line_flags"].(map[string]any) + if clf == nil { + t.Fatal("AgentOptions.command_line_flags missing") + } + if clf["additionalProperties"] != false { + t.Errorf("command_line_flags should be closed (additionalProperties:false), got %v", clf["additionalProperties"]) + } + flags := props(clf) + if _, ok := flags["verbose"]; !ok { + t.Error("command_line_flags missing base flag 'verbose'") + } + if _, ok := flags["users_service_delay"]; !ok { + t.Error("command_line_flags missing embedded per-OS flag 'users_service_delay'") + } + }) + + // Path refs: path-only defs carry `path` but not `paths`, and path+paths defs carry both. + t.Run("path refs", func(t *testing.T) { + for _, name := range []string{"ControlsWithTypes", "SoftwarePackageSpec"} { + p := props(def(name)) + if _, ok := p["path"]; !ok { + t.Errorf("%s: missing 'path'", name) + } + if _, ok := p["paths"]; ok { + t.Errorf("%s: unexpected 'paths' on a path-only def", name) + } + } + for _, name := range []string{"GitOpsPolicySpec", "LabelSpec"} { + p := props(def(name)) + if _, ok := p["path"]; !ok { + t.Errorf("%s: missing 'path'", name) + } + if _, ok := p["paths"]; !ok { + t.Errorf("%s: missing 'paths'", name) + } + } + }) + + // Rename aliases: old key deprecated, new key present and not deprecated. + t.Run("rename aliases", func(t *testing.T) { + p := props(def("ControlsWithTypes")) + old, _ := p["macos_setup"].(map[string]any) + if old["deprecated"] != true { + t.Errorf("macos_setup should be deprecated, got %v", old["deprecated"]) + } + if _, ok := p["setup_experience"]; !ok { + t.Error("setup_experience alias missing") + } + }) +} + +// TestSchemaUpToDate regenerates the schema and asserts the committed file matches, +// so a refactor that changes the output is caught (and the validation/invariant +// tests above stay meaningful against the committed artifact). +func TestSchemaUpToDate(t *testing.T) { + tmp := filepath.Join(t.TempDir(), "out.json") + cmd := exec.Command("go", "run", ".", tmp) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("go run . failed: %v\n%s", err, out) + } + got, err := os.ReadFile(tmp) + if err != nil { + t.Fatalf("read regenerated schema: %v", err) + } + want, err := os.ReadFile(schemaFile) + if err != nil { + t.Fatalf("read committed schema: %v", err) + } + if !bytes.Equal(got, want) { + t.Errorf("%s is stale; run `go run . %s` to update", schemaFile, schemaFile) + } +} + +func collectDescriptions(node any) []string { + var out []string + switch node := node.(type) { + case map[string]any: + if desc, ok := node["description"].(string); ok { + out = append(out, desc) + } + for _, child := range node { + out = append(out, collectDescriptions(child)...) + } + case []any: + for _, child := range node { + out = append(out, collectDescriptions(child)...) + } + } + return out +} + +func containsNote(desc string, note string) bool { + return bytes.Contains([]byte(desc), []byte(note)) +} + +// TestControlsKeysCoverSpec fails when spec.GitOpsControls gains a controls key that +// the hand-written ControlsWithTypes hasn't mirrored. It's the guard that would have +// caught the missing name_template. The embedded fleet.BaseItem's path/paths are added +// separately by addPathReferences, and the tag-less Defined field is internal, so both +// are excluded from the comparison. +func TestControlsKeysCoverSpec(t *testing.T) { + specKeys := jsonTagSet(reflect.TypeFor[spec.GitOpsControls]()) + delete(specKeys, "path") + delete(specKeys, "paths") + + toolKeys := jsonTagSet(reflect.TypeFor[ControlsWithTypes]()) + for key := range specKeys { + if !toolKeys[key] { + t.Errorf("ControlsWithTypes is missing controls key %q from spec.GitOpsControls; add it", key) + } + } +} + +// jsonTagSet returns the json key names of a struct, pulling names up through embedded +// structs and skipping fields with no json tag or "-". +func jsonTagSet(t reflect.Type) map[string]bool { + keys := map[string]bool{} + for field := range t.Fields() { + if field.Anonymous { + for key := range jsonTagSet(field.Type) { + keys[key] = true + } + continue + } + + name, _, _ := strings.Cut(field.Tag.Get("json"), ",") + if name != "" && name != "-" { + keys[name] = true + } + } + return keys +} diff --git a/tools/gitops-auto-complete/testdata/invalid/app_store_id_wrong_type.yml b/tools/gitops-auto-complete/testdata/invalid/app_store_id_wrong_type.yml new file mode 100644 index 00000000000..d4bbdd7d12f --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/app_store_id_wrong_type.yml @@ -0,0 +1,3 @@ +software: + app_store_apps: + - app_store_id: 123 diff --git a/tools/gitops-auto-complete/testdata/invalid/appstore_path.yml b/tools/gitops-auto-complete/testdata/invalid/appstore_path.yml new file mode 100644 index 00000000000..3d4225e64c0 --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/appstore_path.yml @@ -0,0 +1,4 @@ +# app_store_apps items don't accept a top-level path: and require app_store_id. +software: + app_store_apps: + - path: ../lib/software/apps.yml diff --git a/tools/gitops-auto-complete/testdata/invalid/fma_path.yml b/tools/gitops-auto-complete/testdata/invalid/fma_path.yml new file mode 100644 index 00000000000..bef47cf3504 --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/fma_path.yml @@ -0,0 +1,5 @@ +# fleet_maintained_apps items don't accept a top-level path: and require slug. +# fleetctl rejects this with an unknown-key error plus "slug is required". +software: + fleet_maintained_apps: + - path: ../lib/software/multiple-packages.yml diff --git a/tools/gitops-auto-complete/testdata/invalid/label_no_name.yml b/tools/gitops-auto-complete/testdata/invalid/label_no_name.yml new file mode 100644 index 00000000000..a4ffab5b39c --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/label_no_name.yml @@ -0,0 +1,3 @@ +# A label must set name (or path/paths); this one has neither. +labels: + - query: "SELECT 1;" diff --git a/tools/gitops-auto-complete/testdata/invalid/package_no_source.yml b/tools/gitops-auto-complete/testdata/invalid/package_no_source.yml new file mode 100644 index 00000000000..98e9cda2729 --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/package_no_source.yml @@ -0,0 +1,3 @@ +software: + packages: + - self_service: true diff --git a/tools/gitops-auto-complete/testdata/invalid/paths_wrong_type.yml b/tools/gitops-auto-complete/testdata/invalid/paths_wrong_type.yml new file mode 100644 index 00000000000..13a16aedd5a --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/paths_wrong_type.yml @@ -0,0 +1,5 @@ +# `paths` is a single glob string, not a list. +policies: + - paths: + - "../lib/policies/a.yml" + - "../lib/policies/b.yml" diff --git a/tools/gitops-auto-complete/testdata/invalid/policy_no_name.yml b/tools/gitops-auto-complete/testdata/invalid/policy_no_name.yml new file mode 100644 index 00000000000..1c9034efbad --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/policy_no_name.yml @@ -0,0 +1,3 @@ +# A policy must set name (or path/paths); this one has neither. +policies: + - query: "SELECT 1;" diff --git a/tools/gitops-auto-complete/testdata/invalid/report_no_name.yml b/tools/gitops-auto-complete/testdata/invalid/report_no_name.yml new file mode 100644 index 00000000000..686c20b38fa --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/report_no_name.yml @@ -0,0 +1,3 @@ +# A report must set name (or path/paths); this one has neither. +reports: + - query: "SELECT 1;" diff --git a/tools/gitops-auto-complete/testdata/invalid/report_no_query.yml b/tools/gitops-auto-complete/testdata/invalid/report_no_query.yml new file mode 100644 index 00000000000..7ff491b5f20 --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/report_no_query.yml @@ -0,0 +1,3 @@ +# A report must set name AND query; this one omits query. +reports: + - name: My Report diff --git a/tools/gitops-auto-complete/testdata/invalid/unknown_key.yml b/tools/gitops-auto-complete/testdata/invalid/unknown_key.yml new file mode 100644 index 00000000000..511178445ef --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/unknown_key.yml @@ -0,0 +1 @@ +not_a_real_key: 123 diff --git a/tools/gitops-auto-complete/testdata/invalid/url_wrong_type.yml b/tools/gitops-auto-complete/testdata/invalid/url_wrong_type.yml new file mode 100644 index 00000000000..b0388cec717 --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/url_wrong_type.yml @@ -0,0 +1,3 @@ +software: + packages: + - url: 12345 diff --git a/tools/gitops-auto-complete/testdata/invalid/version_wrong_type.yml b/tools/gitops-auto-complete/testdata/invalid/version_wrong_type.yml new file mode 100644 index 00000000000..210ccf27e47 --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/version_wrong_type.yml @@ -0,0 +1,6 @@ +# An unquoted numeric version parses as a YAML number, and fleetctl rejects it +# because a number can't decode into a Go string. It has to be quoted: "13.0". +software: + packages: + - url: https://example.com/pkg.deb + version: 13.0 diff --git a/tools/gitops-auto-complete/testdata/invalid/yara_rules_wrong_shape.yml b/tools/gitops-auto-complete/testdata/invalid/yara_rules_wrong_shape.yml new file mode 100644 index 00000000000..dfee193ce83 --- /dev/null +++ b/tools/gitops-auto-complete/testdata/invalid/yara_rules_wrong_shape.yml @@ -0,0 +1,5 @@ +# gitops yara_rules items are {path}, not {name, contents}; the old shape is rejected. +org_settings: + yara_rules: + - name: my_rule + contents: "rule foo {}" diff --git a/tools/gitops-auto-complete/testdata/valid/global.yml b/tools/gitops-auto-complete/testdata/valid/global.yml new file mode 100644 index 00000000000..77845068014 --- /dev/null +++ b/tools/gitops-auto-complete/testdata/valid/global.yml @@ -0,0 +1,220 @@ +# Test config +labels: + - name: a + description: A cool global label + query: SELECT 1 FROM osquery_info + label_membership_type: dynamic + - name: b + description: A fresh global label + label_membership_type: manual + hosts: + - host1 + - host2 + - name: d + description: A manual label without hosts key + label_membership_type: manual +controls: # Controls added to "No team" + apple_settings: + configuration_profiles: + - path: ./lib/macos-password.mobileconfig + windows_settings: + configuration_profiles: + - path: ./lib/windows-screenlock.xml + scripts: + - path: ./lib/collect-fleetd-logs.sh + enable_disk_encryption: false + windows_require_bitlocker_pin: false + macos_migration: + enable: false + mode: "" + webhook_url: "" + setup_experience: + macos_bootstrap_package: null + enable_end_user_authentication: false + apple_setup_assistant: null + macos_updates: + deadline: null + minimum_version: null + ios_updates: + deadline: null + minimum_version: null + ipados_updates: + deadline: null + minimum_version: null + windows_enabled_and_configured: true + windows_updates: + deadline_days: null + grace_period_days: null +reports: + - name: Scheduled query stats + description: Collect osquery performance stats directly from osquery + query: SELECT *, + (SELECT value from osquery_flags where name = 'pack_delimiter') AS delimiter + FROM osquery_schedule; + interval: 0 + platform: darwin,linux,windows + min_osquery_version: all + observer_can_run: false + automations_enabled: false + logging: snapshot + labels_include_any: + - a + - b + - name: orbit_info + query: SELECT * from orbit_info; + interval: 0 + platform: darwin,linux,windows + min_osquery_version: all + observer_can_run: false + automations_enabled: true + logging: snapshot + - name: osquery_info + query: SELECT * from osquery_info; + interval: 604800 # 1 week + platform: darwin,linux,windows,chrome + min_osquery_version: all + observer_can_run: false + automations_enabled: true + logging: snapshot +policies: + - name: 😊 Failing policy + platform: linux + description: This policy should always fail. + resolution: There is no resolution for this policy. + query: SELECT 1 FROM osquery_info WHERE start_time < 0; + labels_include_any: + - a + - name: Passing policy + platform: linux,windows,darwin,chrome + description: This policy should always pass. + resolution: There is no resolution for this policy. + query: SELECT 1; + labels_exclude_any: + - b + - name: No root logins (macOS, Linux) + platform: linux,darwin + query: SELECT 1 WHERE NOT EXISTS (SELECT * FROM last + WHERE username = "root" + AND time > (( SELECT unix_time FROM time ) - 3600 )) + critical: true + - name: 🔥 Failing policy + platform: linux + description: This policy should always fail. + resolution: There is no resolution for this policy. + query: SELECT 1 FROM osquery_info WHERE start_time < 0; + - name: 😊😊 Failing policy + platform: linux + description: This policy should always fail. + resolution: There is no resolution for this policy. + query: SELECT 1 FROM osquery_info WHERE start_time < 0; +agent_options: + command_line_flags: + distributed_denylist_duration: 0 + config: + decorators: + load: + - SELECT uuid AS host_uuid FROM system_info; + - SELECT hostname AS hostname FROM system_info; + options: + disable_distributed: false + distributed_interval: 10 + distributed_plugin: tls + distributed_tls_max_attempts: 3 + logger_tls_endpoint: /api/v1/osquery/log + pack_delimiter: / +org_settings: + server_settings: + debug_host_ids: + - 10728 + deferred_save_host: false + enable_analytics: true + live_reporting_disabled: false + report_cap: 2000 + discard_reports_data: false + scripts_disabled: false + server_url: $FLEET_SERVER_URL + ai_features_disabled: true + org_info: + contact_url: https://fleetdm.com/company/contact + org_logo_url: "" + org_logo_url_light_background: "" + org_name: $ORG_NAME + smtp_settings: + authentication_method: authmethod_plain + authentication_type: authtype_username_password + configured: false + domain: "" + enable_smtp: false + enable_ssl_tls: true + enable_start_tls: true + password: "" + port: 587 + sender_address: "" + server: "" + user_name: "" + verify_ssl_certs: true + sso_settings: + enable_jit_provisioning: false + enable_jit_role_sync: false + enable_sso: true + enable_sso_idp_login: false + entity_id: https://saml.example.com/entityid + idp_image_url: "" + idp_name: MockSAML + issuer_uri: "" + metadata: "" + metadata_url: https://mocksaml.com/api/saml/metadata + integrations: + jira: [] + zendesk: [] + google_calendar: + - domain: example.com + mdm: + end_user_authentication: + entity_id: "" + idp_name: "" + issuer_uri: "" + metadata: "" + metadata_url: "" + webhook_settings: + activities_webhook: + enable_activities_webhook: true + destination_url: https://activities_webhook_url + failing_policies_webhook: + destination_url: https://host.docker.internal:8080/bozo + enable_failing_policies_webhook: false + host_batch_size: 0 + policy_ids: [] + host_status_webhook: + days_count: 0 + destination_url: "" + enable_host_status_webhook: false + host_percentage: 0 + interval: 24h0m0s + vulnerabilities_webhook: + destination_url: "" + enable_vulnerabilities_webhook: false + host_batch_size: 0 + fleet_desktop: # Applies to Fleet Premium only + transparency_url: https://fleetdm.com/transparency + host_expiry_settings: # Applies to all teams + host_expiry_enabled: false + activity_expiry_settings: + activity_expiry_enabled: true + activity_expiry_window: 60 + features: # Features added to all teams + enable_host_users: true + enable_software_inventory: true + vulnerability_settings: + databases_path: "" + secrets: # These secrets are used to enroll hosts to the "All teams" team + - secret: SampleSecret123 + - secret: ABC +software: + packages: + - url: https://example.com/pkg.deb + version: "1.2.3" + app_store_apps: + - app_store_id: "1234567" + fleet_maintained_apps: + - slug: firefox/darwin diff --git a/tools/gitops-auto-complete/testdata/valid/references.yml b/tools/gitops-auto-complete/testdata/valid/references.yml new file mode 100644 index 00000000000..cb2530f89ef --- /dev/null +++ b/tools/gitops-auto-complete/testdata/valid/references.yml @@ -0,0 +1,15 @@ +# File-reference forms that must validate: path on section values and packages, and +# path/paths (a single glob string) on policies, labels, reports, and yara_rules. +org_settings: + yara_rules: + - path: ../lib/yara/rule.yar +policies: + - path: ../lib/policies/one.yml + - paths: "../lib/policies/*.yml" +labels: + - paths: "../lib/labels/*.yml" +reports: + - path: ../lib/reports/one.yml +software: + packages: + - path: ../lib/software/pkg.yml diff --git a/tools/gitops-auto-complete/testdata/valid/team.yml b/tools/gitops-auto-complete/testdata/valid/team.yml new file mode 100644 index 00000000000..b6e9ceb490d --- /dev/null +++ b/tools/gitops-auto-complete/testdata/valid/team.yml @@ -0,0 +1,138 @@ +name: "${TEST_TEAM_NAME}" +settings: + secrets: + - secret: "SampleSecret123-team" + - secret: "ABC-team" + webhook_settings: + host_status_webhook: + days_count: 14 + destination_url: https://example.com/host_status_webhook + enable_host_status_webhook: true + host_percentage: 25 + features: + enable_host_users: true + enable_software_inventory: true + host_expiry_settings: + host_expiry_enabled: true + host_expiry_window: 30 + integrations: + google_calendar: + enable_calendar_events: true + webhook_url: https://example.com/google_calendar_webhook +agent_options: + command_line_flags: + distributed_denylist_duration: 0 + config: + decorators: + load: + - SELECT uuid AS host_uuid FROM system_info; + - SELECT hostname AS hostname FROM system_info; + options: + disable_distributed: false + distributed_interval: 10 + distributed_plugin: tls + distributed_tls_max_attempts: 3 + logger_tls_endpoint: /api/v1/osquery/log + pack_delimiter: / +controls: + apple_settings: + configuration_profiles: + - path: ./lib/macos-password.mobileconfig + windows_settings: + configuration_profiles: + - path: ./lib/windows-screenlock.xml + scripts: + - path: ./lib/collect-fleetd-logs.sh + enable_disk_encryption: false + windows_require_bitlocker_pin: false + macos_migration: + enable: false + mode: "" + webhook_url: "" + setup_experience: + macos_bootstrap_package: ${SOFTWARE_INSTALLER_URL}/signed.pkg + enable_end_user_authentication: false + apple_setup_assistant: null + apple_enable_release_device_manually: false + macos_script: ./lib/setup_script.sh + macos_manual_agent_install: false + macos_updates: + deadline: null + minimum_version: null + windows_enabled_and_configured: true + windows_updates: + deadline_days: null + grace_period_days: null +reports: + - name: Scheduled query stats + description: Collect osquery performance stats directly from osquery + query: SELECT *, + (SELECT value from osquery_flags where name = 'pack_delimiter') AS delimiter + FROM osquery_schedule; + interval: 0 + platform: darwin,linux,windows + min_osquery_version: all + observer_can_run: false + automations_enabled: false + logging: snapshot + - name: orbit_info + query: SELECT * from orbit_info; + interval: 0 + platform: darwin,linux,windows + min_osquery_version: all + observer_can_run: false + automations_enabled: true + logging: snapshot + - name: osquery_info + query: SELECT * from osquery_info; + interval: 604800 # 1 week + platform: darwin,linux,windows,chrome + min_osquery_version: all + observer_can_run: false + automations_enabled: true + logging: snapshot +policies: + - name: "\U0001F60A Failing policy" + platform: linux + description: This policy should always fail. + resolution: There is no resolution for this policy. + query: SELECT 1 FROM osquery_info WHERE start_time < 0; + calendar_events_enabled: true + labels_exclude_any: + - a + - name: Passing policy + platform: linux,windows,darwin,chrome + description: This policy should always pass. + resolution: There is no resolution for this policy. + query: SELECT 1; + labels_include_any: + - b + - name: No root logins (macOS, Linux) + platform: linux,darwin + query: SELECT 1 WHERE NOT EXISTS (SELECT * FROM last + WHERE username = "root" + AND time > (( SELECT unix_time FROM time ) - 3600 )) + critical: true + - name: "\U0001F525 Failing policy" + platform: linux + description: This policy should always fail. + resolution: There is no resolution for this policy. + query: SELECT 1 FROM osquery_info WHERE start_time < 0; + - name: "\U0001F60A\U0001F60A Failing policy" + platform: linux + description: This policy should always fail. + resolution: There is no resolution for this policy. + query: SELECT 1 FROM osquery_info WHERE start_time < 0; +software: + packages: + - url: ${SOFTWARE_INSTALLER_URL}/ruby.deb + install_script: + path: lib/install_ruby.sh + pre_install_query: + path: lib/query_ruby.yml + post_install_script: + path: lib/post_install_ruby.sh + uninstall_script: + path: lib/uninstall_ruby.sh + - url: ${SOFTWARE_INSTALLER_URL}/other.deb + self_service: true diff --git a/tools/gitops-auto-complete/types.go b/tools/gitops-auto-complete/types.go new file mode 100644 index 00000000000..321c800dd1a --- /dev/null +++ b/tools/gitops-auto-complete/types.go @@ -0,0 +1,129 @@ +package main + +import ( + "reflect" + "strings" + + "github.com/fleetdm/fleet/v4/pkg/spec" + "github.com/fleetdm/fleet/v4/server/fleet" + "github.com/invopop/jsonschema" +) + +// GitOpsSpec spells out the top-level GitOps keys with Fleet's typed structs, since +// spec.GitOps has no json tags and would reflect to PascalCase keys. +type GitOpsSpec struct { + Name string `json:"name,omitempty"` + OrgSettings *spec.GitOpsOrgSettings `json:"org_settings,omitempty"` + TeamSettings *spec.GitOpsFleetSettings `json:"settings,omitempty"` + AgentOptions *fleet.AgentOptions `json:"agent_options,omitempty"` + Controls ControlsWithTypes `json:"controls"` + Policies []*spec.GitOpsPolicySpec `json:"policies,omitempty"` + Reports []*spec.Query `json:"reports,omitempty"` + Software spec.GitOpsSoftware `json:"software"` + Labels []*fleet.LabelSpec `json:"labels,omitempty"` + CustomHostVitals []spec.GitOpsCustomHostVital `json:"custom_host_vitals,omitempty"` +} + +// ControlsWithTypes covers `controls:` with real types. spec.GitOpsControls types +// most keys as `any` so yamlls can't complete them, and leaks an internal Defined field. +type ControlsWithTypes struct { + AndroidEnabledAndConfigured bool `json:"android_enabled_and_configured"` + WindowsEnabledAndConfigured bool `json:"windows_enabled_and_configured"` + EnableDiskEncryption bool `json:"enable_disk_encryption"` + EnableRecoveryLockPassword bool `json:"enable_recovery_lock_password"` + WindowsRequireBitLockerPIN bool `json:"windows_require_bitlocker_pin"` + + NameTemplate string `json:"name_template"` + + MacOSUpdates *fleet.AppleOSUpdateSettings `json:"macos_updates"` + IOSUpdates *fleet.AppleOSUpdateSettings `json:"ios_updates"` + IPadOSUpdates *fleet.AppleOSUpdateSettings `json:"ipados_updates"` + WindowsUpdates *fleet.WindowsUpdates `json:"windows_updates"` + + MacOSSetup *fleet.MacOSSetup `json:"macos_setup" renameto:"setup_experience"` + AppleAccountProvisioning *fleet.AppleAccountProvisioning `json:"apple_account_provisioning"` + Scripts []fleet.BaseItem `json:"scripts"` + + MacOSSettings *fleet.MacOSSettings `json:"macos_settings" renameto:"apple_settings"` + WindowsSettings *fleet.WindowsSettings `json:"windows_settings"` + AndroidSettings *fleet.AndroidSettings `json:"android_settings"` + + // Remaining keys accept any value for now. + MacOSMigration any `json:"macos_migration"` + WindowsMigrationEnabled any `json:"windows_migration_enabled"` + EnableTurnOnWindowsMDMManually any `json:"enable_turn_on_windows_mdm_manually"` + WindowsEntraTenantIDs any `json:"windows_entra_tenant_ids"` + WindowsEntraClientIDs any `json:"windows_entra_client_ids"` + AppleRequireHardwareAttestation any `json:"apple_require_hardware_attestation"` +} + +func goTypeToJSON(name string) string { + switch name { + case "bool": + return "boolean" + case "string": + return "string" + case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": + return "integer" + case "float32", "float64": + return "number" + } + return "" +} + +func typeMapper(goType reflect.Type) *jsonschema.Schema { + packagePath := goType.PkgPath() + + // json.RawMessage reflects to a bare `true` schema that yamlls won't complete. + // In GitOps these blobs are objects, so type them as such. + if packagePath == "encoding/json" && goType.Name() == "RawMessage" { + return &jsonschema.Schema{Type: "object"} + } + + // fleet.Duration embeds time.Duration, so invopop emits a self-referential $def + // that overflows yamlls' resolver. It marshals to a string like "24h". + if strings.HasSuffix(packagePath, "server/fleet") && goType.Name() == "Duration" { + return &jsonschema.Schema{Type: "string"} + } + + // optjson.Bool/String/Int/Slice[T]/Any[T] marshal to their Value, not the + // internal {Set, Valid, Value} struct. + if strings.Contains(packagePath, "pkg/optjson") { + valueField, ok := goType.FieldByName("Value") + if ok { + return schemaForType(valueField.Type) + } + + // BoolOr[T]/StringOr[T] marshal to a scalar or an object, and their generic + // $def names can't resolve as a $ref, so express both arms as an anyOf. + scalar := "boolean" + _, hasString := goType.FieldByName("String") + if hasString { + scalar = "string" + } + return &jsonschema.Schema{AnyOf: []*jsonschema.Schema{{Type: scalar}, {Type: "object"}}} + } + + return nil +} + +func schemaForType(goType reflect.Type) *jsonschema.Schema { + for goType.Kind() == reflect.Pointer { + goType = goType.Elem() + } + switch goType.Kind() { + case reflect.Bool: + return &jsonschema.Schema{Type: "boolean"} + case reflect.String: + return &jsonschema.Schema{Type: "string"} + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return &jsonschema.Schema{Type: "integer"} + case reflect.Float32, reflect.Float64: + return &jsonschema.Schema{Type: "number"} + case reflect.Slice, reflect.Array: + return &jsonschema.Schema{Type: "array", Items: schemaForType(goType.Elem())} + default: + return &jsonschema.Schema{Type: "object"} + } +} diff --git a/tools/gitops-auto-complete/yamlls_test.go b/tools/gitops-auto-complete/yamlls_test.go new file mode 100644 index 00000000000..241d386d148 --- /dev/null +++ b/tools/gitops-auto-complete/yamlls_test.go @@ -0,0 +1,272 @@ +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "sync" + "testing" + "time" +) + +// TestYAMLLS validates the fixtures against a real yaml-language-server, the actual +// editor target, so the generated schema is checked with the same engine users see. +// It's gated behind YAMLLS_TEST=1 because it needs the yamlls binary (a node +// program). Set YAMLLS_BIN to point at the binary if it isn't on PATH. +// +// The schema is attached per-file with a modeline. Only Error-severity (schema) +// diagnostics are counted, so a deprecation hint on a valid file doesn't fail it. +func TestYAMLLS(t *testing.T) { + if os.Getenv("YAMLLS_TEST") == "" { + t.Skip("set YAMLLS_TEST=1 to validate fixtures against a real yaml-language-server") + } + bin := os.Getenv("YAMLLS_BIN") + if bin == "" { + path, err := exec.LookPath("yaml-language-server") + if err != nil { + t.Fatal("YAMLLS_TEST is set but yaml-language-server is not on PATH; add it to PATH or set YAMLLS_BIN") + } + bin = path + } + schemaPath, err := filepath.Abs(schemaFile) + if err != nil { + t.Fatal(err) + } + + yamlls := startYAMLLS(t, bin) + defer yamlls.close() + + run := func(dir string, wantErrors bool) { + files, _ := filepath.Glob(filepath.Join("testdata", dir, "*.yml")) + for _, file := range files { + t.Run(dir+"/"+filepath.Base(file), func(t *testing.T) { + content, err := os.ReadFile(file) + if err != nil { + t.Fatal(err) + } + doc := "# yaml-language-server: $schema=" + schemaPath + "\n" + string(content) + errs := yamlls.diagnose(t, doc, wantErrors) + switch { + case wantErrors && errs == 0: + t.Errorf("%s: expected yamlls schema errors, got none", file) + case !wantErrors && errs > 0: + t.Errorf("%s: expected no yamlls schema errors, got %d", file, errs) + } + }) + } + } + run("valid", false) + run("invalid", true) +} + +type yamllsClient struct { + cmd *exec.Cmd + in io.WriteCloser + mu sync.Mutex // serializes writes (main + auto-responses from readLoop) + msgs chan map[string]any + id int + uri int +} + +func startYAMLLS(t *testing.T, bin string) *yamllsClient { + t.Helper() + cmd := exec.Command(bin, "--stdio") + cmd.Stderr = os.Stderr + in, err := cmd.StdinPipe() + if err != nil { + t.Fatal(err) + } + out, err := cmd.StdoutPipe() + if err != nil { + t.Fatal(err) + } + if err := cmd.Start(); err != nil { + t.Fatalf("start yaml-language-server: %v", err) + } + c := &yamllsClient{cmd: cmd, in: in, msgs: make(chan map[string]any, 64)} + go c.readLoop(out) + + c.request(t, "initialize", map[string]any{ + "processId": nil, + "rootUri": nil, + "capabilities": map[string]any{ + "textDocument": map[string]any{"publishDiagnostics": map[string]any{}}, + }, + }) + c.notify(t, "initialized", map[string]any{}) + return c +} + +func (c *yamllsClient) close() { + _ = c.in.Close() + _ = c.cmd.Process.Kill() + _ = c.cmd.Wait() +} + +func (c *yamllsClient) write(t *testing.T, m map[string]any) { + t.Helper() + b, _ := json.Marshal(m) + c.mu.Lock() + defer c.mu.Unlock() + if _, err := fmt.Fprintf(c.in, "Content-Length: %d\r\n\r\n%s", len(b), b); err != nil { + t.Fatalf("write lsp message: %v", err) + } +} + +func (c *yamllsClient) notify(t *testing.T, method string, params any) { + c.write(t, map[string]any{"jsonrpc": "2.0", "method": method, "params": params}) +} + +func (c *yamllsClient) request(t *testing.T, method string, params any) { + t.Helper() + c.id++ + id := c.id + c.write(t, map[string]any{"jsonrpc": "2.0", "id": id, "method": method, "params": params}) + timeout := time.After(15 * time.Second) + for { + select { + case m, ok := <-c.msgs: + if !ok { + t.Fatalf("yamlls closed while waiting for %s", method) + } + // A response has an id and no method. + if _, hasMethod := m["method"]; !hasMethod && idOf(m) == id { + return + } + case <-timeout: + t.Fatalf("timeout waiting for %s response", method) + } + } +} + +// diagnose opens a fresh document and returns how many Error-severity diagnostics +// yamlls publishes for it. wantErrors reflects the caller's expectation: yamlls often +// publishes an empty set on didOpen before the schema loads, so when errors are +// expected an empty set is treated as premature and diagnose keeps waiting for the +// real one rather than finalizing early (which would let an invalid fixture pass). +func (c *yamllsClient) diagnose(t *testing.T, doc string, wantErrors bool) int { + t.Helper() + c.uri++ + uri := fmt.Sprintf("file:///tmp/gitops-schema-test-%d.yaml", c.uri) + c.notify(t, "textDocument/didOpen", map[string]any{ + "textDocument": map[string]any{"uri": uri, "languageId": "yaml", "version": 1, "text": doc}, + }) + + var latest []any + published := false + hard := time.After(15 * time.Second) + for { + var quiet <-chan time.Time + // Start the quiet countdown only once the result is trustworthy: a valid + // file's empty set is authoritative immediately, but when errors are expected + // only a set that actually contains errors is. + if published && (!wantErrors || countErrors(latest) > 0) { + quiet = time.After(1 * time.Second) + } + select { + case m, ok := <-c.msgs: + if !ok { + t.Fatal("yamlls closed while waiting for diagnostics") + } + if m["method"] == "textDocument/publishDiagnostics" { + if p, _ := m["params"].(map[string]any); p != nil && p["uri"] == uri { + latest, _ = p["diagnostics"].([]any) + published = true + } + } + case <-quiet: + return countErrors(latest) + case <-hard: + if !published { + t.Fatal("timed out waiting for yamlls diagnostics") + } + return countErrors(latest) + } + } +} + +func (c *yamllsClient) readLoop(r io.Reader) { + br := bufio.NewReader(r) + for { + length := 0 + for { + line, err := br.ReadString('\n') + if err != nil { + close(c.msgs) + return + } + line = strings.TrimRight(line, "\r\n") + if line == "" { + break + } + if strings.HasPrefix(strings.ToLower(line), "content-length:") { + length, _ = strconv.Atoi(strings.TrimSpace(line[len("content-length:"):])) + } + } + if length == 0 { + continue + } + body := make([]byte, length) + if _, err := io.ReadFull(br, body); err != nil { + close(c.msgs) + return + } + var m map[string]any + if json.Unmarshal(body, &m) != nil { + continue + } + // Auto-respond to server->client requests (method + id) so yamlls doesn't + // block. Forward responses and notifications to the channel. + if _, hasMethod := m["method"]; hasMethod { + if _, hasID := m["id"]; hasID { + c.respond(m) + continue + } + } + c.msgs <- m + } +} + +// respond returns a null (or empty-array for workspace/configuration) result to a +// server-initiated request. +func (c *yamllsClient) respond(req map[string]any) { + var result any + if req["method"] == "workspace/configuration" { + items := 0 + if p, _ := req["params"].(map[string]any); p != nil { + if arr, _ := p["items"].([]any); arr != nil { + items = len(arr) + } + } + result = make([]any, items) + } + b, _ := json.Marshal(map[string]any{"jsonrpc": "2.0", "id": req["id"], "result": result}) + c.mu.Lock() + defer c.mu.Unlock() + _, _ = fmt.Fprintf(c.in, "Content-Length: %d\r\n\r\n%s", len(b), b) +} + +func idOf(m map[string]any) int { + if f, ok := m["id"].(float64); ok { + return int(f) + } + return -1 +} + +func countErrors(diags []any) int { + count := 0 + for _, diag := range diags { + if fields, ok := diag.(map[string]any); ok { + if sev, ok := fields["severity"].(float64); ok && sev == 1 { + count++ + } + } + } + return count +}