Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
78b6c7d
Add validation for data stream template paths
teresaromero Sep 26, 2025
6b5db09
Add new testdata packages for stream template validation
teresaromero Sep 26, 2025
78ebe46
Add validation for stream templates and corresponding tests
teresaromero Sep 26, 2025
f09e0d6
Add validation for input and integration policy template paths with c…
teresaromero Sep 26, 2025
84cf7b1
Fix policy template validation to support nested inputs and improve e…
teresaromero Sep 26, 2025
45f462b
Add testdata for input and integration policy templates
teresaromero Sep 26, 2025
cd6c408
Add tests for input and integration policy template validation, inclu…
teresaromero Sep 26, 2025
bc144d4
Format imports with goimports
teresaromero Sep 26, 2025
edbba40
Move validation test from spec to validator. Move testdata to test/pa…
teresaromero Sep 26, 2025
5045d28
fix test maifest and spec example to align to validation path
teresaromero Sep 29, 2025
423793f
Refactor validateDataStreamManifestTemplates to simplify parameters b…
teresaromero Sep 29, 2025
fb191e2
Improve error messaging in template path validation by using %w for w…
teresaromero Sep 29, 2025
526c665
Update error messages in template path validation tests for clarity a…
teresaromero Sep 29, 2025
a78f6c2
Add validation for template_path in policy templates and data streams
teresaromero Sep 29, 2025
4fa3db6
Add validation for `template_path` in policy templates and data strea…
teresaromero Sep 30, 2025
364d708
Refactor template path handling in validation functions to use filepa…
teresaromero Sep 30, 2025
6e35950
Update tests to use filepath
teresaromero Sep 30, 2025
0d3e22c
filepath join at tests
teresaromero Sep 30, 2025
9dca430
Refactor tests to use os and filepath for directory and file handling…
teresaromero Sep 30, 2025
704aef2
Reorder import statements
teresaromero Sep 30, 2025
801b990
Refactor error handling in template validation functions to use prede…
teresaromero Oct 1, 2025
741ffb0
Merge branch 'main' into 703-stream-yml-hbs-exists
teresaromero Oct 1, 2025
8f47f3c
remove changelog template_path quotes
teresaromero Oct 1, 2025
9d3e1dd
Comment out validation rules and test cases for stream and input poli…
teresaromero Oct 1, 2025
316dbd3
Refactor path handling in validation functions to use path.Join inste…
teresaromero Oct 1, 2025
13f5ae8
Uncomment validation rules and test cases for stream and input policy…
teresaromero Oct 1, 2025
cfa7087
Fix changelog entry order
teresaromero Oct 1, 2025
19bf9da
Add validation for default template paths in data stream tests
teresaromero Oct 3, 2025
731a589
Rename validation function for agent input template paths and update …
teresaromero Oct 3, 2025
807f407
Add stream template files for data stream agent validation tests
teresaromero Oct 3, 2025
b2be7dd
Refactor error handling in validateAgentInputTemplatePath to differen…
teresaromero Oct 3, 2025
0882440
Change specs with required fields and json patch
teresaromero Oct 3, 2025
a6379bf
Update validation rules and manifest files for version 3.6.0 complian…
teresaromero Oct 3, 2025
bfe466b
revert changes on old test packages
teresaromero Oct 3, 2025
53ccaf3
recover deleted file
teresaromero Oct 3, 2025
f1369eb
Revert "revert changes on old test packages"
teresaromero Oct 3, 2025
d0dd5d8
revert version limit
teresaromero Oct 6, 2025
6ee279c
fix test cases
teresaromero Oct 6, 2025
4da74b3
fix testdata with template_paths
teresaromero Oct 7, 2025
5d1ac9c
Revert "fix testdata with template_paths"
teresaromero Oct 7, 2025
616761c
Revert "fix test cases"
teresaromero Oct 7, 2025
7b474a7
revert required field at integrations, fix required validation for in…
teresaromero Oct 7, 2025
818eff5
change streams template_path validation, use walkdir with endsWith logic
teresaromero Oct 8, 2025
86fec86
Merge branch 'main' into 703-stream-yml-hbs-exists
teresaromero Oct 14, 2025
84d2161
changelog move template_path validation entry to 3.5.1-next
teresaromero Oct 15, 2025
a84f9c7
Rename defaultTemplatePath to defaultStreamTemplatePath for clarity
teresaromero Oct 15, 2025
fd34e79
Change fs.SkipDir to fs.SkipAll to stop directory traversal once a ma…
teresaromero Oct 15, 2025
11efa5d
Fix error handling in validateDataStreamManifestTemplates for directo…
teresaromero Oct 15, 2025
8ec5de0
Update link in comment to point to the correct commit in Kibana repos…
teresaromero Oct 15, 2025
a3b56e5
Refactor policy template validation: consolidate ValidateStreamTempla…
teresaromero Oct 16, 2025
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
197 changes: 197 additions & 0 deletions code/go/internal/validator/semantic/validate_policy_template_path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package semantic

import (
"errors"
"fmt"
"io/fs"
"os"
"path"
"strings"

"gopkg.in/yaml.v3"

"github.com/elastic/package-spec/v3/code/go/internal/fspath"
"github.com/elastic/package-spec/v3/code/go/pkg/specerrors"
)

const (
defaultStreamTemplatePath = "stream.yml.hbs"
)

