From a7c267de29099d38d14463295f1fcffad769b423 Mon Sep 17 00:00:00 2001 From: Alexander Jung Date: Tue, 19 Dec 2023 18:21:51 +0100 Subject: [PATCH] refactor: Clean up `kraft pkg info` by using native `Catalog` This commit effectively reverses #536 in favour of using the existing package manager's `Catalog` method. Since packages already expose a `Columns` method, this information can be used to "show" information about a specific named package. Simultaneously, the `Catalog` method and the information returned by the implementing `pack.Package` works for all current package managers. Secondly, wrap the invocations of `Catalog` with `tui.processtree` which is useful when used with the `-u|--update` flag for very long wait times and to help indicate to the user the longevity of the invocation. Finally, utilize the newly introduced `PrintPackages` method which was previously ripped from `kraft pkg ls`. Signed-off-by: Alexander Jung --- internal/cli/kraft/pkg/info/info.go | 132 ++++++++++++---------------- manifest/manager.go | 42 --------- oci/manager.go | 4 - packmanager/manager.go | 3 - packmanager/umbrella.go | 25 ------ 5 files changed, 54 insertions(+), 152 deletions(-) diff --git a/internal/cli/kraft/pkg/info/info.go b/internal/cli/kraft/pkg/info/info.go index 1ad0c87c0..cc2f486be 100644 --- a/internal/cli/kraft/pkg/info/info.go +++ b/internal/cli/kraft/pkg/info/info.go @@ -7,25 +7,23 @@ package info import ( "context" - "encoding/json" "fmt" - "os" - "path" - "reflect" - "strings" "github.com/MakeNowJust/heredoc" "github.com/spf13/cobra" - "gopkg.in/yaml.v2" "kraftkit.sh/cmdfactory" "kraftkit.sh/config" + pkgutils "kraftkit.sh/internal/cli/kraft/pkg/utils" "kraftkit.sh/iostreams" - "kraftkit.sh/manifest" + "kraftkit.sh/log" + "kraftkit.sh/pack" "kraftkit.sh/packmanager" + "kraftkit.sh/tui/processtree" ) type InfoOptions struct { - Output string `long:"output" short:"o" usage:"Set output format" default:"yaml"` + Output string `long:"output" short:"o" usage:"Set output format" default:"table"` + Update bool `long:"update" short:"u" usage:"Get latest information about components before listing results"` } // Info shows package information. @@ -45,7 +43,7 @@ func New() *cobra.Command { Long: heredoc.Doc(` Shows a Unikraft package like library, core etc details `), - Args: cmdfactory.MinimumArgs(1, "package name is not specified to show information"), + Args: cmdfactory.MinimumArgs(1, "package name(s) not specified"), Example: heredoc.Doc(` # Shows details for the library nginx $ kraft pkg info nginx`), @@ -60,86 +58,64 @@ func New() *cobra.Command { return cmd } -func (opts *InfoOptions) Pre(cmd *cobra.Command, _ []string) error { - ctx, err := packmanager.WithDefaultUmbrellaManagerInContext(cmd.Context()) +func (opts *InfoOptions) Run(ctx context.Context, args []string) error { + ctx, err := packmanager.WithDefaultUmbrellaManagerInContext(ctx) if err != nil { return err } - cmd.SetContext(ctx) - return nil -} + parallel := !config.G[config.KraftKit](ctx).NoParallel + norender := log.LoggerTypeFromString(config.G[config.KraftKit](ctx).Log.Type) != log.FANCY + + var searches []*processtree.ProcessTreeItem + var packs []pack.Package + + for _, arg := range args { + search := processtree.NewProcessTreeItem( + fmt.Sprintf("finding %s", arg), "", + func(ctx context.Context) error { + more, err := packmanager.G(ctx).Catalog(ctx, + packmanager.WithName(arg), + packmanager.WithUpdate(opts.Update), + ) + if err != nil { + return err + } -func (opts *InfoOptions) Run(ctx context.Context, args []string) error { - if len(args) == 0 { - return fmt.Errorf("package name is not specified to show information") - } else if opts.Output != "json" && opts.Output != "yaml" { - return fmt.Errorf("specified output format is not supported") + if len(more) == 0 { + return fmt.Errorf("could not find: %s", arg) + } + + packs = append(packs, more...) + + return nil + }, + ) + + searches = append(searches, search) } - metadata, err := packmanager.G(ctx).Catalog(ctx, - packmanager.WithName(args[0]), + treemodel, err := processtree.NewProcessTree( + ctx, + []processtree.ProcessTreeOption{ + processtree.IsParallel(parallel), + processtree.WithRenderer(norender), + processtree.WithFailFast(false), + processtree.WithHideOnSuccess(true), + }, + searches..., ) if err != nil { return err } - if metadata != nil { - var byteCode []byte - var manifestStruct *manifest.Manifest - var origin string - value := reflect.ValueOf(metadata).Elem() - numFields := value.NumField() - structType := value.Type() - - for i := 0; i < numFields; i++ { - if structType.Field(i).Name == "Origin" { - origin = value.Field(i).String() - } - } - - if len(origin) > 0 && !strings.HasPrefix(origin, "http") { - var indexYaml manifest.ManifestIndex - manifestIndexYamlPath := path.Join(config.G[config.KraftKit](ctx).Paths.Manifests, "index.yaml") - indexbyteCode, err := os.ReadFile(manifestIndexYamlPath) - if err != nil { - return err - } - if err = yaml.Unmarshal(indexbyteCode, &indexYaml); err != nil { - return err - } - for _, manifestObj := range indexYaml.Manifests { - if args[0] == manifestObj.Name { - manifestYamlPath := path.Join(config.G[config.KraftKit](ctx).Paths.Manifests, manifestObj.Manifest) - byteCode, err = os.ReadFile(manifestYamlPath) - if err != nil { - return err - } - manifestStruct, err = manifest.NewManifestFromBytes(ctx, byteCode) - if err != nil { - return err - } - break - } - } - } else if len(origin) > 0 { - manifestStruct, err = manifest.NewManifestFromURL(ctx, origin) - if err != nil { - return err - } - } - - if opts.Output == "json" { - byteCode, err = json.Marshal(manifestStruct) - } else { - byteCode, err = yaml.Marshal(manifestStruct) - } - - if err != nil { - return err - } - - fmt.Fprint(iostreams.G(ctx).Out, string(byteCode)+"\n") + if err := treemodel.Start(); err != nil { + return fmt.Errorf("could not complete search: %v", err) } - return nil + + if len(packs) == 0 { + return fmt.Errorf("could not find package(s): %v", args) + } + + return pkgutils.PrintPackages(ctx, iostreams.G(ctx).Out, opts.Output, packs...) } diff --git a/manifest/manager.go b/manifest/manager.go index 665260f9d..0d1cb2665 100644 --- a/manifest/manager.go +++ b/manifest/manager.go @@ -499,45 +499,3 @@ func (m *manifestManager) LocalManifestIndex(ctx context.Context) string { func (m *manifestManager) Format() pack.PackageFormat { return ManifestFormat } - -func (m *manifestManager) Show(ctx context.Context, outputFormat string, qopts ...packmanager.QueryOption) (any, error) { - var query packmanager.Query - var metadata any - - for _, qopt := range qopts { - qopt(&query) - } - - packages, err := m.Catalog(ctx, packmanager.WithUpdate(query.Update())) - if err != nil { - return nil, err - } - - // Sort packages by type, name, version, format - sort.Slice(packages, func(i, j int) bool { - if packages[i].Type() != packages[j].Type() { - return packages[i].Type() < packages[j].Type() - } - - if packages[i].Name() != packages[j].Name() { - return packages[i].Name() < packages[j].Name() - } - - if packages[i].Version() != packages[j].Version() { - return packages[i].Version() < packages[j].Version() - } - - return packages[i].Format() < packages[j].Format() - }) - - for _, pack := range packages { - if query.Name() == pack.Name() { - metadata = pack.Metadata() - } - } - - if metadata == nil { - return nil, fmt.Errorf("%s package information not found", query.Name()) - } - return metadata, nil -} diff --git a/oci/manager.go b/oci/manager.go index fd6582ea2..a05f4d0f8 100644 --- a/oci/manager.go +++ b/oci/manager.go @@ -712,7 +712,3 @@ func (manager *ociManager) From(pack.PackageFormat) (packmanager.PackageManager, func (manager *ociManager) Format() pack.PackageFormat { return OCIFormat } - -func (manager *ociManager) Show(ctx context.Context, outputFormat string, qopts ...packmanager.QueryOption) (any, error) { - return nil, fmt.Errorf("show is not implemented for ociManager") -} diff --git a/packmanager/manager.go b/packmanager/manager.go index f68990ec9..2ecbb225e 100644 --- a/packmanager/manager.go +++ b/packmanager/manager.go @@ -58,7 +58,4 @@ type PackageManager interface { // Format returns the name of the implementation. Format() pack.PackageFormat - - // Show package information. - Show(context.Context, string, ...QueryOption) (any, error) } diff --git a/packmanager/umbrella.go b/packmanager/umbrella.go index 231ca3b5f..ba5a98a78 100644 --- a/packmanager/umbrella.go +++ b/packmanager/umbrella.go @@ -296,28 +296,3 @@ func NewUmbrellaManager(ctx context.Context, constructors []func(*UmbrellaManage return u, nil } - -func (u UmbrellaManager) Show(ctx context.Context, outputFormat string, qopts ...QueryOption) (any, error) { - var metadatas []any - - for _, manager := range u.packageManagers { - log.G(ctx).WithFields(logrus.Fields{ - "format": manager.Format(), - }).Tracef("showing package information") - - metadata, err := manager.Show(ctx, outputFormat, qopts...) - if err != nil { - log.G(ctx). - WithField("format", manager.Format()). - Debugf("could not show package: %v", err) - continue - } - - metadatas = append(metadatas, metadata) - } - if len(metadatas) == 0 { - return nil, fmt.Errorf("no information found about requested package") - } - - return metadatas[0], nil -}