-
Notifications
You must be signed in to change notification settings - Fork 78
Add spec for shared folder and link files #888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 21 commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
cc4e351
Add spec for shared folder and link files
marc-gr aaa3efd
Add changelog entry
marc-gr 7605ac8
Fix description
marc-gr 1c5bd73
Move links logic to spec
marc-gr 6c51d57
Take into account root for copy
marc-gr e2d4cc3
Write to root
marc-gr 45c0583
Add required back where needed and simplfify test
marc-gr e930d16
Add comments
marc-gr 9cce7ff
Update go mod
marc-gr e893c88
Add go version 1.24.2
marc-gr 3819365
Revert go version update
marc-gr 3c540ea
Simplify link files validation
marc-gr 6b5c07a
Fix comment
marc-gr f70449a
Fix included path building
marc-gr b9ec960
Fix patterns
marc-gr c7ced63
revert go.dum
marc-gr 9d22235
Unescape unrelated patterns
marc-gr b214967
Add semantic anyof validation for contents
marc-gr a130490
Add comment
marc-gr 60c652d
Add toslash to avoid failing on windows
marc-gr 08c5495
Simplify initialization
marc-gr 94ba5df
Add link to test package and reuse shared spec
marc-gr 7f832dc
Merge remote-tracking branch 'upstream/main' into feat/includes
marc-gr e03cb61
Merge remote-tracking branch 'origin/feat/includes' into feat/includes
marc-gr 9ab873d
Add allowLink option into the spec
marc-gr 7422eec
Export NewLinkedFile
marc-gr 835ce7e
Add fs to block links
marc-gr 829e846
Fix lints
marc-gr 4c330a8
Rename with_links package
marc-gr 4a66c9d
Move changelog
marc-gr 4f01f75
Merge remote-tracking branch 'upstream/main' into feat/includes
marc-gr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
96 changes: 96 additions & 0 deletions
96
code/go/internal/validator/semantic/validate_anyof_required_contents.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// 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" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/elastic/package-spec/v3/code/go/internal/fspath" | ||
"github.com/elastic/package-spec/v3/code/go/pkg/specerrors" | ||
) | ||
|
||
type anyOfCondition struct { | ||
path string | ||
anyOfPatterns []string | ||
} | ||
|
||
func (a *anyOfCondition) validate(fsys fspath.FS) specerrors.ValidationErrors { | ||
if len(a.anyOfPatterns) == 0 || a.path == "" { | ||
return nil | ||
} | ||
|
||
var errs specerrors.ValidationErrors | ||
if err := a.validatePath(fsys, a.path); err != nil { | ||
errs = append(errs, specerrors.NewStructuredErrorf("path %q: %w", a.path, err)) | ||
} | ||
|
||
dataStreams, err := listDataStreams(fsys) | ||
if err != nil { | ||
return specerrors.ValidationErrors{specerrors.NewStructuredError(err, specerrors.UnassignedCode)} | ||
} | ||
|
||
for _, dataStream := range dataStreams { | ||
path := filepath.ToSlash(filepath.Join("data_stream", dataStream, a.path)) | ||
err := a.validatePath(fsys, path) | ||
if err != nil { | ||
errs = append(errs, specerrors.NewStructuredErrorf("data stream %q: %w", dataStream, err)) | ||
} | ||
} | ||
|
||
if len(errs) > 0 { | ||
return errs | ||
} | ||
return nil | ||
} | ||
|
||
func (a *anyOfCondition) validatePath(fsys fspath.FS, path string) specerrors.ValidationError { | ||
files, err := fs.ReadDir(fsys, path) | ||
if err != nil { | ||
if !errors.Is(err, os.ErrNotExist) { | ||
return specerrors.NewStructuredError(err, specerrors.UnassignedCode) | ||
} | ||
return nil | ||
} | ||
for _, file := range files { | ||
for _, pattern := range a.anyOfPatterns { | ||
matched, err := filepath.Match(pattern, file.Name()) | ||
if err != nil { | ||
return specerrors.NewStructuredError(err, specerrors.UnassignedCode) | ||
} | ||
if matched { | ||
return nil | ||
} | ||
} | ||
} | ||
return specerrors.NewStructuredErrorf("no file matching any of the patterns %v found in %s", a.anyOfPatterns, path) | ||
} | ||
|
||
// ValidateAnyOfRequiredContents validates that at least one file matching | ||
// any of the patterns in the given path exists in the package. | ||
// It checks the following paths: | ||
// - agent/input | ||
// - agent/stream | ||
// - fields | ||
// - elasticsearch/ingest_pipeline | ||
func ValidateAnyOfRequiredContents(fsys fspath.FS) specerrors.ValidationErrors { | ||
conditions := []anyOfCondition{ | ||
{path: "agent/input", anyOfPatterns: []string{"*.yml.hbs", "*.yml.hbs.link"}}, | ||
{path: "agent/stream", anyOfPatterns: []string{"*.yml.hbs", "*.yml.hbs.link"}}, | ||
{path: "fields", anyOfPatterns: []string{"*.yml", "*.yml.link"}}, | ||
{path: "elasticsearch/ingest_pipeline", anyOfPatterns: []string{"*.yml", "*.json", "*.yml.link", "*.json.link"}}, | ||
} | ||
|
||
var errs specerrors.ValidationErrors | ||
for _, c := range conditions { | ||
cerrs := c.validate(fsys) | ||
if cerrs != nil { | ||
errs = append(errs, cerrs...) | ||
} | ||
} | ||
return errs | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// 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 linkedfiles | ||
|
||
import ( | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
var _ fs.FS = (*LinksFS)(nil) | ||
|
||
// LinksFS is a filesystem that handles linked files. | ||
// It wraps another filesystem and checks for linked files with the ".link" extension. | ||
// If a linked file is found, it reads the link file to determine the target file | ||
// and its checksum. If the target file is up to date, it returns the target file. | ||
// Otherwise, it returns an error. | ||
type LinksFS struct { | ||
marc-gr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
workDir string | ||
inner fs.FS | ||
} | ||
|
||
// NewLinksFS creates a new LinksFS. | ||
func NewLinksFS(workDir string, inner fs.FS) *LinksFS { | ||
marc-gr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return &LinksFS{workDir: workDir, inner: inner} | ||
} | ||
|
||
// Open opens a file in the filesystem. | ||
func (lfs *LinksFS) Open(name string) (fs.File, error) { | ||
marc-gr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if filepath.Ext(name) != LinkExtension { | ||
return lfs.inner.Open(name) | ||
} | ||
pathName := filepath.Join(lfs.workDir, name) | ||
l, err := newLinkedFile(pathName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if !l.UpToDate { | ||
return nil, fmt.Errorf("linked file %s is not up to date", name) | ||
} | ||
includedPath := filepath.Join(lfs.workDir, filepath.Dir(name), l.IncludedFilePath) | ||
return os.Open(includedPath) | ||
} |
marc-gr marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// 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 linkedfiles | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
// LinkExtension is the file extension for linked files. | ||
const LinkExtension = ".link" | ||
|
||
// A Link represents a linked file. | ||
// It contains the path to the link file, the checksum of the linked file, | ||
// the path to the target file, and the checksum of the included file contents. | ||
// It also contains a boolean indicating whether the link is up to date. | ||
type Link struct { | ||
LinkFilePath string | ||
LinkChecksum string | ||
|
||
IncludedFilePath string | ||
IncludedFileContentsChecksum string | ||
|
||
UpToDate bool | ||
} | ||
|
||
func newLinkedFile(linkFilePath string) (Link, error) { | ||
var l Link | ||
firstLine, err := readFirstLine(linkFilePath) | ||
if err != nil { | ||
return Link{}, err | ||
} | ||
l.LinkFilePath = linkFilePath | ||
|
||
fields := strings.Fields(firstLine) | ||
l.IncludedFilePath = fields[0] | ||
if len(fields) == 2 { | ||
l.LinkChecksum = fields[1] | ||
} | ||
|
||
pathName := filepath.Join(filepath.Dir(linkFilePath), l.IncludedFilePath) | ||
cs, err := getLinkedFileChecksum(pathName) | ||
if err != nil { | ||
return Link{}, fmt.Errorf("could not collect file %v: %w", l.IncludedFilePath, err) | ||
} | ||
if l.LinkChecksum == cs { | ||
l.UpToDate = true | ||
} | ||
l.IncludedFileContentsChecksum = cs | ||
|
||
return l, nil | ||
} | ||
|
||
func getLinkedFileChecksum(path string) (string, error) { | ||
b, err := os.ReadFile(filepath.FromSlash(path)) | ||
if err != nil { | ||
return "", err | ||
} | ||
cs, err := checksum(b) | ||
if err != nil { | ||
return "", err | ||
} | ||
return cs, nil | ||
} | ||
|
||
func readFirstLine(filePath string) (string, error) { | ||
file, err := os.Open(filepath.FromSlash(filePath)) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer file.Close() | ||
|
||
scanner := bufio.NewScanner(file) | ||
if scanner.Scan() { | ||
return scanner.Text(), nil | ||
} | ||
|
||
if err := scanner.Err(); err != nil { | ||
return "", err | ||
} | ||
|
||
return "", fmt.Errorf("file is empty or first line is missing") | ||
} | ||
|
||
func checksum(b []byte) (string, error) { | ||
hash := sha256.New() | ||
if _, err := io.Copy(hash, bytes.NewReader(b)); err != nil { | ||
return "", err | ||
} | ||
return hex.EncodeToString(hash.Sum(nil)), nil | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,12 @@ | |
- description: Allow security_rule assets in content package. | ||
type: enhancement | ||
link: https://github.com/elastic/package-spec/pull/885 | ||
- description: Add support for _dev/shared folder. | ||
type: enhancement | ||
link: https://github.com/elastic/package-spec/pull/888 | ||
- description: Add support for *.link files in agent, pipelines, and fields folders. | ||
type: enhancement | ||
link: https://github.com/elastic/package-spec/pull/888 | ||
Comment on lines
+18
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be moved to a new version block for |
||
- version: 3.3.4 | ||
changes: | ||
- description: Remove slo assets when spec versions are less than 3.4.0. | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
spec: | ||
additionalContents: true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
spec: | ||
additionalContents: true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we remark that this
fields
folder is just for the ones defined underdata_stream
? Is that right?If so, would it be better to re-phrase that item in the comment as this? WDYT @marc-gr ?
IIUC the fields folder in
transforms
is not taken into account here. If it would be needed those links in transforms, I guess it could be added support in a follow-up. Let's continue with the current links defined in this PR , is that ok @jsoriano?