Skip to content

Commit 9d8ce35

Browse files
authored
Add support to filter errors in packages based on codes (#1467)
Add support to filter errores in packages based on the codes available in package-spec. These codes are defined in a new file "validation.yml" that should be present at the root of the package. A new test package has been added with this new file to check that errors are filtered.
1 parent 03b8213 commit 9d8ce35

File tree

14 files changed

+436
-22
lines changed

14 files changed

+436
-22
lines changed

cmd/lint.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import (
1010

1111
"github.com/spf13/cobra"
1212

13-
"github.com/elastic/package-spec/v2/code/go/pkg/validator"
14-
1513
"github.com/elastic/elastic-package/internal/cobraext"
1614
"github.com/elastic/elastic-package/internal/docs"
15+
"github.com/elastic/elastic-package/internal/logger"
1716
"github.com/elastic/elastic-package/internal/packages"
17+
"github.com/elastic/elastic-package/internal/validation"
1818
)
1919

2020
const lintLongDescription = `Use this command to validate the contents of a package using the package specification (see: https://github.com/elastic/package-spec).
@@ -69,10 +69,12 @@ func validateSourceCommandAction(cmd *cobra.Command, args []string) error {
6969
if err != nil {
7070
return fmt.Errorf("locating package root failed: %w", err)
7171
}
72-
err = validator.ValidateFromPath(packageRootPath)
73-
if err != nil {
74-
return fmt.Errorf("linting package failed: %w", err)
72+
errs, skipped := validation.ValidateAndFilterFromPath(packageRootPath)
73+
if skipped != nil {
74+
logger.Infof("Skipped errors: %v", skipped)
75+
}
76+
if errs != nil {
77+
return fmt.Errorf("linting package failed: %w", errs)
7578
}
76-
7779
return nil
7880
}

docs/howto/filter_errors.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# HOWTO: Filter errors based on Validation Codes
2+
3+
## Introduction
4+
5+
Starting with package-spec 2.13.0, there is an option to filter some errors based on their validation codes.
6+
7+
These validation codes are defined in package-spec [here][package-spec-code-errors].
8+
9+
## How to filter specific errors
10+
11+
Since package-spec 2.13.0, it's allowed to define a new file in the root of the package `validation.yml`.
12+
In this file, the errors to be filtered will be configured following this syntax:
13+
14+
```yaml
15+
errors:
16+
exclude_checks:
17+
- code1
18+
- code2
19+
```
20+
21+
`exclude_checks` key accepts a list of codes so more than one kind of error can be filtered.
22+
23+
24+
The code errors available to use in this configuration can be checked [here][package-spec-code-errors].
25+
The codes can be also checked in the error message itself shown by elastic-package. If the error is allowed
26+
to be filtered, it will have at the end of the message the code in parenthesis.
27+
28+
For instance, in this example the error code is `SVR00002`:
29+
30+
```
31+
[2023-09-28T19:10:19.758Z] Error: checking package failed: linting package failed: found 1 validation error:
32+
[2023-09-28T19:10:19.758Z] 1. file "/var/lib/jenkins/workspace/est-manager_integrations_PR-8012/src/github.com/elastic/integrations/packages/o365/kibana/dashboard/o365-712e2c00-685d-11ea-8d6a-292ef5d68366.json" is invalid: expected filter in dashboard: no filter found (SVR00002)
33+
```
34+
35+
36+
For that specific error, if it is needed to be filtered, then the configuration file `validation.yml` should be like:
37+
```yaml
38+
errors:
39+
exclude_checks:
40+
- SVR00002
41+
```
42+
43+
44+
[package-spec-code-errors]: https://github.com/elastic/package-spec/blob/49120aea627a1652823a7f344ba3d1c9b029fd5a/code/go/pkg/specerrors/constants.go
45+

internal/builder/packages.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@ import (
1212

1313
"github.com/magefile/mage/sh"
1414

15-
"github.com/elastic/package-spec/v2/code/go/pkg/validator"
16-
1715
"github.com/elastic/elastic-package/internal/environment"
1816
"github.com/elastic/elastic-package/internal/files"
1917
"github.com/elastic/elastic-package/internal/logger"
2018
"github.com/elastic/elastic-package/internal/packages"
19+
"github.com/elastic/elastic-package/internal/validation"
2120
)
2221

2322
const builtPackagesFolder = "packages"
@@ -195,9 +194,12 @@ func BuildPackage(options BuildOptions) (string, error) {
195194
}
196195

197196
logger.Debugf("Validating built package (path: %s)", destinationDir)
198-
err = validator.ValidateFromPath(destinationDir)
199-
if err != nil {
200-
return "", fmt.Errorf("invalid content found in built package: %w", err)
197+
errs, skipped := validation.ValidateAndFilterFromPath(destinationDir)
198+
if skipped != nil {
199+
logger.Infof("Skipped errors: %v", skipped)
200+
}
201+
if errs != nil {
202+
return "", fmt.Errorf("invalid content found in built package: %w", errs)
201203
}
202204
return destinationDir, nil
203205
}
@@ -218,9 +220,12 @@ func buildZippedPackage(options BuildOptions, destinationDir string) (string, er
218220
logger.Debug("Skip validation of the built .zip package")
219221
} else {
220222
logger.Debugf("Validating built .zip package (path: %s)", zippedPackagePath)
221-
err = validator.ValidateFromZip(zippedPackagePath)
222-
if err != nil {
223-
return "", fmt.Errorf("invalid content found in built zip package: %w", err)
223+
errs, skipped := validation.ValidateAndFilterFromZip(zippedPackagePath)
224+
if skipped != nil {
225+
logger.Infof("Skipped errors: %v", skipped)
226+
}
227+
if errs != nil {
228+
return "", fmt.Errorf("invalid content found in built zip package: %w", errs)
224229
}
225230
}
226231

internal/packages/archetype/package_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ import (
99
"path/filepath"
1010
"testing"
1111

12-
"github.com/elastic/package-spec/v2/code/go/pkg/validator"
13-
1412
"github.com/stretchr/testify/require"
1513

1614
"github.com/elastic/elastic-package/internal/packages"
15+
"github.com/elastic/elastic-package/internal/validation"
1716
)
1817

1918
func TestPackage(t *testing.T) {
@@ -83,7 +82,7 @@ func createPackageDescriptorForTest(packageType, kibanaVersion string) PackageDe
8382
}
8483

8584
func checkPackage(packageRoot string) error {
86-
err := validator.ValidateFromPath(packageRoot)
85+
err := validation.ValidateFromPath(packageRoot)
8786
if err != nil {
8887
return fmt.Errorf("linting package failed: %w", err)
8988
}

internal/packages/installer/factory.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ import (
1010

1111
"github.com/Masterminds/semver/v3"
1212

13-
"github.com/elastic/package-spec/v2/code/go/pkg/validator"
14-
1513
"github.com/elastic/elastic-package/internal/builder"
1614
"github.com/elastic/elastic-package/internal/kibana"
1715
"github.com/elastic/elastic-package/internal/logger"
1816
"github.com/elastic/elastic-package/internal/packages"
17+
"github.com/elastic/elastic-package/internal/validation"
1918
)
2019

2120
var semver8_7_0 = semver.MustParse("8.7.0")
@@ -61,9 +60,12 @@ func NewForPackage(options Options) (Installer, error) {
6160
}
6261
if !options.SkipValidation {
6362
logger.Debugf("Validating built .zip package (path: %s)", options.ZipPath)
64-
err := validator.ValidateFromZip(options.ZipPath)
65-
if err != nil {
66-
return nil, fmt.Errorf("invalid content found in built zip package: %w", err)
63+
errs, skipped := validation.ValidateAndFilterFromZip(options.ZipPath)
64+
if skipped != nil {
65+
logger.Infof("Skipped errors: %v", skipped)
66+
}
67+
if errs != nil {
68+
return nil, fmt.Errorf("invalid content found in built zip package: %w", errs)
6769
}
6870
}
6971
logger.Debug("Skip validation of the built .zip package")

internal/validation/validation.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package validation
6+
7+
import (
8+
"archive/zip"
9+
"errors"
10+
"fmt"
11+
"io/fs"
12+
"os"
13+
14+
"github.com/elastic/package-spec/v2/code/go/pkg/specerrors"
15+
"github.com/elastic/package-spec/v2/code/go/pkg/validator"
16+
)
17+
18+
func ValidateFromPath(rootPath string) error {
19+
return validator.ValidateFromPath(rootPath)
20+
}
21+
22+
func ValidateFromZip(packagePath string) error {
23+
return validator.ValidateFromZip(packagePath)
24+
}
25+
26+
func ValidateAndFilterFromPath(rootPath string) (error, error) {
27+
allErrors := validator.ValidateFromPath(rootPath)
28+
if allErrors == nil {
29+
return nil, nil
30+
}
31+
32+
fsys := os.DirFS(rootPath)
33+
result, err := filterErrors(allErrors, fsys)
34+
if err != nil {
35+
return err, nil
36+
}
37+
return result.Processed, result.Removed
38+
}
39+
40+
func ValidateAndFilterFromZip(packagePath string) (error, error) {
41+
allErrors := validator.ValidateFromZip(packagePath)
42+
if allErrors == nil {
43+
return nil, nil
44+
}
45+
46+
fsys, err := zip.OpenReader(packagePath)
47+
if err != nil {
48+
return fmt.Errorf("failed to open zip file (%s): %w", packagePath, err), nil
49+
}
50+
defer fsys.Close()
51+
52+
fsZip, err := fsFromPackageZip(fsys)
53+
if err != nil {
54+
return fmt.Errorf("failed to extract filesystem from zip file (%s): %w", packagePath, err), nil
55+
}
56+
57+
result, err := filterErrors(allErrors, fsZip)
58+
if err != nil {
59+
return err, nil
60+
}
61+
return result.Processed, result.Removed
62+
}
63+
64+
func fsFromPackageZip(fsys fs.FS) (fs.FS, error) {
65+
dirs, err := fs.ReadDir(fsys, ".")
66+
if err != nil {
67+
return nil, fmt.Errorf("failed to read root directory in zip file fs: %w", err)
68+
}
69+
if len(dirs) != 1 {
70+
return nil, fmt.Errorf("a single directory is expected in zip file, %d found", len(dirs))
71+
}
72+
73+
subDir, err := fs.Sub(fsys, dirs[0].Name())
74+
if err != nil {
75+
return nil, err
76+
}
77+
return subDir, nil
78+
}
79+
80+
func filterErrors(allErrors error, fsys fs.FS) (specerrors.FilterResult, error) {
81+
errs, ok := allErrors.(specerrors.ValidationErrors)
82+
if !ok {
83+
return specerrors.FilterResult{Processed: allErrors, Removed: nil}, nil
84+
}
85+
86+
config, err := specerrors.LoadConfigFilter(fsys)
87+
if err != nil && errors.Is(err, fs.ErrNotExist) {
88+
return specerrors.FilterResult{Processed: allErrors, Removed: nil}, nil
89+
}
90+
if err != nil {
91+
return specerrors.FilterResult{Processed: allErrors, Removed: nil},
92+
fmt.Errorf("failed to read config filter: %w", err)
93+
}
94+
if config == nil {
95+
return specerrors.FilterResult{Processed: allErrors, Removed: nil}, nil
96+
}
97+
98+
filter := specerrors.NewFilter(config)
99+
100+
result, err := filter.Run(errs)
101+
if err != nil {
102+
return specerrors.FilterResult{Processed: allErrors, Removed: nil},
103+
fmt.Errorf("failed to filter errors: %w", err)
104+
}
105+
return result, nil
106+
107+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
Elastic License 2.0
2+
3+
URL: https://www.elastic.co/licensing/elastic-license
4+
5+
## Acceptance
6+
7+
By using the software, you agree to all of the terms and conditions below.
8+
9+
## Copyright License
10+
11+
The licensor grants you a non-exclusive, royalty-free, worldwide,
12+
non-sublicensable, non-transferable license to use, copy, distribute, make
13+
available, and prepare derivative works of the software, in each case subject to
14+
the limitations and conditions below.
15+
16+
## Limitations
17+
18+
You may not provide the software to third parties as a hosted or managed
19+
service, where the service provides users with access to any substantial set of
20+
the features or functionality of the software.
21+
22+
You may not move, change, disable, or circumvent the license key functionality
23+
in the software, and you may not remove or obscure any functionality in the
24+
software that is protected by the license key.
25+
26+
You may not alter, remove, or obscure any licensing, copyright, or other notices
27+
of the licensor in the software. Any use of the licensor’s trademarks is subject
28+
to applicable law.
29+
30+
## Patents
31+
32+
The licensor grants you a license, under any patent claims the licensor can
33+
license, or becomes able to license, to make, have made, use, sell, offer for
34+
sale, import and have imported the software, in each case subject to the
35+
limitations and conditions in this license. This license does not cover any
36+
patent claims that you cause to be infringed by modifications or additions to
37+
the software. If you or your company make any written claim that the software
38+
infringes or contributes to infringement of any patent, your patent license for
39+
the software granted under these terms ends immediately. If your company makes
40+
such a claim, your patent license ends immediately for work on behalf of your
41+
company.
42+
43+
## Notices
44+
45+
You must ensure that anyone who gets a copy of any part of the software from you
46+
also gets a copy of these terms.
47+
48+
If you modify the software, you must include in any modified copies of the
49+
software prominent notices stating that you have modified the software.
50+
51+
## No Other Rights
52+
53+
These terms do not imply any licenses other than those expressly granted in
54+
these terms.
55+
56+
## Termination
57+
58+
If you use the software in violation of these terms, such use is not licensed,
59+
and your licenses will automatically terminate. If the licensor provides you
60+
with a notice of your violation, and you cease all violation of this license no
61+
later than 30 days after you receive that notice, your licenses will be
62+
reinstated retroactively. However, if you violate these terms after such
63+
reinstatement, any additional violation of these terms will cause your licenses
64+
to terminate automatically and permanently.
65+
66+
## No Liability
67+
68+
*As far as the law allows, the software comes as is, without any warranty or
69+
condition, and the licensor will not be liable to you for any damages arising
70+
out of these terms or the use or nature of the software, under any kind of
71+
legal claim.*
72+
73+
## Definitions
74+
75+
The **licensor** is the entity offering these terms, and the **software** is the
76+
software the licensor makes available under these terms, including any portion
77+
of it.
78+
79+
**you** refers to the individual or entity agreeing to these terms.
80+
81+
**your company** is any legal entity, sole proprietorship, or other kind of
82+
organization that you work for, plus all organizations that have control over,
83+
are under the control of, or are under common control with that
84+
organization. **control** means ownership of substantially all the assets of an
85+
entity, or the power to direct its management and policies by vote, contract, or
86+
otherwise. Control can be direct or indirect.
87+
88+
**your licenses** are all the licenses granted to you for the software under
89+
these terms.
90+
91+
**use** means anything you do with the software requiring one of your licenses.
92+
93+
**trademark** means trademarks, service marks, and similar rights.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# newer versions go on top
2+
- version: "0.0.1"
3+
changes:
4+
- description: Initial draft of the package
5+
type: enhancement
6+
link: https://github.com/elastic/integrations/pull/1 # FIXME Replace with the real PR link

0 commit comments

Comments
 (0)