x-pack/osquerybeat/ext/osquery-extension/cmd/gentables/examples/README.md
This directory contains sample table and view specifications to demonstrate the gentables code generator.
tables/sample_table.yaml - Example table specification with multiple columns and comprehensive documentationviews/sample_view.yaml - Example view specification demonstrating UNION ALL, CASE expressions, and date calculationsNote: The generator accepts both .yaml and .yml file extensions. You can use whichever you prefer, and both can coexist in the same directory.
To test the generator with these samples, you have two options:
cd /path/to/cmd/gentables
go run . \
-spec-dir examples/tables,examples/views \
-out-dir examples/tables/generated \
-docs-dir examples/docs \
-views-out-dir examples/views/generated \
-views-docs-dir examples/docs \
-verbose
cd /path/to/cmd/gentables/examples
go run ../main.go \
-spec-dir tables,views \
-out-dir tables/generated \
-docs-dir tables/docs \
-views-out-dir views/generated \
-views-docs-dir views/docs \
-verbose
The generator will create:
tables/generated/ - Table code packages
sample_custom_table/sample_custom_table.go - Generated table coderegistry_*.go - Platform-specific registriesviews/generated/ - View code packages
sample_combined_resources/sample_combined_resources.go - Generated view coderegistry_*.go - Platform-specific registriestables/docs/ - Table documentation
sample_custom_table.md - Generated table documentationviews/docs/ - View documentation
sample_combined_resources.md - Generated view documentationNote: Import paths in the generated platform files are calculated automatically based on the output directory locations and the module path detected from go.mod.
Tables and views use identical YAML format, differentiated only by the type field and the presence of the query field for views.
type: table|view # Required: "table" or "view"
name: spec_name # Required: table or view name
description: Brief description # Required: brief description
platforms: [linux, darwin, windows] # Optional: defaults to all platforms
implementation_package: pkg/path # Required for tables: import path of package that registers this table
group: my_group # Optional: scopes shared types for this spec
columns: # Required: column definitions
- name: column_name # Required: column name
type: TEXT|INTEGER|BIGINT|DOUBLE # Required: osquery column type
description: Column description # Required: column description
go_type: string|int32|int64|float64|time.Time # Optional: override Go type
format: unix|rfc3339 # Optional: format hint for struct tags
timezone: UTC # Optional: timezone hint for struct tags
documentation: # Required: documentation
description: Detailed description # Required: detailed description
examples: # Required: at least one SQL query example
- title: Example title
query: SELECT * FROM spec_name;
notes: # Required: at least one note
- Note text
related_tables: # Optional: defaults to empty list
- other_table
# View-specific fields:
required_tables: # Optional: tables this view depends on
- table_name
query: | # Required for views only
SELECT column_name FROM table_name;
type: table): Must NOT have a query fieldtype: view): Must have a query field containing only the SELECT statement(s)Note: The query field should contain only the SELECT statement(s). The tool automatically wraps it with CREATE VIEW ... AS in the generated code.
The generator creates individual packages for each table/view with better encapsulation:
pkg/tables/
├── registry.go # STATIC - registry of all tables
└── generated/
└── sample_custom_table/ # Directory: descriptive with underscores
└── sample_custom_table.go # Package: samplecustomtable (idiomatic)
pkg/views/
├── registry.go # STATIC - registry of all views
└── generated/
└── sample_combined_resources/ # Directory: descriptive with underscores
└── sample_combined_resources.go # Package: samplecombinedresources (idiomatic)
Package Naming Convention:
sample_custom_table)samplecustomtable)This follows Go best practices where package names should be short, lowercase, and without underscores, while directory names can remain descriptive.
Each table/view package includes:
Result struct with osquery tagsColumns() function (tables) or View() function (views)TableName constant (tables only)// Import using descriptive directory path, idiomatic package alias
import samplecustomtable "github.com/.../pkg/tables/generated/sample_custom_table"
// Access the types and functions
var result samplecustomtable.Result
columns := samplecustomtable.Columns()
name := samplecustomtable.TableName
Registry registration is automatic via generated registry files:
pkg/tables/generated/registry.gopkg/views/generated/registry.goinit() functions or manual registration neededEvery table must specify implementation_package: the import path of the Go package that registers the table (via RegisterGenerateFunc() in init()). This guarantees each table has a single, explicit registration point.
Example 1 – dedicated implementation package (implementation lives in a separate package):
type: table
name: my_table
platforms: [linux, darwin, windows]
implementation_package: github.com/elastic/beats/v7/x-pack/osquerybeat/ext/osquery-extension/pkg/myimpl
columns:
- name: id
type: BIGINT
description: Unique identifier
# ... more columns
Example 2 – implementation in generated package (implementation lives alongside generated code in the same package):
type: table
name: sample_jumplists
group: jumplists
implementation_package: github.com/elastic/beats/v7/x-pack/osquerybeat/ext/osquery-extension/pkg/tables/generated/jumplists/sample_jumplists
columns:
# ...
How it works:
implementation_package in registry_linux.go, etc.init() calls RegisterGenerateFunc()registry.go calls all table registrationstables.RegisterTables() and views.RegisterViews()The generator automatically creates Result structs with osquery tags for proper serialization:
All columns get an osquery tag with their column name:
type Result struct {
Id int64 `osquery:"id"` // Basic column
Name string `osquery:"name"` // Text column
}
Use the optional format and timezone fields in your column specs to add additional tags:
YAML Spec:
columns:
- name: created_time
type: BIGINT
description: Creation timestamp
format: unix # Adds format:"unix" tag
timezone: UTC # Adds tz:"UTC" tag
Generated Go:
type Result struct {
CreatedTime int64 `osquery:"created_time" format:"unix" tz:"UTC"`
}
unix - UNIX epoch timestamp (seconds since 1970-01-01)rfc3339 - RFC3339 formatted timestamp (ISO 8601)UTC - Coordinated Universal TimeAmerica/New_York)These tags are used by the osquery encoding package for proper serialization and deserialization of query results.
For timestamp fields, you can use go_type: time.Time to generate time.Time fields instead of int64/string:
YAML Spec:
columns:
- name: timestamp
type: BIGINT
description: Event timestamp
go_type: time.Time # Override default int64 with time.Time
format: unix
timezone: UTC
Generated Go:
type Result struct {
Timestamp time.Time `osquery:"timestamp" format:"unix" tz:"UTC"`
}
The encoding package automatically converts between time.Time and the appropriate format based on the tags.
Both tables and views must document their columns in the spec. For views, this is especially important because:
The columns field must list all columns that the view returns, matching the SELECT statement in the query.
The generator uses pingcap SQL parser to validate view specifications:
SELECT * skip validation with a warning (assumes columns are correct)Alias Requirements:
The following do NOT require an AS alias:
SELECT id, name FROM tableSELECT t.id, t.name FROM table tSELECT * or SELECT t.*The following REQUIRE an AS alias:
COUNT(*) → COUNT(*) AS total_countUPPER(name) → UPPER(name) AS name_upperCASE WHEN ... END → CASE WHEN ... END AS status_labelvalue * 2 → value * 2 AS doubled_valueHow it works:
Advantages:
Note:
query field should contain ONLY the SELECT statementCREATE VIEW view_name AS when generating codeThe generator applies sensible defaults for commonly omitted fields:
platforms: Defaults to ["linux", "darwin", "windows"] if not specifieddocumentation.related_tables: Defaults to empty array if not specifiedThis allows you to write minimal specs for cross-platform tables/views without repetitive boilerplate.
type - Must be "table" or "view"name - Spec namedescription - Brief descriptioncolumns - At least one column with:
name - Column nametype - Column type (TEXT, INTEGER, BIGINT, DOUBLE, BOOLEAN)description - Column descriptiondocumentation.description - Detailed descriptiondocumentation.examples - At least one example querydocumentation.notes - At least one noteimplementation_package - Import path of the package that registers this table (via RegisterGenerateFunc() in init())platforms - Defaults to ["linux", "darwin", "windows"]columns[].go_type - Explicit Go type override (for example, "time.Time")columns[].format - Format hint for osquery tags (for example, "unix", "rfc3339")columns[].timezone - Timezone hint for osquery tags (for example, "UTC")documentation.related_tables - Defaults to empty arrayrequired_tables - Only applies to views; optionalgroup - Required if shared_types is setquery - Must contain SELECT statement(s) onlyquery - Must NOT be presentThese examples are for demonstration purposes only and are not processed by the actual build. They are kept separate from the real table/view specs in ../../specs/.