Skip to content
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

Add filtering by annotations on search #114

Merged
merged 1 commit into from
Apr 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/hypper/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func newSearchRepoCmd(logger log.Logger) *cobra.Command {
f.BoolVarP(&o.Versions, "versions", "l", false, "show the long listing, with each version of each chart on its own line, for repositories you have added")
f.BoolVar(&o.Devel, "devel", false, "use development versions (alpha, beta, and release candidate releases), too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
f.StringVar(&o.Version, "version", "", "search using semantic versioning constraints on repositories you have added")
f.StringToStringVarP(&o.Annotations, "annotations", "a", map[string]string{}, "filter using annotations. The format is --annotations key=value")
f.UintVar(&o.MaxColWidth, "max-col-width", 50, "maximum column width for output table")
bindOutputFlag(cmd, &o.OutputFormat)

Expand Down
27 changes: 26 additions & 1 deletion cmd/hypper/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,32 @@ func TestSearchRepositoriesCmd(t *testing.T) {
name: "search for 'alpine', expect valid yaml output",
cmd: "search repo alpine --output yaml",
golden: "output/search-output-yaml.txt",
}}
}, {
name: "search for hypper, expect latest version 0.2.0",
cmd: "search repo hypper --output yaml",
golden: "output/search-hypper-latest.txt",
}, { // I use yaml for the output because its easier to create the golden files, sue me :p
Copy link
Member

Choose a reason for hiding this comment

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

🤣 🤣 🤣 top!

// Should find version 0.1.0 as that is the only one with that annotation
name: "search for hypper with annotation hypper.cattle.io/namespace:hyppernamespace",
cmd: "search repo hypper -a 'hypper.cattle.io/namespace=hyppernamespace' --output yaml",
golden: "output/search-hypper-annotation.txt",
}, {
// Should find latest version 0.2.0 with that annotation
name: "search for hypper with regexp annotation hypper.cattle.io/release-name:hypper*",
cmd: "search repo hypper --regexp -a 'hypper.cattle.io/release-name=hypper*' --output yaml",
golden: "output/search-hypper-annotation-regexp.txt",
}, {
// Should find that exact version with the annotation, not the latest
name: "search for hypper with version and annotation hypper.cattle.io/release-name:hyppername",
cmd: "search repo hypper --version 0.1.0 -a 'hypper.cattle.io/release-name=hyppername' --output yaml",
golden: "output/search-hypper-annotation-version.txt",
}, {
// Now everything at the same time! regexp! version! annotations!
name: "search for hypper with version and annotation hypper.cattle.io/release-name:hyppername",
cmd: "search repo hypper --regexp --version 0.1.0 -a 'hypper.cattle.io/release-name=^hypper' --output yaml",
golden: "output/search-hypper-annotation-version-regexp.txt",
},
}

settings.Debug = true
defer func() { settings.Debug = false }()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,23 @@ entries:
email: containers@bitnami.com
icon: ""
apiVersion: v2
hypper:
- name: hypper
url: https://example.com/stable/hypper-0.1.0.tgz
created: "2018-04-23T08:20:27.160959131Z"
version: 0.1.0
description: Chart for hypper
icon: ""
apiVersion: v2
annotations:
hypper.cattle.io/namespace: hyppernamespace
hypper.cattle.io/release-name: hyppername
- name: hypper
url: https://example.com/stable/hypper-0.2.0.tgz
created: "2018-04-23T08:20:27.160959131Z"
version: 0.2.0
description: Chart for hypper
icon: ""
apiVersion: v2
annotations:
hypper.cattle.io/release-name: hyppername
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- app_version: ""
description: Chart for hypper
name: testing/hypper
version: 0.2.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- app_version: ""
description: Chart for hypper
name: testing/hypper
version: 0.1.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- app_version: ""
description: Chart for hypper
name: testing/hypper
version: 0.1.0
4 changes: 4 additions & 0 deletions cmd/hypper/testdata/output/search-hypper-annotation.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- app_version: ""
description: Chart for hypper
name: testing/hypper
version: 0.1.0
4 changes: 4 additions & 0 deletions cmd/hypper/testdata/output/search-hypper-latest.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- app_version: ""
description: Chart for hypper
name: testing/hypper
version: 0.2.0
34 changes: 34 additions & 0 deletions docs/user/howto/searchannotations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Searching for charts in repos using annotations

Apart from having the normal [search](docs/user/howto/search.md) in hypper, we support searching for specific annotations in a search.

Using the `-a` flag in `hypper search repo` allows to filter the returned charts based on the KEY=VALUE values in annotations.

```shell
$ hypper search repo -a 'hypper.cattle.io/release-name=fleet'
NAME CHART VERSION APP VERSION DESCRIPTION
hypper/fleet 0.3.500 0.3.5 Fleet Manager - GitOps at Scale

```


