spiegel_podman/cmd/podman/artifact/list.go
Paul Holzinger 34de0feda5
cmd/podman: refactor Context handling
The PodmanOptionsKey is never used anywhere so it is pointless to add
this. Second having several functions to return the same context makes
no sense so fold them all into one. Lastly create the context once and
always return the same one instead of having to nil check each time.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2025-02-12 14:01:10 +01:00

143 lines
3.8 KiB
Go

package artifact
import (
"fmt"
"os"
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/report"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/podman/v5/cmd/podman/common"
"github.com/containers/podman/v5/cmd/podman/registry"
"github.com/containers/podman/v5/cmd/podman/validate"
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/docker/go-units"
"github.com/spf13/cobra"
)
var (
listCmd = &cobra.Command{
Use: "ls [options]",
Aliases: []string{"list"},
Short: "List OCI artifacts",
Long: "List OCI artifacts in local store",
RunE: list,
Args: validate.NoArgs,
ValidArgsFunction: completion.AutocompleteNone,
Example: `podman artifact ls`,
Annotations: map[string]string{registry.EngineMode: registry.ABIMode},
}
listFlag = listFlagType{}
)
type listFlagType struct {
format string
noHeading bool
noTrunc bool
}
type artifactListOutput struct {
Digest string
Repository string
Size string
Tag string
}
var (
defaultArtifactListOutputFormat = "{{range .}}{{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.Size}}\n{{end -}}"
)
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: listCmd,
Parent: artifactCmd,
})
flags := listCmd.Flags()
formatFlagName := "format"
flags.StringVar(&listFlag.format, formatFlagName, defaultArtifactListOutputFormat, "Format volume output using JSON or a Go template")
_ = listCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&artifactListOutput{}))
flags.BoolVarP(&listFlag.noHeading, "noheading", "n", false, "Do not print column headings")
flags.BoolVar(&listFlag.noTrunc, "no-trunc", false, "Do not truncate output")
}
func list(cmd *cobra.Command, _ []string) error {
reports, err := registry.ImageEngine().ArtifactList(registry.Context(), entities.ArtifactListOptions{})
if err != nil {
return err
}
return outputTemplate(cmd, reports)
}
func outputTemplate(cmd *cobra.Command, lrs []*entities.ArtifactListReport) error {
var err error
artifacts := make([]artifactListOutput, 0)
for _, lr := range lrs {
var (
tag string
)
artifactName, err := lr.Artifact.GetName()
if err != nil {
return err
}
repo, err := reference.Parse(artifactName)
if err != nil {
return err
}
named, ok := repo.(reference.Named)
if !ok {
return fmt.Errorf("%q is an invalid artifact name", artifactName)
}
if tagged, ok := named.(reference.Tagged); ok {
tag = tagged.Tag()
}
// Note: Right now we only support things that are single manifests
// We should certainly expand this support for things like arch, etc
// as we move on
artifactDigest, err := lr.Artifact.GetDigest()
if err != nil {
return err
}
artifactHash := artifactDigest.Encoded()[0:12]
// If the user does not want truncated hashes
if listFlag.noTrunc {
artifactHash = artifactDigest.Encoded()
}
artifacts = append(artifacts, artifactListOutput{
Digest: artifactHash,
Repository: named.Name(),
Size: units.HumanSize(float64(lr.Artifact.TotalSizeBytes())),
Tag: tag,
})
}
headers := report.Headers(artifactListOutput{}, map[string]string{
"REPOSITORY": "REPOSITORY",
"Tag": "TAG",
"Size": "SIZE",
"Digest": "DIGEST",
})
rpt := report.New(os.Stdout, cmd.Name())
defer rpt.Flush()
switch {
case cmd.Flag("format").Changed:
rpt, err = rpt.Parse(report.OriginUser, listFlag.format)
default:
rpt, err = rpt.Parse(report.OriginPodman, listFlag.format)
}
if err != nil {
return err
}
if rpt.RenderHeaders && !listFlag.noHeading {
if err := rpt.Execute(headers); err != nil {
return fmt.Errorf("failed to write report column headers: %w", err)
}
}
return rpt.Execute(artifacts)
}