Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ img/ optional images
kibana/ optional
tags.yml optional schema:integration/kibana/tags.spec.yml
dashboard/*.json optional opaque JSON saved objects
search/*.json optional " (3.6.6+)
security_ai_prompt/*.json optional "
security_rule/*.json optional "
alerting_rule_template/*.json optional "
Expand Down
1 change: 1 addition & 0 deletions cmd/generate/augment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ base_types:
- deprecated
- description
- format_version
- group
- icons
- name
- owner
Expand Down
4 changes: 4 additions & 0 deletions pkgspec/manifest.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

160 changes: 160 additions & 0 deletions pkgspec/pkgspec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,163 @@ description: A test field.
t.Errorf("path = %q, want test.yml", field.FilePath())
}
}

func TestUnmarshalManifestGroup(t *testing.T) {
// group is a top-level field on integration, input, and content manifests
// (package-spec 3.6.6). It is lifted into the shared Manifest base type,
// so it must be populated on the promoted embedded struct.
tests := []struct {
name string
yamlData string
manifest func() (*Manifest, any)
}{
{
name: "integration",
yamlData: `name: nginx
title: Nginx
version: 1.0.0
description: Nginx integration.
format_version: 3.6.6
type: integration
group: nginx
owner:
github: elastic/integrations
type: elastic
`,
manifest: func() (*Manifest, any) {
var m IntegrationManifest
return &m.Manifest, &m
},
},
{
name: "input",
yamlData: `name: udp
title: Custom UDP Logs
version: 1.0.0
description: Custom UDP input.
format_version: 3.6.6
type: input
group: redis
owner:
github: elastic/integrations
type: elastic
`,
manifest: func() (*Manifest, any) {
var m InputManifest
return &m.Manifest, &m
},
},
{
name: "content",
yamlData: `name: security_rules
title: Security Rules
version: 1.0.0
description: Content package.
format_version: 3.6.6
type: content
group: security_rules
owner:
github: elastic/security
type: elastic
`,
manifest: func() (*Manifest, any) {
var m ContentManifest
return &m.Manifest, &m
},
},
}

want := map[string]string{
"integration": "nginx",
"input": "redis",
"content": "security_rules",
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
base, target := tt.manifest()
if err := yaml.Unmarshal([]byte(tt.yamlData), target); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if base.Group != want[tt.name] {
t.Errorf("group = %q, want %q", base.Group, want[tt.name])
}
})
}
}

func TestUnmarshalManifestGroupAbsent(t *testing.T) {
yamlData := `name: nginx
title: Nginx
version: 1.0.0
description: Nginx integration.
format_version: 3.6.6
type: integration
owner:
github: elastic/integrations
type: elastic
`
var m IntegrationManifest
if err := yaml.Unmarshal([]byte(yamlData), &m); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if m.Group != "" {
t.Errorf("group = %q, want empty", m.Group)
}
}

func TestUnmarshalTestConfigPolicyIgnoreFields(t *testing.T) {
// Policy tests use a dedicated config schema that adds ignore_fields
// (package-spec 3.6.6). Other test categories do not have the field.
yamlData := `system:
parallel: false
policy:
parallel: true
ignore_fields:
- state.user_agent
- state.cursor.last_timestamp
skip:
reason: flaky
link: https://github.com/elastic/integrations/issues/1
`
var cfg TestConfig
if err := yaml.Unmarshal([]byte(yamlData), &cfg); err != nil {
t.Fatalf("unmarshal: %v", err)
}

wantIgnore := []string{"state.user_agent", "state.cursor.last_timestamp"}
if len(cfg.Policy.IgnoreFields) != len(wantIgnore) {
t.Fatalf("ignore_fields = %v, want %v", cfg.Policy.IgnoreFields, wantIgnore)
}
for i, want := range wantIgnore {
if cfg.Policy.IgnoreFields[i] != want {
t.Errorf("ignore_fields[%d] = %q, want %q", i, cfg.Policy.IgnoreFields[i], want)
}
}

if cfg.Policy.Parallel == nil || !*cfg.Policy.Parallel {
t.Errorf("policy.parallel = %v, want true", cfg.Policy.Parallel)
}
if cfg.Policy.Skip.Reason != "flaky" {
t.Errorf("policy.skip.reason = %q, want flaky", cfg.Policy.Skip.Reason)
}
if cfg.System.Parallel == nil || *cfg.System.Parallel {
t.Errorf("system.parallel = %v, want false", cfg.System.Parallel)
}
}

func TestUnmarshalInputTestConfigPolicyIgnoreFields(t *testing.T) {
// Input packages share the policy test config schema (package-spec 3.6.6).
yamlData := `policy:
ignore_fields:
- state.user_agent
`
var cfg InputTestConfig
if err := yaml.Unmarshal([]byte(yamlData), &cfg); err != nil {
t.Fatalf("unmarshal: %v", err)
}

if len(cfg.Policy.IgnoreFields) != 1 || cfg.Policy.IgnoreFields[0] != "state.user_agent" {
t.Errorf("ignore_fields = %v, want [state.user_agent]", cfg.Policy.IgnoreFields)
}
}
4 changes: 2 additions & 2 deletions pkgspec/test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions pkgspec/types.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkgspec/version.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 69 additions & 0 deletions pkgsql/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ version: 1.0.0
description: A test package.
format_version: 3.5.7
type: integration
group: test_group
owner:
github: elastic/integrations
type: elastic
Expand Down Expand Up @@ -283,6 +284,17 @@ samples:
t.Errorf("got name=%s version=%s type=%s", name, version, pkgType)
}

// Verify marketplace group. The column name is a SQLite keyword so it
// must be quoted.
var group sql.NullString
err = db.QueryRowContext(ctx, `SELECT "group" FROM packages WHERE name = 'test-package'`).Scan(&group)
if err != nil {
t.Fatalf("querying group: %v", err)
}
if !group.Valid || group.String != "test_group" {
t.Errorf("expected group=test_group, got %v", group)
}

// Verify conditions.
var condKibana, condElastic sql.NullString
err = db.QueryRowContext(ctx, "SELECT conditions_kibana_version, conditions_elastic_subscription FROM packages WHERE name = 'test-package'").
Expand Down Expand Up @@ -595,6 +607,16 @@ policy_templates:
t.Fatalf("writing packages: %v", err)
}

// The manifest omits group, so the column must be NULL rather than "".
var group sql.NullString
err = db.QueryRowContext(ctx, `SELECT "group" FROM packages`).Scan(&group)
if err != nil {
t.Fatalf("querying group: %v", err)
}
if group.Valid {
t.Errorf("expected group=NULL, got %v", group)
}

// Verify images were inserted.
var imgCount int
err = db.QueryRowContext(ctx, "SELECT count(*) FROM images").Scan(&imgCount)
Expand Down Expand Up @@ -660,6 +682,7 @@ version: 1.0.0
description: A test input package.
format_version: 3.5.7
type: input
group: input_group
categories:
- custom
conditions:
Expand Down Expand Up @@ -724,6 +747,16 @@ owner:
t.Errorf("expected type=input, got %s", pkgType)
}

// Verify marketplace group.
var group sql.NullString
err = db.QueryRowContext(ctx, `SELECT "group" FROM packages WHERE name = 'test-input'`).Scan(&group)
if err != nil {
t.Fatalf("querying group: %v", err)
}
if !group.Valid || group.String != "input_group" {
t.Errorf("expected group=input_group, got %v", group)
}

// Verify policy template was inserted.
var ptCount int
err = db.QueryRowContext(ctx, "SELECT count(*) FROM policy_templates").Scan(&ptCount)
Expand Down Expand Up @@ -824,6 +857,7 @@ version: 1.0.0
description: A test content package.
format_version: 3.5.7
type: content
group: content_group
owner:
github: elastic/security
type: elastic
Expand All @@ -844,6 +878,15 @@ discovery:
type: enhancement
link: https://github.com/test/1
`)},
"kibana/search/test-content-alerts.json": {Data: []byte(`{
"id": "test-content-alerts",
"type": "search",
"attributes": {
"title": "Alert Events",
"description": "Saved search over alert events."
},
"references": []
}`)},
}

pkg, err := pkgreader.Read(".", pkgreader.WithFS(fsys))
Expand All @@ -869,6 +912,32 @@ discovery:
t.Errorf("expected type=content, got %s", pkgType)
}

// Verify marketplace group.
var group sql.NullString
err = db.QueryRowContext(ctx, `SELECT "group" FROM packages WHERE name = 'test-content'`).Scan(&group)
if err != nil {
t.Fatalf("querying group: %v", err)
}
if !group.Valid || group.String != "content_group" {
t.Errorf("expected group=content_group, got %v", group)
}

// Verify saved search assets, which content packages may ship as of
// package-spec 3.6.6.
var searchAssetType, searchObjectID, searchTitle string
err = db.QueryRowContext(ctx,
"SELECT asset_type, object_id, title FROM kibana_saved_objects WHERE asset_type = 'search'").
Scan(&searchAssetType, &searchObjectID, &searchTitle)
if err != nil {
t.Fatalf("querying saved search: %v", err)
}
if searchObjectID != "test-content-alerts" {
t.Errorf("expected object_id=test-content-alerts, got %s", searchObjectID)
}
if searchTitle != "Alert Events" {
t.Errorf("expected title=Alert Events, got %s", searchTitle)
}

// Verify conditions.
var condKibana, condElastic sql.NullString
err = db.QueryRowContext(ctx, "SELECT conditions_kibana_version, conditions_elastic_subscription FROM packages WHERE name = 'test-content'").
Expand Down
1 change: 1 addition & 0 deletions pkgsql/insert.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkgsql/internal/db/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkgsql/internal/db/query.sql
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ INSERT INTO packages (
file_column,
description,
format_version,
"group",
name,
owner_github,
owner_type,
Expand Down Expand Up @@ -127,6 +128,7 @@ INSERT INTO packages (
?,
?,
?,
?,
?
) RETURNING id;

Expand Down
Loading
Loading