Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package validator
package packages

import (
"fmt"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package validator
package packages

import (
"path/filepath"
Expand Down
7 changes: 4 additions & 3 deletions code/go/internal/validator/folder_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,25 @@ import (
"github.com/pkg/errors"

ve "github.com/elastic/package-spec/v2/code/go/internal/errors"
"github.com/elastic/package-spec/v2/code/go/internal/packages"
"github.com/elastic/package-spec/v2/code/go/internal/spectypes"
"github.com/elastic/package-spec/v2/code/go/internal/validator/common"
)

type validator struct {
spec spectypes.ItemSpec
pkg *Package
pkg *packages.Package
folderPath string

totalSize spectypes.FileSize
totalContents int
}

func newValidator(spec spectypes.ItemSpec, pkg *Package) *validator {
func newValidator(spec spectypes.ItemSpec, pkg *packages.Package) *validator {
return newValidatorForPath(spec, pkg, ".")
}

func newValidatorForPath(spec spectypes.ItemSpec, pkg *Package, folderPath string) *validator {
func newValidatorForPath(spec spectypes.ItemSpec, pkg *packages.Package, folderPath string) *validator {
return &validator{
spec: spec,
pkg: pkg,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// 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 (
"regexp"

"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"

ve "github.com/elastic/package-spec/v2/code/go/internal/errors"
"github.com/elastic/package-spec/v2/code/go/internal/fspath"
"github.com/elastic/package-spec/v2/code/go/internal/packages"
"github.com/elastic/package-spec/v2/code/go/internal/pkgpath"
)

// ValidateMinimumKibanaVersion if the package is an input package, and the package version is >= 1.0.0,
// then the kibana version condition must be >= 8.8.0
func ValidateMinimumKibanaVersion(fsys fspath.FS) ve.ValidationErrors {
pkg, err := packages.NewPackageFromFS(fsys.Path(), fsys)
if err != nil {
return ve.ValidationErrors{err}
}

manifest, err := readManifest(fsys)
if err != nil {
return ve.ValidationErrors{err}
}

kibanaVersionCondition, err := getKibanaVersionCondition(*manifest)
if err != nil {
return ve.ValidationErrors{err}
}

err = validateMinimumKibanaVersion(pkg.Type, *pkg.Version, kibanaVersionCondition)
if err != nil {
return ve.ValidationErrors{err}
}

return nil
}

func validateMinimumKibanaVersion(packageType string, packageVersion semver.Version, kibanaVersionCondition string) error {

if packageType != "input" {
return nil
}

if packageVersion.LessThan(semver.MustParse("1.0.0")) {
return nil
}

if kibanaVersionConditionIsGreaterThanOrEqualTo8_8_0(kibanaVersionCondition) {
return nil
}

return errors.New("Warning: conditions.kibana.version must be ^8.8.0 or greater for non experimental input packages (version > 1.0.0)")
}

func readManifest(fsys fspath.FS) (*pkgpath.File, error) {
manifestPath := "manifest.yml"
f, err := pkgpath.Files(fsys, manifestPath)
if err != nil {
return nil, errors.Wrap(err, "can't locate manifest file")
}

if len(f) != 1 {
return nil, errors.New("single manifest file expected")
}

return &f[0], nil
}

func getKibanaVersionCondition(manifest pkgpath.File) (string, error) {

val, err := manifest.Values("$.conditions[\"kibana.version\"]")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

do we need to handle:

conditions: kibana.version: 1234 

and

conditions: kibana: version: 1234 

here? It seems the lib treats them differently, all packages use kibana.version

Copy link
Member

Choose a reason for hiding this comment

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

This actually looks like an issue with this Values helper. The spec allows both kibana.version, or version as an attribute inside kibana.

It should probably use ucfg to parse these files instead of plain yaml. See https://github.com/elastic/go-ucfg#dot-notations

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'll have a go at this 👌

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

@hop-dev hop-dev Mar 6, 2023

Choose a reason for hiding this comment

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

I have put a temporary workaround in this PR (9840df4)and have a draft PR for using ucfg https://github.com/elastic/package-spec/pull/487/files

if err != nil {
val, err = manifest.Values("$.conditions.kibana.version")
if err != nil {
return "", nil
}
}

sVal, ok := val.(string)
if !ok {
return "", errors.New("manifest kibana version is not a string")
}

return sVal, nil
}

func kibanaVersionConditionIsGreaterThanOrEqualTo8_8_0(kibanaVersionCondition string) bool {
if kibanaVersionCondition == "" {
return false
}

if kibanaVersionCondition == "^8.8.0" {
return true
}

// get all versions e.g 8.8.0, 8.8.1 from "^8.8.0 || ^8.8.1" and check if any of them is less than 8.8.0
pattern := `(\d+\.\d+\.\d+)`
semver8_8_0 := semver.MustParse("8.8.0")
regex := regexp.MustCompile(pattern)
matches := regex.FindAllString(kibanaVersionCondition, -1)

for _, match := range matches {
matchVersion, err := semver.NewVersion(match)
if err != nil {
return false
}

if matchVersion.LessThan(semver8_8_0) {
return false
}
}

return true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// 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"
"testing"

"github.com/Masterminds/semver/v3"
"github.com/stretchr/testify/assert"
)

func TestValidateKibanaVersionGreaterThan(t *testing.T) {
var tests = []struct {
version string
expectedVal bool
}{
{
"^8.8.0",
true,
},
{
"^10.11.12",
true,
},
{
"8.8.0",
true,
},
{
"^8.8.0 || ^9.9.0",
true,
},
{
"^8.8.0 || ^9.9.0 || ^10.11.12",
true,
},
{
"^7.7.0",
false,
},
{
"^7.7.0 || ^8.8.0",
false,
},
{
"^7.7.0 || ^10.11.12",
false,
},
{
"",
false,
},
}
for _, test := range tests {
t.Run(test.version, func(t *testing.T) {
assert.Equal(t, kibanaVersionConditionIsGreaterThanOrEqualTo8_8_0(test.version), test.expectedVal)
})
}
}
func TestValidateMinimumKibanaVersion(t *testing.T) {

kbnVersionError := errors.New("Warning: conditions.kibana.version must be ^8.8.0 or greater for non experimental input packages (version > 1.0.0)")
var tests = []struct {
packageType string
packageVersion semver.Version
kibanaVersionCondition string
expectedErr error
}{
{
"integration",
*semver.MustParse("1.0.0"),
"^7.14.0",
nil,
},
{
"input",
*semver.MustParse("0.1.0"),
"^7.14.0",
nil,
},
{
"input",
*semver.MustParse("1.0.0"),
"^7.14.0",
kbnVersionError,
},
{
"input",
*semver.MustParse("1.0.0"),
"^8.8.0 || ^7.14.0",
kbnVersionError,
},
{
"input",
*semver.MustParse("1.0.0"),
"^8.8.0",
nil,
},
}

for _, test := range tests {
t.Run(test.packageType+"--"+test.packageVersion.String()+"--"+test.kibanaVersionCondition, func(t *testing.T) {
res := validateMinimumKibanaVersion(test.packageType, test.packageVersion, test.kibanaVersionCondition)

if test.expectedErr == nil {
assert.Nil(t, res)
} else {
assert.Equal(t, test.expectedErr.Error(), res.Error())
}
})
}
}
4 changes: 3 additions & 1 deletion code/go/internal/validator/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
ve "github.com/elastic/package-spec/v2/code/go/internal/errors"
"github.com/elastic/package-spec/v2/code/go/internal/fspath"
"github.com/elastic/package-spec/v2/code/go/internal/loader"
"github.com/elastic/package-spec/v2/code/go/internal/packages"
"github.com/elastic/package-spec/v2/code/go/internal/spectypes"
"github.com/elastic/package-spec/v2/code/go/internal/validator/semantic"
)
Expand Down Expand Up @@ -49,7 +50,7 @@ func NewSpec(version semver.Version) (*Spec, error) {
}

// ValidatePackage validates the given Package against the Spec
func (s Spec) ValidatePackage(pkg Package) ve.ValidationErrors {
func (s Spec) ValidatePackage(pkg packages.Package) ve.ValidationErrors {
var errs ve.ValidationErrors

rootSpec, err := loader.LoadSpec(s.fs, s.version, pkg.Type)
Expand Down Expand Up @@ -116,6 +117,7 @@ func (s Spec) rules(rootSpec spectypes.ItemSpec) validationRules {
{fn: semantic.ValidateVersionIntegrity},
{fn: semantic.ValidateChangelogLinks},
{fn: semantic.ValidatePrerelease},
{fn: semantic.ValidateMinimumKibanaVersion},
{fn: semantic.ValidateFieldGroups},
{fn: semantic.ValidateFieldsLimits(rootSpec.MaxFieldsPerDataStream())},
{fn: semantic.ValidateUniqueFields, since: semver.MustParse("2.0.0")},
Expand Down
5 changes: 3 additions & 2 deletions code/go/internal/validator/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/elastic/package-spec/v2/code/go/internal/fspath"
"github.com/elastic/package-spec/v2/code/go/internal/packages"
)

func TestNewSpec(t *testing.T) {
Expand Down Expand Up @@ -42,7 +43,7 @@ func TestNoBetaFeatures_Package_GA(t *testing.T) {
*semver.MustParse("1.0.0"),
fspath.DirFS("testdata/fakespec"),
}
pkg, err := NewPackage("testdata/packages/features_ga")
pkg, err := packages.NewPackage("testdata/packages/features_ga")
require.NoError(t, err)

err = s.ValidatePackage(*pkg)
Expand All @@ -55,7 +56,7 @@ func TestBetaFeatures_Package_GA(t *testing.T) {
*semver.MustParse("1.0.0"),
fspath.DirFS("testdata/fakespec"),
}
pkg, err := NewPackage("testdata/packages/features_beta")
pkg, err := packages.NewPackage("testdata/packages/features_beta")
require.NoError(t, err)

errs := s.ValidatePackage(*pkg)
Expand Down
3 changes: 2 additions & 1 deletion code/go/pkg/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"io/fs"
"os"

"github.com/elastic/package-spec/v2/code/go/internal/packages"
"github.com/elastic/package-spec/v2/code/go/internal/validator"
)

Expand Down Expand Up @@ -47,7 +48,7 @@ func ValidateFromZip(packagePath string) error {
// ValidateFromFS validates a package against the appropiate specification and returns any errors.
// Package files are obtained throug the given filesystem.
func ValidateFromFS(location string, fsys fs.FS) error {
pkg, err := validator.NewPackageFromFS(location, fsys)
pkg, err := packages.NewPackageFromFS(location, fsys)
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions code/go/pkg/validator/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,13 @@ func TestValidateWarnings(t *testing.T) {
"good": []string{},
"good_v2": []string{},
"custom_logs": []string{
"package with non-stable semantic version and active beta features (enabled in [../../../../test/packages/custom_logs]) can't be released as stable version.",
"conditions.kibana.version must be ^8.8.0 or greater for non experimental input packages (version > 1.0.0)",
},
"httpjson_input": []string{
"package with non-stable semantic version and active beta features (enabled in [../../../../test/packages/httpjson_input]) can't be released as stable version.",
"conditions.kibana.version must be ^8.8.0 or greater for non experimental input packages (version > 1.0.0)",
},
"sql_input": []string{
"package with non-stable semantic version and active beta features (enabled in [../../../../test/packages/sql_input]) can't be released as stable version.",
"conditions.kibana.version must be ^8.8.0 or greater for non experimental input packages (version > 1.0.0)",
},
"visualizations_by_reference": []string{
"references found in dashboard kibana/dashboard/visualizations_by_reference-82273ffe-6acc-4f2f-bbee-c1004abba63d.json: visualizations_by_reference-5e1a01ff-6f9a-41c1-b7ad-326472db42b6 (visualization), visualizations_by_reference-8287a5d5-1576-4f3a-83c4-444e9058439b (lens)",
Expand Down
3 changes: 3 additions & 0 deletions spec/changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
##
- version: 2.6.0-next
changes:
- description: Release input packages spec as GA
type: enhancement
link: https://github.com/elastic/package-spec/pull/485
- description: Remove monitoring_infrastructure category
type: breaking-change
link: https://github.com/elastic/package-spec/pull/490
Expand Down
1 change: 0 additions & 1 deletion spec/input/spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ spec:
sizeLimit: 30MB
configurationSizeLimit: 5MB
relativePathSizeLimit: 3MB
release: beta
contents:
- description: The main package manifest file
type: file
Expand Down
2 changes: 1 addition & 1 deletion test/packages/good_input/changelog.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# newer versions go on top
- version: "0.2.0"
- version: "1.0.0"
changes:
- description: Initial draft of the package
type: enhancement
Expand Down
Loading