We also support the `--regexp` flag when using annotations, so you can search for the VALUE with a regexp

```shell
$ hypper search repo -a 'hypper.cattle.io/release-name=.*crd.*' --regexp
NAME CHART VERSION APP VERSION DESCRIPTION
hypper/fleet-crd 0.3.500 0.3.5 Fleet Manager CustomResourceDefinitions
hypper/longhorn-crd 1.1.001 Installs the CRDs for longhorn.
hypper/rancher-backup-crd 1.0.400 1.0.4 Installs the CRDs for rancher-backup.
hypper/rancher-cis-benchmark-crd 1.0.301 Installs the CRDs for rancher-cis-benchmark.
hypper/rancher-gatekeeper-crd 3.3.001 Installs the CRDs for rancher-gatekeeper.
hypper/rancher-logging-crd 3.9.002 Installs the CRDs for rancher-logging.
hypper/rancher-monitoring-crd 9.4.204 Installs the CRDs for rancher-monitoring.
hypper/rancher-operator-crd 0.1.300 0.1.3 Rancher Operator CustomResourceDefinitions

```

Of course the usual search flags also work in conjunction with annotations, so you can search for annotations *and* a specific version with he `--version` flag for example.


Note: Currently you can pass more than one annotation to search for. This is currently an OR filter, so if any of the given annotation are found in a chart, it will match.
30 changes: 30 additions & 0 deletions pkg/search/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
Copyright Suse LLC.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package search
/* Provides client-side repository searching

Currently the helm search is implemented under the cmd dir which means that most
of its options cant be used in a composite struct to build on top of them

Our implementation but extracts it into a package so it can be reused and composed over


Search supports building an in-memory search index based on the contents of
multiple repositories, and then using string matching or regular expressions
to find matches based on the name, version or annotations.
*/
package search
6 changes: 0 additions & 6 deletions pkg/search/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

/*Package search provides client-side repository searching.
dragonchaser marked this conversation as resolved.
Show resolved Hide resolved

This supports building an in-memory search index based on the contents of
multiple repositories, and then using string matching or regular expressions
to find matches.
*/
package search

import (
Expand Down
53 changes: 47 additions & 6 deletions pkg/search/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

/* Package search implements the search for charts in repos but extracts it into a package
so it can be reused and composed over
Currently the helm search is implemented under the cmd dir which means that most
of its options cant be used in a composite struct to build on top of them
*/
package search

import (
Expand All @@ -34,6 +29,7 @@ import (
"io"
"os"
"path/filepath"
"regexp"
"strings"
)

Expand All @@ -53,6 +49,7 @@ type RepoOptions struct {
MaxColWidth uint
RepoFile string
RepoCacheDir string
Annotations map[string]string
OutputFormat output.Format
}

Expand All @@ -78,7 +75,14 @@ func (o *RepoOptions) Run(logger log.Logger, args []string) error {
}

SortScore(res)
data, err := o.applyConstraint(res)
// First we need to check for annotations, otherwise the applyConstraint will only return the highest
// chart version for a given release
data, err := o.applyAnnotationsConstraint(res)
viccuad marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}

data, err = o.applyConstraint(data)
if err != nil {
return err
}
Expand Down Expand Up @@ -136,6 +140,43 @@ func (o *RepoOptions) applyConstraint(res []*Result) ([]*Result, error) {
return data, nil
}

// applyAnnotationsConstraint get a result list and filters it based on the annotations
// Currently is an OR so if more than one annotation is given, it will search for both and
// return the result as long as it finds at least one match
func (o *RepoOptions) applyAnnotationsConstraint(res []*Result) ([]*Result, error) {
if len(o.Annotations) == 0 {
return res, nil
}

data := res[:0]
for _, result := range res {
for key, value := range o.Annotations {
annotation := fmt.Sprintf("%v:%v", key, value)
log.Debugf("Checking for annotation %v", annotation)
if val, ok := result.Chart.Annotations[key]; ok {
log.Debugf("Found chart annotation %v with value %v", key, val)
if o.Regexp {
matcher, err := regexp.Compile(value)
if err != nil {
return []*Result{}, err
}
if index := matcher.FindStringIndex(val); len(index) > 0 {
log.Debugf("Regexp %v matches against %v", value, val)
data = append(data, result)
}
} else {
if val == value {
log.Debugf("Value %v matches against %v", value, val)
data = append(data, result)
}
}
}
}
}

return data, nil
}

// buildIndex loads the repos to add them to the index search
func (o *RepoOptions) buildIndex() (*Index, error) {
// Load the repositories.yaml
Expand Down