Skip to content

[WIP] olmv1 catalog search #233

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
22 changes: 22 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require (
k8s.io/api v0.32.2
k8s.io/apiextensions-apiserver v0.32.2
k8s.io/apimachinery v0.32.2
k8s.io/cli-runtime v0.32.2
k8s.io/client-go v0.32.2
sigs.k8s.io/controller-runtime v0.20.2
sigs.k8s.io/yaml v1.4.0
Expand All @@ -27,6 +28,7 @@ require (
require (
cel.dev/expr v0.18.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.12.9 // indirect
Expand Down Expand Up @@ -57,6 +59,10 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.1 // indirect
github.com/go-git/go-git/v5 v5.13.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
Expand All @@ -65,34 +71,47 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.22.1 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/h2non/filetype v1.1.3 // indirect
github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/joelanford/ignore v0.1.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/capability v0.3.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.etcd.io/bbolt v1.3.11 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
Expand All @@ -116,10 +135,13 @@ require (
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/kustomize/api v0.18.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
)
53 changes: 53 additions & 0 deletions go.sum

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions internal/cmd/internal/olmv1/catalog_search.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package olmv1

import (
"os"

"github.com/operator-framework/kubectl-operator/internal/cmd/internal/log"
v1action "github.com/operator-framework/kubectl-operator/internal/pkg/v1/action"
"github.com/operator-framework/kubectl-operator/pkg/action"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

// NewCatalogInstalledGetCmd handles get commands in the form of:
// catalog(s) [catalog_name] - this will either list all the installed operators
// if no catalog_name has been provided or display the details of the specific
// one otherwise
func NewCatalogSearchCmd(cfg *action.Configuration) *cobra.Command {
i := v1action.NewCatalogSearch(cfg)
i.Logf = log.Printf

cmd := &cobra.Command{
Use: "catalog",
Aliases: []string{"catalogs"},
Args: cobra.RangeArgs(0, 1),
Short: "Search catalogs for installable operators matching parameters",
Run: func(cmd *cobra.Command, args []string) {
catalogContents, err := i.Run(cmd.Context())
if err != nil {
log.Fatalf("failed querying catalog(s): %v", err)
}
switch i.OutputFormat {
case "", "table":
printFormattedDeclCfg(os.Stdout, catalogContents, i.ListVersions)
case "json":
printDeclCfgJSON(os.Stdout, catalogContents)
case "yaml":
printDeclCfgYAML(os.Stdout, catalogContents)
default:
log.Fatalf("unsupported output format %s: allwed formats are (json|yaml|table)", i.OutputFormat)
}
},
}
bindCatalogSearchFlags(cmd.Flags(), i)

return cmd
}

func bindCatalogSearchFlags(fs *pflag.FlagSet, i *v1action.CatalogSearch) {
fs.StringVar(&i.Catalog, "catalog", "", "Catalog to search on. If not provided, all available catalogs are searched.")
fs.StringToStringVarP(&i.Selector, "selector", "l", map[string]string{}, "Selector (label query) to filter catalogs on, supports '=', '==', and '!='")
fs.StringVarP(&i.OutputFormat, "output", "o", "", "output format. One of: (yaml|json)")
fs.BoolVar(&i.ListVersions, "list-versions", false, "List all versions available for each package")
fs.StringVar(&i.Package, "package", "", "Search for package by name. If empty, all available packages will be listed")
// installable vs uninstallable, all versions, channels
// fs.StringVar(&i.showAll, "image", "", "Image reference for the catalog source. Leave unset to retain the current image.")
}
41 changes: 41 additions & 0 deletions internal/cmd/internal/olmv1/catalog_update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package olmv1

import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"

"github.com/operator-framework/kubectl-operator/internal/cmd/internal/log"
v1action "github.com/operator-framework/kubectl-operator/internal/pkg/v1/action"
"github.com/operator-framework/kubectl-operator/pkg/action"
)

// NewCatalogUpdateCmd allows updating a selected clustercatalog
func NewCatalogUpdateCmd(cfg *action.Configuration) *cobra.Command {
i := v1action.NewCatalogUpdate(cfg)
i.Logf = log.Printf

cmd := &cobra.Command{
Use: "catalog <catalog>",
Short: "Update a catalog",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
i.CatalogName = args[0]
_, err := i.Run(cmd.Context())
if err != nil {
log.Fatalf("failed to update catalog: %v", err)
}
log.Printf("catalog %q updated", i.CatalogName)
},
}
bindCatalogUpdateFlags(cmd.Flags(), i)

return cmd
}

func bindCatalogUpdateFlags(fs *pflag.FlagSet, i *v1action.CatalogUpdate) {
fs.Int32Var(i.Priority, "priority", 1, "priority determines the likelihood of a catalog being selected in conflict scenarios")
fs.IntVar(i.PollIntervalMinutes, "source-poll-interval-minutes", 5, "catalog source polling interval [in minutes]. Set to 0 or -1 to remove the polling interval.")
fs.StringToStringVar(&i.Labels, "labels", map[string]string{}, "labels that will be added to the catalog")
fs.StringVar(&i.AvailabilityMode, "availability-mode", "", "available means that the catalog should be active and serving data")
fs.StringVar(&i.ImageRef, "image", "", "Image reference for the catalog source. Leave unset to retain the current image.")
}
110 changes: 108 additions & 2 deletions internal/cmd/internal/olmv1/printing.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ package olmv1
import (
"cmp"
"fmt"
"io"
"os"
"slices"
"sort"
"strings"
"text/tabwriter"
"time"

"github.com/blang/semver/v4"
olmv1 "github.com/operator-framework/operator-controller/api/v1"
"github.com/operator-framework/operator-registry/alpha/declcfg"
"github.com/operator-framework/operator-registry/alpha/property"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/duration"

olmv1 "github.com/operator-framework/operator-controller/api/v1"
"k8s.io/apimachinery/pkg/util/json"
)

func printFormattedExtensions(extensions ...olmv1.ClusterExtension) {
Expand Down Expand Up @@ -63,6 +68,107 @@ func printFormattedCatalogs(catalogs ...olmv1.ClusterCatalog) {
_ = tw.Flush()
}

func printFormattedDeclCfg(w io.Writer, catalogDcfg map[string]*declcfg.DeclarativeConfig, listVersions bool) {
tw := tabwriter.NewWriter(w, 3, 4, 2, ' ', 0)
if listVersions {
_, _ = fmt.Fprint(tw, "PACKAGE\tCATALOG\tPROVIDER\tVERSION\n")
} else {
_, _ = fmt.Fprint(tw, "PACKAGE\tCATALOG\tPROVIDER\tCHANNELS\n")
}
sortedCatalogs := []string{}
for catalogName := range catalogDcfg {
sortedCatalogs = append(sortedCatalogs, catalogName)
}
sort.Strings(sortedCatalogs)
for _, catalogName := range sortedCatalogs {
dcfg := catalogDcfg[catalogName]
type dcfgPrintMeta struct {
provider string
channels []string
versions []semver.Version
}
pkgProviders := map[string]*dcfgPrintMeta{}
sort.SliceStable(dcfg.Packages, func(i, j int) bool {
return dcfg.Packages[i].Name < dcfg.Packages[j].Name
})

if listVersions {
for _, b := range dcfg.Bundles {
if pkgProviders[b.Package] == nil {
pkgProviders[b.Package] = &dcfgPrintMeta{}
}
if pkgProviders[b.Package].versions == nil {
pkgProviders[b.Package].versions = []semver.Version{}
}
for _, versionProp := range b.Properties {
if versionProp.Type == property.TypePackage {
var pkgProp property.Package
if err := json.Unmarshal(versionProp.Value, &pkgProp); err == nil && len(pkgProp.Version) > 0 {
if parsedVersion, err := semver.Parse(pkgProp.Version); err == nil {
pkgProviders[b.Package].versions = append(pkgProviders[b.Package].versions, parsedVersion)
}
}
continue
}
if versionProp.Type == property.TypeCSVMetadata {
var pkgProp property.CSVMetadata
if err := json.Unmarshal(versionProp.Value, &pkgProp); err == nil && len(pkgProp.Provider.Name) > 0 {
pkgProviders[b.Package].provider = pkgProp.Provider.Name
}
continue
}
}
}
} else {
for _, c := range dcfg.Channels {
if pkgProviders[c.Package] == nil {
pkgProviders[c.Package] = &dcfgPrintMeta{}
}
if pkgProviders[c.Package].channels == nil {
pkgProviders[c.Package].channels = []string{}
}
pkgProviders[c.Package].channels = append(pkgProviders[c.Package].channels, c.Name)
}
}

for _, p := range dcfg.Packages {
if listVersions {
sort.SliceStable(pkgProviders[p.Name].versions, func(i, j int) bool {
return pkgProviders[p.Name].versions[i].GT(pkgProviders[p.Name].versions[j])
})
for _, v := range pkgProviders[p.Name].versions {
_, _ = fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n",
p.Name,
catalogName,
pkgProviders[p.Name].provider,
v)
}
} else {
sort.Strings(pkgProviders[p.Name].channels)
_, _ = fmt.Fprintf(tw, "%s\t%s\t%s\t%s\n",
p.Name,
catalogName,
pkgProviders[p.Name].provider,
strings.Join(pkgProviders[p.Name].channels, ","))
}
}
}
_ = tw.Flush()
}

func printDeclCfgJSON(w io.Writer, catalogDcfg map[string]*declcfg.DeclarativeConfig) {
for _, dcfg := range catalogDcfg {
_ = declcfg.WriteJSON(*dcfg, w)
}
}

func printDeclCfgYAML(w io.Writer, catalogDcfg map[string]*declcfg.DeclarativeConfig) {
for _, dcfg := range catalogDcfg {
_ = declcfg.WriteYAML(*dcfg, w)
_, _ = w.Write([]byte("---\n"))
}
}

// sortExtensions sorts extensions in place and uses the following sorting order:
// name (asc), version (desc)
func sortExtensions(extensions []olmv1.ClusterExtension) {
Expand Down
9 changes: 9 additions & 0 deletions internal/cmd/olmv1.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func newOlmV1Cmd(cfg *action.Configuration) *cobra.Command {
}
updateCmd.AddCommand(
olmv1.NewExtensionUpdateCmd(cfg),
olmv1.NewCatalogUpdateCmd(cfg),
)

installCmd := &cobra.Command{
Expand All @@ -57,12 +58,20 @@ func newOlmV1Cmd(cfg *action.Configuration) *cobra.Command {
}
installCmd.AddCommand(olmv1.NewExtensionInstallCmd(cfg))

searchCmd := &cobra.Command{
Use: "search",
Short: "Search for packages",
Long: "Search one or all available catalogs for packages or versions",
}
searchCmd.AddCommand(olmv1.NewCatalogSearchCmd(cfg))

cmd.AddCommand(
installCmd,
getCmd,
createCmd,
deleteCmd,
updateCmd,
searchCmd,
)

return cmd
Expand Down
Loading
Loading