From 26644d7cb85d5935bb59c7891ac9a81d6092673c Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Sun, 29 Mar 2020 18:21:44 -0500 Subject: podman v2 image tag and untag add the ability to tag and untag images in podmanv2 Signed-off-by: Brent Baude --- cmd/podmanV2/images/tag.go | 34 ++++++++++++++++++++++++ cmd/podmanV2/images/untag.go | 33 +++++++++++++++++++++++ pkg/api/handlers/libpod/images.go | 26 +++++++++++++++++++ pkg/api/server/register_images.go | 34 ++++++++++++++++++++++++ pkg/bindings/images/images.go | 16 ++++++++++++ pkg/domain/entities/engine_image.go | 2 ++ pkg/domain/entities/images.go | 3 +++ pkg/domain/infra/abi/images.go | 25 ++++++++++++++++++ pkg/domain/infra/tunnel/images.go | 52 +++++++++++++++++++++++++++++++++++++ 9 files changed, 225 insertions(+) create mode 100644 cmd/podmanV2/images/tag.go create mode 100644 cmd/podmanV2/images/untag.go diff --git a/cmd/podmanV2/images/tag.go b/cmd/podmanV2/images/tag.go new file mode 100644 index 000000000..f66fe7857 --- /dev/null +++ b/cmd/podmanV2/images/tag.go @@ -0,0 +1,34 @@ +package images + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + tagDescription = "Adds one or more additional names to locally-stored image." + tagCommand = &cobra.Command{ + Use: "tag [flags] IMAGE TARGET_NAME [TARGET_NAME...]", + Short: "Add an additional name to a local image", + Long: tagDescription, + RunE: tag, + Args: cobra.MinimumNArgs(2), + Example: `podman tag 0e3bbc2 fedora:latest + podman tag imageID:latest myNewImage:newTag + podman tag httpd myregistryhost:5000/fedora/httpd:v2`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: tagCommand, + }) + tagCommand.SetHelpTemplate(registry.HelpTemplate()) + tagCommand.SetUsageTemplate(registry.UsageTemplate()) +} + +func tag(cmd *cobra.Command, args []string) error { + return registry.ImageEngine().Tag(registry.GetContext(), args[0], args[1:], entities.ImageTagOptions{}) +} diff --git a/cmd/podmanV2/images/untag.go b/cmd/podmanV2/images/untag.go new file mode 100644 index 000000000..c84827bb3 --- /dev/null +++ b/cmd/podmanV2/images/untag.go @@ -0,0 +1,33 @@ +package images + +import ( + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + untagCommand = &cobra.Command{ + Use: "untag [flags] IMAGE [NAME...]", + Short: "Remove a name from a local image", + Long: "Removes one or more names from a locally-stored image.", + RunE: untag, + Args: cobra.MinimumNArgs(1), + Example: `podman untag 0e3bbc2 + podman untag imageID:latest otherImageName:latest + podman untag httpd myregistryhost:5000/fedora/httpd:v2`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: untagCommand, + }) + untagCommand.SetHelpTemplate(registry.HelpTemplate()) + untagCommand.SetUsageTemplate(registry.UsageTemplate()) +} + +func untag(cmd *cobra.Command, args []string) error { + return registry.ImageEngine().Untag(registry.GetContext(), args[0], args[1:], entities.ImageUntagOptions{}) +} diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index bb54450da..bc227d9a1 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -510,3 +510,29 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { } utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: commitImage.ID()}) // nolint } + +func UntagImage(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + name := utils.GetName(r) + newImage, err := runtime.ImageRuntime().NewFromLocal(name) + if err != nil { + utils.ImageNotFound(w, name, errors.Wrapf(err, "Failed to find image %s", name)) + return + } + tag := "latest" + if len(r.Form.Get("tag")) > 0 { + tag = r.Form.Get("tag") + } + if len(r.Form.Get("repo")) < 1 { + utils.Error(w, "repo tag is required", http.StatusBadRequest, errors.New("repo parameter is required to tag an image")) + return + } + repo := r.Form.Get("repo") + tagName := fmt.Sprintf("%s:%s", repo, tag) + if err := newImage.UntagImage(tagName); err != nil { + utils.Error(w, "failed to untag", http.StatusInternalServerError, err) + return + } + utils.WriteResponse(w, http.StatusCreated, "") +} diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index e8dfe2fa8..74b245a77 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -1019,5 +1019,39 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // 500: // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/commit"), s.APIHandler(libpod.CommitContainer)).Methods(http.MethodPost) + // swagger:operation POST /libpod/images/{name:.*}/untag libpod libpodUntagImage + // --- + // tags: + // - images + // summary: Untag an image + // description: Untag an image + // parameters: + // - in: path + // name: name:.* + // type: string + // required: true + // description: the name or ID of the container + // - in: query + // name: repo + // type: string + // description: the repository to untag + // - in: query + // name: tag + // type: string + // description: the name of the tag to untag + // produces: + // - application/json + // responses: + // 201: + // description: no error + // 400: + // $ref: '#/responses/BadParamError' + // 404: + // $ref: '#/responses/NoSuchImage' + // 409: + // $ref: '#/responses/ConflictError' + // 500: + // $ref: '#/responses/InternalError' + r.Handle(VersionedPath("/libpod/images/{name:.*}/untag"), s.APIHandler(libpod.UntagImage)).Methods(http.MethodPost) return nil } diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 5f5c4443f..9ef85b955 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -197,6 +197,22 @@ func Tag(ctx context.Context, nameOrID, tag, repo string) error { return response.Process(nil) } +// Untag removes a name from locally-stored image. Both the tag and repo parameters are required. +func Untag(ctx context.Context, nameOrID, tag, repo string) error { + conn, err := bindings.GetClient(ctx) + if err != nil { + return err + } + params := url.Values{} + params.Set("tag", tag) + params.Set("repo", repo) + response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/untag", params, nameOrID) + if err != nil { + return err + } + return response.Process(nil) +} + func Build(nameOrId string) {} // Imports adds the given image to the local image store. This can be done by file and the given reader diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index cdb8d7f0d..6547cbfaf 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -11,4 +11,6 @@ type ImageEngine interface { List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error) Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error) Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error) + Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error + Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 8e3f49be3..41579398b 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -171,3 +171,6 @@ type ImagePruneReport struct { Report Report Size int64 } + +type ImageTagOptions struct{} +type ImageUntagOptions struct{} diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index ef2879246..38b6c151d 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -264,3 +264,28 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti // copy(report.Report.Id, id) // return &report, nil // } + +func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, options entities.ImageTagOptions) error { + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) + if err != nil { + return err + } + for _, tag := range tags { + if err := newImage.TagImage(tag); err != nil { + return err + } + } + return nil +} +func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error { + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) + if err != nil { + return err + } + for _, tag := range tags { + if err := newImage.UntagImage(tag); err != nil { + return err + } + } + return nil +} diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 7638d908a..d7f281243 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -3,9 +3,11 @@ package tunnel import ( "context" + "github.com/containers/image/v5/docker/reference" images "github.com/containers/libpod/pkg/bindings/images" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/utils" + "github.com/pkg/errors" ) func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) { @@ -93,3 +95,53 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti } return &entities.ImagePullReport{Images: pulledImages}, nil } + +func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, options entities.ImageTagOptions) error { + for _, newTag := range tags { + var ( + tag, repo string + ) + ref, err := reference.Parse(newTag) + if err != nil { + return err + } + if t, ok := ref.(reference.Tagged); ok { + tag = t.Tag() + } + if r, ok := ref.(reference.Named); ok { + repo = r.Name() + } + if len(repo) < 1 { + return errors.Errorf("invalid image name %q", nameOrId) + } + if err := images.Tag(ir.ClientCxt, nameOrId, tag, repo); err != nil { + return err + } + } + return nil +} + +func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error { + for _, newTag := range tags { + var ( + tag, repo string + ) + ref, err := reference.Parse(newTag) + if err != nil { + return err + } + if t, ok := ref.(reference.Tagged); ok { + tag = t.Tag() + } + if r, ok := ref.(reference.Named); ok { + repo = r.Name() + } + if len(repo) < 1 { + return errors.Errorf("invalid image name %q", nameOrId) + } + if err := images.Untag(ir.ClientCxt, nameOrId, tag, repo); err != nil { + return err + } + } + return nil +} -- cgit v1.2.3-54-g00ecf