From c290aed0bae5e90504f7f2139c45ee214c8b3ce2 Mon Sep 17 00:00:00 2001 From: Mario Rodriguez Molins Date: Wed, 12 Jul 2023 13:02:41 +0200 Subject: [PATCH 1/3] Add capabilities parameter in search endpoint --- packages/package.go | 17 +++++- packages/packages.go | 13 +++++ packages/packages_test.go | 119 +++++++++++++++++++++++++++++++++++--- search.go | 5 ++ 4 files changed, 145 insertions(+), 9 deletions(-) diff --git a/packages/package.go b/packages/package.go index ca224e6a2..49e040087 100644 --- a/packages/package.go +++ b/packages/package.go @@ -120,7 +120,8 @@ type KibanaConditions struct { // ElasticConditions defines conditions related to Elastic subscriptions or partnerships. type ElasticConditions struct { - Subscription string `config:"subscription" json:"subscription" yaml:"subscription"` + Subscription string `config:"subscription" json:"subscription" yaml:"subscription"` + Capabilities []string `config:"capabilities,omitempty" json:"capabilities,omitempty" yaml:"capabilities,omitempty"` } type Version struct { @@ -387,6 +388,20 @@ func (p *Package) HasKibanaVersion(version *semver.Version) bool { return p.Conditions.Kibana.constraint.Check(version) } +func (p *Package) HasCapabilities(capabilities []string) bool { + if p.Conditions == nil || p.Conditions.Elastic == nil || p.Conditions.Elastic.Capabilities == nil || capabilities == nil { + return true + } + + // TODO if there are several capabilities in the query, should all of them match with the ones defined in the package ? + for _, c := range capabilities { + if !util.StringsContains(p.Conditions.Elastic.Capabilities, c) { + return false + } + } + return true +} + func (p *Package) IsNewerOrEqual(pp *Package) bool { return !p.versionSemVer.LessThan(pp.versionSemVer) } diff --git a/packages/packages.go b/packages/packages.go index 6043b0e43..9a0bca5e6 100644 --- a/packages/packages.go +++ b/packages/packages.go @@ -293,6 +293,7 @@ type Filter struct { PackageName string PackageVersion string PackageType string + Capabilities []string // Deprecated, release tags to be removed. Experimental bool @@ -342,6 +343,12 @@ func (f *Filter) Apply(ctx context.Context, packages Packages) Packages { continue } + if f.Capabilities != nil { + if valid := p.HasCapabilities(f.Capabilities); !valid { + continue + } + } + addPackage := true if !f.AllVersions { // Check if the version exists and if it should be added or not. @@ -405,6 +412,12 @@ func (f *Filter) legacyApply(ctx context.Context, packages Packages) Packages { continue } + if f.Capabilities != nil { + if valid := p.HasCapabilities(f.Capabilities); !valid { + continue + } + } + addPackage := true if !f.AllVersions { // Check if the version exists and if it should be added or not. diff --git a/packages/packages_test.go b/packages/packages_test.go index ef4a1c1db..d4d58036b 100644 --- a/packages/packages_test.go +++ b/packages/packages_test.go @@ -84,6 +84,36 @@ func TestPackagesFilter(t *testing.T) { Type: "integration", KibanaVersion: "^8.0.0", }, + { + Name: "obs_package", + Version: "1.1.0", + Type: "integration", + Capabilities: []string{"observability"}, + }, + { + Name: "obs_sec_package", + Version: "1.0.0", + Type: "integration", + Capabilities: []string{"observability", "security"}, + }, + { + Name: "obs_sec_package", + Version: "2.0.0-rc1", + Type: "integration", + Capabilities: []string{"observability", "security"}, + }, + { + Name: "obs_sec_package", + Version: "2.0.0", + Type: "integration", + Capabilities: []string{"observability", "security"}, + }, + { + Name: "obs_sec_uptime_package", + Version: "2.0.0", + Type: "integration", + Capabilities: []string{"observability", "security", "uptime"}, + }, } packages := buildFilterTestPackages(filterTestPackages) @@ -164,6 +194,9 @@ func TestPackagesFilter(t *testing.T) { {Name: "apache", Version: "1.0.0"}, {Name: "nginx", Version: "2.0.0"}, {Name: "redisenterprise", Version: "1.0.0"}, + {Name: "obs_package", Version: "1.1.0"}, + {Name: "obs_sec_package", Version: "2.0.0"}, + {Name: "obs_sec_uptime_package", Version: "2.0.0"}, }, }, { @@ -231,6 +264,7 @@ func TestPackagesFilter(t *testing.T) { filterTestPackage{Name: "apache", Version: "1.0.0-rc1"}, filterTestPackage{Name: "apache", Version: "2.0.0-rc2"}, filterTestPackage{Name: "redisenterprise", Version: "0.1.1"}, + filterTestPackage{Name: "obs_sec_package", Version: "2.0.0-rc1"}, ), }, { @@ -350,6 +384,60 @@ func TestPackagesFilter(t *testing.T) { {Name: "etcd", Version: "1.0.0-rc2"}, }, }, + { + Title: "non existing capabilities search", + Filter: Filter{ + Capabilities: []string{"no_match"}, + }, + Expected: []filterTestPackage{ + {Name: "apache", Version: "1.0.0"}, + {Name: "nginx", Version: "2.0.0"}, + {Name: "redisenterprise", Version: "1.0.0"}, + }, + }, + { + Title: "observability capabilities search", + Filter: Filter{ + Capabilities: []string{"observability"}, + }, + Expected: []filterTestPackage{ + {Name: "apache", Version: "1.0.0"}, + {Name: "nginx", Version: "2.0.0"}, + {Name: "redisenterprise", Version: "1.0.0"}, + {Name: "obs_package", Version: "1.1.0"}, + {Name: "obs_sec_package", Version: "2.0.0"}, + {Name: "obs_sec_uptime_package", Version: "2.0.0"}, + }, + }, + { + Title: "observability and security capabilities search", + Filter: Filter{ + Capabilities: []string{"observability", "security"}, + }, + Expected: []filterTestPackage{ + {Name: "apache", Version: "1.0.0"}, + {Name: "nginx", Version: "2.0.0"}, + {Name: "redisenterprise", Version: "1.0.0"}, + {Name: "obs_sec_package", Version: "2.0.0"}, + {Name: "obs_sec_uptime_package", Version: "2.0.0"}, + }, + }, + { + Title: "observability, security and uptime capabilities search - legacy kibana", + Filter: Filter{ + Experimental: true, + Capabilities: []string{"observability", "security", "uptime"}, + }, + Expected: []filterTestPackage{ + {Name: "apache", Version: "1.0.0"}, + {Name: "nginx", Version: "2.0.0"}, + {Name: "mysql", Version: "0.9.0"}, + {Name: "logstash", Version: "1.1.0"}, + {Name: "etcd", Version: "1.0.0-rc2"}, + {Name: "redisenterprise", Version: "1.0.0"}, + {Name: "obs_sec_uptime_package", Version: "2.0.0"}, + }, + }, } for _, c := range cases { @@ -366,6 +454,7 @@ type filterTestPackage struct { Release string Type string KibanaVersion string + Capabilities []string } func (p filterTestPackage) Build() *Package { @@ -377,15 +466,29 @@ func (p filterTestPackage) Build() *Package { build.Release = p.Release build.Type = p.Type - constraints, err := semver.NewConstraint(p.KibanaVersion) - if err != nil { - panic(err) + if p.KibanaVersion != "" { + constraints, err := semver.NewConstraint(p.KibanaVersion) + if err != nil { + panic(err) + } + build.Conditions = &Conditions{ + Kibana: &KibanaConditions{ + Version: p.KibanaVersion, + constraint: constraints, + }, + } } - build.Conditions = &Conditions{ - Kibana: &KibanaConditions{ - Version: p.KibanaVersion, - constraint: constraints, - }, + if p.Capabilities != nil { + elasticConditions := ElasticConditions{ + Capabilities: p.Capabilities, + } + if build.Conditions != nil { + build.Conditions.Elastic = &elasticConditions + } else { + build.Conditions = &Conditions{ + Elastic: &elasticConditions, + } + } } return &build } diff --git a/search.go b/search.go index 08f027a31..109d46b84 100644 --- a/search.go +++ b/search.go @@ -11,6 +11,7 @@ import ( "net/url" "sort" "strconv" + "strings" "time" "github.com/Masterminds/semver/v3" @@ -97,6 +98,10 @@ func newSearchFilterFromQuery(query url.Values) (*packages.Filter, error) { filter.PackageType = v } + if v := query.Get("capabilities"); v != "" { + filter.Capabilities = strings.Split(v, ",") + } + if v := query.Get("all"); v != "" { // Default is false, also on error filter.AllVersions, err = strconv.ParseBool(v) From 8b75ff8c67bf9e5887a114a72d6ab0e71c2cf15a Mon Sep 17 00:00:00 2001 From: Mario Rodriguez Molins Date: Wed, 12 Jul 2023 15:20:53 +0200 Subject: [PATCH 2/3] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3785d60d..874a9b4b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +* Add new query parameter "capabilities" in search endpoint [#1054](https://github.com/elastic/package-registry/pull/1054) + ### Deprecated ### Known Issues From 8854475bf1a692c8435cea5089bf1c5e22e1addd Mon Sep 17 00:00:00 2001 From: Mario Rodriguez Molins Date: Thu, 13 Jul 2023 15:56:22 +0200 Subject: [PATCH 3/3] Add package if all its capabilities are included in the query value --- packages/package.go | 7 +++---- packages/packages.go | 4 ++-- packages/packages_test.go | 6 +++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/package.go b/packages/package.go index 49e040087..c54ba6605 100644 --- a/packages/package.go +++ b/packages/package.go @@ -388,14 +388,13 @@ func (p *Package) HasKibanaVersion(version *semver.Version) bool { return p.Conditions.Kibana.constraint.Check(version) } -func (p *Package) HasCapabilities(capabilities []string) bool { +func (p *Package) WorksWithCapabilities(capabilities []string) bool { if p.Conditions == nil || p.Conditions.Elastic == nil || p.Conditions.Elastic.Capabilities == nil || capabilities == nil { return true } - // TODO if there are several capabilities in the query, should all of them match with the ones defined in the package ? - for _, c := range capabilities { - if !util.StringsContains(p.Conditions.Elastic.Capabilities, c) { + for _, requiredCapability := range p.Conditions.Elastic.Capabilities { + if !util.StringsContains(capabilities, requiredCapability) { return false } } diff --git a/packages/packages.go b/packages/packages.go index 9a0bca5e6..bcfb0ad3c 100644 --- a/packages/packages.go +++ b/packages/packages.go @@ -344,7 +344,7 @@ func (f *Filter) Apply(ctx context.Context, packages Packages) Packages { } if f.Capabilities != nil { - if valid := p.HasCapabilities(f.Capabilities); !valid { + if valid := p.WorksWithCapabilities(f.Capabilities); !valid { continue } } @@ -413,7 +413,7 @@ func (f *Filter) legacyApply(ctx context.Context, packages Packages) Packages { } if f.Capabilities != nil { - if valid := p.HasCapabilities(f.Capabilities); !valid { + if valid := p.WorksWithCapabilities(f.Capabilities); !valid { continue } } diff --git a/packages/packages_test.go b/packages/packages_test.go index d4d58036b..babb1c0d4 100644 --- a/packages/packages_test.go +++ b/packages/packages_test.go @@ -405,8 +405,6 @@ func TestPackagesFilter(t *testing.T) { {Name: "nginx", Version: "2.0.0"}, {Name: "redisenterprise", Version: "1.0.0"}, {Name: "obs_package", Version: "1.1.0"}, - {Name: "obs_sec_package", Version: "2.0.0"}, - {Name: "obs_sec_uptime_package", Version: "2.0.0"}, }, }, { @@ -418,8 +416,8 @@ func TestPackagesFilter(t *testing.T) { {Name: "apache", Version: "1.0.0"}, {Name: "nginx", Version: "2.0.0"}, {Name: "redisenterprise", Version: "1.0.0"}, + {Name: "obs_package", Version: "1.1.0"}, {Name: "obs_sec_package", Version: "2.0.0"}, - {Name: "obs_sec_uptime_package", Version: "2.0.0"}, }, }, { @@ -435,6 +433,8 @@ func TestPackagesFilter(t *testing.T) { {Name: "logstash", Version: "1.1.0"}, {Name: "etcd", Version: "1.0.0-rc2"}, {Name: "redisenterprise", Version: "1.0.0"}, + {Name: "obs_package", Version: "1.1.0"}, + {Name: "obs_sec_package", Version: "2.0.0"}, {Name: "obs_sec_uptime_package", Version: "2.0.0"}, }, },