var (
errRequiredTemplatePath = errors.New("template_path is required for input type packages")
errFailedToReadManifest = errors.New("failed to read manifest")
errFailedToParseManifest = errors.New("failed to parse manifest")
errTemplateNotFound = errors.New("template file not found")
)

type policyTemplateInput struct {
Type string `yaml:"type"`
TemplatePath string `yaml:"template_path"` // optional for integration packages
}

type policyTemplate struct {
Name string `yaml:"name"`
TemplatePath string `yaml:"template_path"` // input type packages require this field
Inputs []policyTemplateInput `yaml:"inputs"` // integration type packages
}

type packageManifest struct { // package manifest
Type string `yaml:"type"` // integration or input
PolicyTemplates []policyTemplate `yaml:"policy_templates"`
}

type stream struct {
Input string `yaml:"input"`
TemplatePath string `yaml:"template_path"`
}

type streamManifest struct {
Streams []stream `yaml:"streams"`
}

// ValidatePolicyTemplates validates that all referenced template_path files exist for integration and input policy templates
func ValidatePolicyTemplates(fsys fspath.FS) specerrors.ValidationErrors {
var errs specerrors.ValidationErrors

manifestPath := "manifest.yml"
data, err := fs.ReadFile(fsys, manifestPath)
if err != nil {
return specerrors.ValidationErrors{
specerrors.NewStructuredErrorf("file \"%s\" is invalid: %ww", fsys.Path(manifestPath), errFailedToReadManifest)}
}

var manifest packageManifest
err = yaml.Unmarshal(data, &manifest)
if err != nil {
return specerrors.ValidationErrors{
specerrors.NewStructuredErrorf("file \"%s\" is invalid: %w", fsys.Path(manifestPath), errFailedToParseManifest)}
}

for _, policyTemplate := range manifest.PolicyTemplates {
switch manifest.Type {
case "integration":
err := validateIntegrationPackagePolicyTemplate(fsys, policyTemplate)
if err != nil {
errs = append(errs, specerrors.NewStructuredErrorf(
"file \"%s\" is invalid: policy template \"%s\" references input template_path: %w",
fsys.Path(manifestPath), policyTemplate.Name, err))
}
case "input":
err := validateInputPackagePolicyTemplate(fsys, policyTemplate)
if err != nil {
errs = append(errs, specerrors.NewStructuredErrorf(
"file \"%s\" is invalid: policy template \"%s\" references template_path \"%s\": %w",
fsys.Path(manifestPath), policyTemplate.Name, policyTemplate.TemplatePath, err))
}
}
}

return errs
}

// validateInputPackagePolicyTemplate validates the template_path at the policy template level for input type packages
// if the template_path is empty, it returns an error as this field is required for input type packages
func validateInputPackagePolicyTemplate(fsys fspath.FS, policyTemplate policyTemplate) error {
if policyTemplate.TemplatePath == "" {
return errRequiredTemplatePath
}
return validateAgentInputTemplatePath(fsys, policyTemplate.TemplatePath)
}

func validateAgentInputTemplatePath(fsys fspath.FS, tmplPath string) error {
templatePath := path.Join("agent", "input", tmplPath)
_, err := fs.Stat(fsys, templatePath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return errTemplateNotFound
}
return fmt.Errorf("failed to stat template file %s: %w", fsys.Path(templatePath), err)
}

return nil
}

// validateIntegrationPackagePolicyTemplate validates the template_path at the inputs level for integration type packages
// if the template_path is empty, it looks up at the data stream manifest for the stream input that matches the input type of the policy template
// and uses its template_path to look for the corresponding template file at the data stream stream directory
// if no matching stream input is found, it returns an error as at least one stream input must match the input type of the policy template
// if a matching stream input is found but its template_path file does not exist, it returns an error
func validateIntegrationPackagePolicyTemplate(fsys fspath.FS, policyTemplate policyTemplate) error {
for _, input := range policyTemplate.Inputs {
if input.TemplatePath != "" {
err := validateAgentInputTemplatePath(fsys, input.TemplatePath)
if err != nil {
return err
}
continue
}

var found bool
// when an input.TemplatePath is empty, lookup at the data stream manifest
err := fs.WalkDir(
fsys,
path.Join("data_stream"),
func(p string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if found {
return fs.SkipAll
}
// read the data stream manifest and look for the stream input that matches the input.type of the policy template
if !d.IsDir() && d.Name() == "manifest.yml" {
data, err := fs.ReadFile(fsys, p)
if err != nil {
return err
}
var sm streamManifest
err = yaml.Unmarshal(data, &sm)
if err != nil {
return err
}
for _, stream := range sm.Streams {
// skip if the stream input type does not match the policy template input type
if stream.Input == input.Type {
streamName := path.Base(path.Dir(p))
// as template_path is optional at the stream level, default to "stream.yml.hbs" if not set
templatePath := stream.TemplatePath
if templatePath == "" {
templatePath = defaultStreamTemplatePath
}

// look for the template_path file at the data stream stream directory
err := fs.WalkDir(
fsys,
path.Join("data_stream", streamName, "agent", "stream"),
func(p string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() && d.Name() != "" && strings.HasSuffix(d.Name(), templatePath) {
found = true
return fs.SkipAll
}
return nil
})
if err != nil {
return err
}
}
}
}
return nil
})
if err != nil {
return err
}
if !found {
return errTemplateNotFound
}
}
return nil
}
Loading