Skip to content

Conversation

@teresaromero
Copy link
Contributor

@teresaromero teresaromero commented Oct 31, 2025

What does this PR do?

Includes a fix at the integration packages template path validation, taking into account the existence of linked files.
A template can be shared accross data streams by using a shared file and at the agent/stream file, place a .link.

The validation previously implemented did not take into account this occurence, as the iterator was looking for specific file names. It has been spotted while running the integration tests with elastic-package, as the test case for with_links package was correctly mapped.

Why it was not detected at package-spec tests? The package representing a package with link files was not taking into account that streams are linked with the manifest policy template declaration. This PR modifies the package so the test is reliable and spots this occurance.

Why is it important?

When linked files are used to share data streams templates, the validation should be able to check if the file exists, and the final "included" file exists too.

The fs handler already has embedded the link files system, so when a file is .link it looks for the included directly.

l, err := NewLinkedFile(pathName)

Checklist

tested replacing package-spec at elastic-package and testing the error

replace github.com/elastic/package-spec/v3 v3.5.0 => ../package-spec

Related issues

Related #703
Bug introduced at #1002

@teresaromero teresaromero requested a review from a team as a code owner October 31, 2025 10:32
@teresaromero
Copy link
Contributor Author

Shall this also be check at input packages? 🤔 can the template be a link file at agent/input?

I can't seem to find in the spec the link allowance parameter for inputs. The spec does not have an "agent" folder

@mrodm
Copy link
Contributor

mrodm commented Oct 31, 2025

Shall this also be check at input packages? 🤔 can the template be a link file at agent/input?

I can't seem to find in the spec the link allowance parameter for inputs. The spec does not have an "agent" folder

Input packages use the same definition for agent folder as the integration packages:

- description: Folder containing agent-related definitions
type: folder
name: agent
required: true
$ref: "../integration/agent/spec.yml"

And in that file it is allowed link files:

- description: Config template file for inputs defined in the policy_templates section of the top level manifest
type: file
sizeLimit: 2MB
pattern: '^.+\.yml\.hbs$'
required: true
allowLink: true

So, it should be also added for input packages.


// findPathWithPattern looks for a file matching the templatePath in the given data stream directory
// It checks for exact matches, files ending with the templatePath, or templatePath + ".link"
func findPathWithPattern(fsys fspath.FS, dsDir, templatePath string) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option here is using directly the fsys that will iterate over the linked files already "rendered".

func ValidateFromFS(location string, fsys fs.FS) error {
// If we are not explicitly using the linkedfiles.FS, we wrap fsys with
// a linkedfiles.BlockFS to block the use of linked files.
if _, ok := fsys.(*linkedfiles.FS); !ok {
fsys = linkedfiles.NewBlockFS(fsys)
}
pkg, err := packages.NewPackageFromFS(location, fsys)
if err != nil {
return err
}

So maybe it could be used fs.WalkDir and look for the template paths without checking explictely for *.link. Maybe something like this (it needs to be tested)?

func findPathWithPattern(fsys fspath.FS, dsDir, templatePath string) (string, error) {
	mainPath := path.Join(dsDir, "agent", "stream")

	foundFile := ""
	err := fs.WalkDir(fsys, mainPath, func(p string, d fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if d.IsDir() {
			return nil
		}
		base := filepath.Base(p)
		if base == templatePath || strings.HasSuffix(p, templatePath) {
			foundFile = base
			return fs.SkipAll // found a match, stop walking
		}
		return nil
	})
	if err != nil {
		return "", err
	}

	if foundFile == "" {
		return "", errTemplateNotFound
	}

	return foundFile, ""
}

As a advantage, there is no need to check *.link files.
As a disadvantage, it requires to iterate over all files in the directory.

WDYT @jsoriano @teresaromero ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checked locally and my proposed solution here does not work as I expected.

It should be added also more conditions to that if in order to work successfully:

		if base == templatePath || base == templatePath+".link" || strings.HasSuffix(match, templatePath) || strings.HasSuffix(match, templatePath+".link") {
			foundFile = match
			break
		}

So there is no advantage of using WalkDir here.
The current implementation based on fs.Glob could be kept 👍

}

