Skip to content

Commit

Permalink
feat: Added a subcommand show for pkg
Browse files Browse the repository at this point in the history
Signed-off-by: sahil <mohdssahil1@gmail.com>
  • Loading branch information
MdSahil-oss committed Dec 16, 2023
1 parent 9c23f29 commit 4db9276
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 9 deletions.
2 changes: 2 additions & 0 deletions internal/cli/kraft/pkg/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"kraftkit.sh/internal/cli/kraft/pkg/pull"
"kraftkit.sh/internal/cli/kraft/pkg/push"
"kraftkit.sh/internal/cli/kraft/pkg/remove"
"kraftkit.sh/internal/cli/kraft/pkg/show"
"kraftkit.sh/internal/cli/kraft/pkg/source"
"kraftkit.sh/internal/cli/kraft/pkg/unsource"
"kraftkit.sh/internal/cli/kraft/pkg/update"
Expand Down Expand Up @@ -242,6 +243,7 @@ func NewCmd() *cobra.Command {
cmd.AddCommand(source.NewCmd())
cmd.AddCommand(unsource.NewCmd())
cmd.AddCommand(update.NewCmd())
cmd.AddCommand(show.New())

cmd.Flags().Var(
cmdfactory.NewEnumFlag[packmanager.MergeStrategy](
Expand Down
136 changes: 136 additions & 0 deletions internal/cli/kraft/pkg/show/show.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package show

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"
"kraftkit.sh/iostreams"
"kraftkit.sh/manifest"
"kraftkit.sh/packmanager"
)

type ShowOptions struct {
Output string `long:"output" short:"o" usage:"Set output format" default:"yaml"`
}

// Show shows a package manifest.
func Show(ctx context.Context, opts *ShowOptions, args ...string) error {
if opts == nil {
opts = &ShowOptions{}
}

return opts.Run(ctx, args)
}

func New() *cobra.Command {
cmd, err := cmdfactory.New(&ShowOptions{}, 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
`),
Args: cmdfactory.MinimumArgs(1, "package name is not specified to show information"),
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 *ShowOptions) Pre(cmd *cobra.Command, _ []string) error {
ctx, err := packmanager.WithDefaultUmbrellaManagerInContext(cmd.Context())
if err != nil {
return err
}

cmd.SetContext(ctx)
return nil
}

func (opts *ShowOptions) Run(ctx context.Context, 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")
}

metadata, err := packmanager.G(ctx).Show(ctx, opts.Output, packmanager.WithName(args[0]))
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")
}
return nil
}
42 changes: 42 additions & 0 deletions manifest/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -499,3 +499,45 @@ 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
}
16 changes: 8 additions & 8 deletions manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,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.
Expand Down
4 changes: 4 additions & 0 deletions oci/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -712,3 +712,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")
}
3 changes: 3 additions & 0 deletions packmanager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,7 @@ type PackageManager interface {

// Format returns the name of the implementation.
Format() pack.PackageFormat

// Show package information.
Show(context.Context, string, ...QueryOption) (any, error)
}
2 changes: 1 addition & 1 deletion packmanager/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (query *Query) KConfig() []string {
return query.kConfig
}

// Update indicates whether the package manager should use use remote manifests
// Update indicates whether the package manager should use remote manifests
// when making its query.
func (query *Query) Update() bool {
return query.update
Expand Down
26 changes: 26 additions & 0 deletions packmanager/umbrella.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/sirupsen/logrus"

"kraftkit.sh/log"

"kraftkit.sh/pack"
"kraftkit.sh/unikraft/component"
)
Expand Down Expand Up @@ -295,3 +296,28 @@ 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
}

0 comments on commit 4db9276

Please sign in to comment.