From baf8395417b08c261c6255a37648665aa3ddc650 Mon Sep 17 00:00:00 2001 From: sahil Date: Sat, 24 Jun 2023 23:15:03 +0530 Subject: [PATCH] feat: Added a subcommand show for pkg Signed-off-by: sahil --- cmd/kraft/pkg/pkg.go | 2 + cmd/kraft/pkg/show/show.go | 95 ++++++++++++++++++++++++++++++++++++++ manifest/manager.go | 4 ++ manifest/manifest.go | 16 +++---- oci/manager.go | 4 ++ packmanager/manager.go | 3 ++ packmanager/umbrella.go | 44 ++++++++++++++++++ 7 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 cmd/kraft/pkg/show/show.go diff --git a/cmd/kraft/pkg/pkg.go b/cmd/kraft/pkg/pkg.go index 9df0f5519..09c7dde57 100644 --- a/cmd/kraft/pkg/pkg.go +++ b/cmd/kraft/pkg/pkg.go @@ -26,6 +26,7 @@ import ( "kraftkit.sh/cmd/kraft/pkg/list" "kraftkit.sh/cmd/kraft/pkg/pull" "kraftkit.sh/cmd/kraft/pkg/push" + "kraftkit.sh/cmd/kraft/pkg/show" "kraftkit.sh/cmd/kraft/pkg/source" "kraftkit.sh/cmd/kraft/pkg/unsource" "kraftkit.sh/cmd/kraft/pkg/update" @@ -80,6 +81,7 @@ func New() *cobra.Command { cmd.AddCommand(source.New()) cmd.AddCommand(unsource.New()) cmd.AddCommand(update.New()) + cmd.AddCommand(show.New()) return cmd } diff --git a/cmd/kraft/pkg/show/show.go b/cmd/kraft/pkg/show/show.go new file mode 100644 index 000000000..0bb0203f1 --- /dev/null +++ b/cmd/kraft/pkg/show/show.go @@ -0,0 +1,95 @@ +package show + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + + "github.com/MakeNowJust/heredoc" + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" + "kraftkit.sh/cmdfactory" + "kraftkit.sh/iostreams" + "kraftkit.sh/manifest" + "kraftkit.sh/packmanager" +) + +type Show struct { + Output string `long:"output" short:"o" usage:"Set output format" default:"yaml"` + Cache bool `long:"local" short:"l" usage:"Search package details locally" default:"false"` +} + +func New() *cobra.Command { + cmd, err := cmdfactory.New(&Show{}, cobra.Command{ + Short: "Shows a Unikraft package", + Use: "show [FLAGS] [PACKAGE|DIR]", + Aliases: []string{"sw"}, + Long: heredoc.Doc(` + Shows a Unikraft package like library, core etc details + `), + Example: heredoc.Doc(` + # Shows details for the library nginx + $ kraft pkg show nginx`), + Annotations: map[string]string{ + cmdfactory.AnnotationHelpGroup: "pkg", + }, + }) + if err != nil { + panic(err) + } + + return cmd +} + +func (opts *Show) Pre(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + pm, err := packmanager.NewUmbrellaManager(ctx) + if err != nil { + return err + } + + cmd.SetContext(packmanager.WithPackageManager(ctx, pm)) + + return nil +} + +func (opts *Show) Run(cmd *cobra.Command, args []string) error { + opts.Output = strings.ToLower(opts.Output) + 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") + } + + ctx := cmd.Context() + + metadata, err := packmanager.G(ctx).Show(ctx, opts.Output, packmanager.WithCache(opts.Cache), packmanager.WithName(args[0])) + + if metadata != nil { + var byteCode []byte + var manifestStruct *manifest.Manifest + if opts.Cache { + manifestStruct, err = manifest.NewManifestFromFile(ctx, reflect.ValueOf(metadata).Elem().FieldByName("Origin").String()) + } else { + manifestStruct, err = manifest.NewManifestFromURL(ctx, reflect.ValueOf(metadata).Elem().FieldByName("Origin").String()) + } + 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 != nil { + return err + } + + return nil +} diff --git a/manifest/manager.go b/manifest/manager.go index 980f896f6..0143fec44 100644 --- a/manifest/manager.go +++ b/manifest/manager.go @@ -397,3 +397,7 @@ 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) { + return nil, fmt.Errorf("Show is not implemented for manifestManager") +} diff --git a/manifest/manifest.go b/manifest/manifest.go index aba06ee3c..045c0a52d 100644 --- a/manifest/manifest.go +++ b/manifest/manifest.go @@ -26,32 +26,32 @@ import ( type Manifest struct { // Name of the entity which this manifest represents - Name string `yaml:"name"` + Name string `yaml:"name" json:"name"` // Type of entity which this manifest represetns - Type unikraft.ComponentType `yaml:"type"` + Type unikraft.ComponentType `yaml:"type" json:"type"` // Manifest is used to point to remote manifest, allowing the manifest itself // to be retrieved by indirection. Manifest is XOR with Versions and should // be back-propagated. - Manifest string `yaml:"manifest,omitempty"` + Manifest string `yaml:"manifest,omitempty" json:"manifest,omitempty"` // Description of what this manifest represents - Description string `yaml:"description,omitempty"` + Description string `yaml:"description,omitempty" json:"description,omitempty"` // Origin represents where (and therefore how) this manifest was populated - Origin string `yaml:"origin,omitempty"` + Origin string `yaml:"origin,omitempty" json:"origin,omitempty"` // Provider is the string name of the underlying implementation providing the // contents of this manifest - Provider Provider `yaml:"provider,omitempty"` + Provider Provider `yaml:"provider,omitempty" json:"provider,omitempty"` // Channels provides multiple ways to retrieve versions. Classically this is // a separation between "staging" and "stable" - Channels []ManifestChannel `yaml:"channels,omitempty"` + Channels []ManifestChannel `yaml:"channels,omitempty" json:"channels,omitempty"` // Versions - Versions []ManifestVersion `yaml:"versions,omitempty"` + Versions []ManifestVersion `yaml:"versions,omitempty" json:"versions,omitempty"` // mopts contains additional configuration used within the implementation that // are non-exportable attributes and variables. diff --git a/oci/manager.go b/oci/manager.go index 73f5b5a7e..5ac997266 100644 --- a/oci/manager.go +++ b/oci/manager.go @@ -440,3 +440,7 @@ 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 d5748fa96..479b1c7b0 100644 --- a/packmanager/manager.go +++ b/packmanager/manager.go @@ -55,4 +55,7 @@ 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 bfc979663..85f13d9c7 100644 --- a/packmanager/umbrella.go +++ b/packmanager/umbrella.go @@ -7,10 +7,12 @@ package packmanager import ( "context" "fmt" + "sort" "github.com/sirupsen/logrus" "kraftkit.sh/log" + "kraftkit.sh/pack" "kraftkit.sh/unikraft/component" ) @@ -220,3 +222,45 @@ func (u umbrella) IsCompatible(ctx context.Context, source string, qopts ...Quer func (u umbrella) Format() pack.PackageFormat { return UmbrellaFormat } + +func (u umbrella) Show(ctx context.Context, outputFormat string, qopts ...QueryOption) (any, error) { + var query Query + var metadata any + + for _, qopt := range qopts { + qopt(&query) + } + + packages, err := u.Catalog(ctx, WithCache(query.useCache)) + 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("Package %s information not found", query.name) + } + return metadata, nil +}