-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
the podman artifact verb is used to manage OCI artifacts. the following verbs were added to `podman artifact`: * add * inspect * ls * pull * push * rm Notable items with this PR: * all artifact commands and their output are subject to change. i.e. consider all of this tech preview * there is no way to add a file to an artifact that already exists in the store. you would need to delete and recreate the artifact. * all references to artifacts names should be fully qualified names in the form of repo/name:tag (i.e. quay.io/artifact/foobar:latest) * i understand that we will likely want to be able to attribute things like arch, etc to artifact files. this function is not available yet. Many thanks to Paul Holzinger for autocompletion PRs and review PRs that fixed issues early on. Also fix up some Args function to specify the correct number of args. Signed-off-by: Paul Holzinger <[email protected]> Signed-off-by: Brent Baude <[email protected]>
- Loading branch information
Showing
29 changed files
with
2,344 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package artifact | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/containers/podman/v5/cmd/podman/common" | ||
"github.com/containers/podman/v5/cmd/podman/registry" | ||
"github.com/containers/podman/v5/pkg/domain/entities" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
addCmd = &cobra.Command{ | ||
Use: "add ARTIFACT PATH [...PATH]", | ||
Short: "Add an OCI artifact to the local store", | ||
Long: "Add an OCI artifact to the local store from the local filesystem", | ||
RunE: add, | ||
Args: cobra.MinimumNArgs(2), | ||
ValidArgsFunction: common.AutocompleteArtifactAdd, | ||
Example: `podman artifact add quay.io/myimage/myartifact:latest /tmp/foobar.txt`, | ||
} | ||
) | ||
|
||
func init() { | ||
registry.Commands = append(registry.Commands, registry.CliCommand{ | ||
Command: addCmd, | ||
Parent: artifactCmd, | ||
}) | ||
|
||
// TODO When the inspect structure has been defined, we need to uncommand and redirect this. Reminder, this | ||
// will also need to be reflected in the podman-artifact-inspect man page | ||
// _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&machine.InspectInfo{})) | ||
} | ||
|
||
func add(cmd *cobra.Command, args []string) error { | ||
report, err := registry.ImageEngine().ArtifactAdd(registry.Context(), args[0], args[1:], entities.ArtifactAddoptions{}) | ||
if err != nil { | ||
return err | ||
} | ||
fmt.Println(report.ArtifactDigest.Encoded()) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package artifact | ||
|
||
import ( | ||
"github.com/containers/podman/v5/cmd/podman/registry" | ||
"github.com/containers/podman/v5/cmd/podman/validate" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
json = registry.JSONLibrary() | ||
// Command: podman _artifact_ | ||
artifactCmd = &cobra.Command{ | ||
Use: "artifact", | ||
Short: "Manage OCI artifacts", | ||
Long: "Manage OCI artifacts", | ||
//PersistentPreRunE: validate.NoOp, | ||
RunE: validate.SubCommandExists, | ||
} | ||
) | ||
|
||
func init() { | ||
if !registry.IsRemote() { | ||
registry.Commands = append(registry.Commands, registry.CliCommand{ | ||
Command: artifactCmd, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package artifact | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/containers/podman/v5/cmd/podman/common" | ||
"github.com/containers/podman/v5/cmd/podman/registry" | ||
"github.com/containers/podman/v5/pkg/domain/entities" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
inspectCmd = &cobra.Command{ | ||
Use: "inspect [ARTIFACT...]", | ||
Short: "Inspect an OCI artifact", | ||
Long: "Provide details on an OCI artifact", | ||
RunE: inspect, | ||
Args: cobra.MinimumNArgs(1), | ||
ValidArgsFunction: common.AutocompleteArtifacts, | ||
Example: `podman artifact inspect quay.io/myimage/myartifact:latest`, | ||
} | ||
inspectFlag = inspectFlagType{} | ||
) | ||
|
||
type inspectFlagType struct { | ||
format string | ||
remote bool | ||
} | ||
|
||
func init() { | ||
registry.Commands = append(registry.Commands, registry.CliCommand{ | ||
Command: inspectCmd, | ||
Parent: artifactCmd, | ||
}) | ||
|
||
// TODO When things firm up on inspect looks, we can do a format implementation | ||
// flags := inspectCmd.Flags() | ||
// formatFlagName := "format" | ||
// flags.StringVar(&inspectFlag.format, formatFlagName, "", "Format volume output using JSON or a Go template") | ||
|
||
// This is something we wanted to do but did not seem important enough for initial PR | ||
// remoteFlagName := "remote" | ||
// flags.BoolVar(&inspectFlag.remote, remoteFlagName, false, "Inspect the image on a container image registry") | ||
|
||
// TODO When the inspect structure has been defined, we need to uncomment and redirect this. Reminder, this | ||
// will also need to be reflected in the podman-artifact-inspect man page | ||
// _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&machine.InspectInfo{})) | ||
} | ||
|
||
func inspect(cmd *cobra.Command, args []string) error { | ||
if inspectFlag.remote { | ||
return fmt.Errorf("not implemented") | ||
} | ||
|
||
if inspectFlag.format != "" { | ||
return fmt.Errorf("not implemented") | ||
} | ||
|
||
artifactOptions := entities.ArtifactInspectOptions{} | ||
inspectData, err := registry.ImageEngine().ArtifactInspect(registry.GetContext(), args[0], artifactOptions) | ||
if err != nil { | ||
return err | ||
} | ||
return printJSON(inspectData) | ||
} | ||
|
||
func printJSON(data interface{}) error { | ||
enc := json.NewEncoder(os.Stdout) | ||
// by default, json marshallers will force utf=8 from | ||
// a string. | ||
enc.SetEscapeHTML(false) | ||
enc.SetIndent("", " ") | ||
return enc.Encode(data) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
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`, | ||
} | ||
listFlag = listFlagType{} | ||
) | ||
|
||
type listFlagType struct { | ||
format string | ||
} | ||
|
||
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{})) | ||
// TODO When the inspect structure has been defined, we need to uncomment and redirect this. Reminder, this | ||
// will also need to be reflected in the podman-artifact-inspect man page | ||
// _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&machine.InspectInfo{})) | ||
} | ||
|
||
func list(cmd *cobra.Command, _ []string) error { | ||
reports, err := registry.ImageEngine().ArtifactList(registry.GetContext(), 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 | ||
} | ||
// TODO when we default to shorter ids, i would foresee a switch | ||
// like images that will show the full ids. | ||
artifacts = append(artifacts, artifactListOutput{ | ||
Digest: artifactDigest.Encoded(), | ||
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 { | ||
if err := rpt.Execute(headers); err != nil { | ||
return fmt.Errorf("failed to write report column headers: %w", err) | ||
} | ||
} | ||
return rpt.Execute(artifacts) | ||
} |
Oops, something went wrong.