_, err := fs.ReadFile(fsys, path.Join(dsDir, "agent", "stream", stream.TemplatePath))
foundFile, err := findPathWithPattern(fsys, dsDir, stream.TemplatePath)
Copy link
Contributor

@mrodm mrodm Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like that these changes should be added also into validateAgentInputTemplatePath:

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
}

since that path also allow linked files:

- description: Folder containing input definitions
type: folder
name: input
required: true
additionalContents: false
contents:
- description: Config template file for inputs defined in the policy_templates section of the top level manifest
type: file
sizeLimit: 2MB
pattern: '^.+\.yml\.hbs$'
required: true
allowLink: true

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the PR with this change. Made the finder function more broad with the dir param instead of data stream dir. Updated also test cases


// findPathWithPattern looks for a file matching the templatePath in the given data stream directory
// It checks for exact matches, files ending with the templatePath, or templatePath + ".link"
func findPathWithPattern(fsys fspath.FS, dsDir, templatePath string) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checked locally and my proposed solution here does not work as I expected.

It should be added also more conditions to that if in order to work successfully:

		if base == templatePath || base == templatePath+".link" || strings.HasSuffix(match, templatePath) || strings.HasSuffix(match, templatePath+".link") {
			foundFile = match
			break
		}

So there is no advantage of using WalkDir here.
The current implementation based on fs.Glob could be kept 👍

Comment on lines 196 to 199
if strings.HasSuffix(match, templatePath) {
foundFile = match
break
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be added a new if to validate whether or not it exists a template path with the suffix templatePath+".link" too.

In the with_links test package from elastic-package, for instance there could be a file named:

test/packages/other/with_links/data_stream/first/agent/stream/filestream.yml.hbs.link

And this implementation would fail as:

Error: checking package failed: linting package failed: found 1 validation error:
   1. file "/home/user/Coding/work/elastic-package-tere/test/packages/other/with_links/manifest.yml" is invalid: policy template "sample" references input template_path: error validating input from streams "logfile": template file not found

You could create a new variable templatePathWithLink or similar to store the path adding the extension .link.

	var foundFile string
	templatePathWithLink := fmt.Sprintf("%s.link", templatePath)
	for _, match := range matches {
		base := filepath.Base(match)
		if base == templatePath || base == templatePathWithLink {
			foundFile = match
			break
		}
		// fallback to check for suffix match, in case the path is prefixed
		if strings.HasSuffix(match, templatePath) {
			foundFile = match
			break
		}
		if strings.HasSuffix(match, templatePathWithLink) {
			foundFile = match
			break
		}
	}

Copy link
Member

@jsoriano jsoriano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, added a couple of small comments.

Comment on lines 175 to 177
// findPathWithPattern looks for a file matching the templatePath in the given directory (dir)
// It checks for exact matches, files ending with the templatePath, or templatePath + ".link"
func findPathWithPattern(fsys fspath.FS, dir, templatePath string) (string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit. It would be more correct to call this findPathWithSuffix, right?

Suggested change
// findPathWithPattern looks for a file matching the templatePath in the given directory (dir)
// It checks for exact matches, files ending with the templatePath, or templatePath + ".link"
func findPathWithPattern(fsys fspath.FS, dir, templatePath string) (string, error) {
// findPathWithSuffix looks for a file ending with the suffix in the given directory (dir)
// It checks for exact matches, files ending with the suffix, or suffix + ".link"
func findPathWithSuffix(fsys fspath.FS, dir, suffix string) (string, error) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i got the point for renaming, but was not sure about the suffix suggestion. The function also matches exact without being a suffix... i've renamed it to findPathAtDirectory... as the files should be inside the dir param ... WDYT?

mrodm
mrodm previously approved these changes Nov 4, 2025
Copy link
Contributor

@mrodm mrodm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!
Just a minor comment related to the changelog entry description.

Co-authored-by: Mario Rodriguez Molins <[email protected]>
@elasticmachine
Copy link

💚 Build Succeeded

History

@teresaromero teresaromero merged commit 2183978 into elastic:main Nov 4, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants