diff options
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 84 | ||||
-rw-r--r-- | pkg/api/server/register_images.go | 11 | ||||
-rw-r--r-- | pkg/bindings/containers/commit.go | 49 | ||||
-rw-r--r-- | pkg/bindings/containers/types.go | 13 | ||||
-rw-r--r-- | pkg/bindings/test/pods_test.go | 3 |
5 files changed, 156 insertions, 4 deletions
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index cfd3b993e..b6f2f58e8 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -10,12 +10,15 @@ import ( "strconv" "strings" + "github.com/containers/buildah" "github.com/containers/image/v5/docker" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" + image2 "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" "github.com/containers/libpod/pkg/util" @@ -416,3 +419,84 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, res) } + +func CommitContainer(w http.ResponseWriter, r *http.Request) { + var ( + destImage string + mimeType string + ) + decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + query := struct { + Author string `schema:"author"` + Changes []string `schema:"changes"` + Comment string `schema:"comment"` + Container string `schema:"container"` + Format string `schema:"format"` + Pause bool `schema:"pause"` + Repo string `schema:"repo"` + Tag string `schema:"tag"` + }{ + Format: "oci", + } + + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) + return + } + rtc, err := runtime.GetConfig() + if err != nil { + utils.Error(w, "failed to get runtime config", http.StatusInternalServerError, errors.Wrap(err, "failed to get runtime config")) + return + } + sc := image2.GetSystemContext(rtc.SignaturePolicyPath, "", false) + tag := "latest" + options := libpod.ContainerCommitOptions{ + Pause: true, + } + switch query.Format { + case "oci": + mimeType = buildah.OCIv1ImageManifest + if len(query.Comment) > 0 { + utils.InternalServerError(w, errors.New("messages are only compatible with the docker image format (-f docker)")) + return + } + case "docker": + mimeType = manifest.DockerV2Schema2MediaType + default: + utils.InternalServerError(w, errors.Errorf("unrecognized image format %q", query.Format)) + return + } + options.CommitOptions = buildah.CommitOptions{ + SignaturePolicyPath: rtc.SignaturePolicyPath, + ReportWriter: os.Stderr, + SystemContext: sc, + PreferredManifestType: mimeType, + } + + if len(query.Tag) > 0 { + tag = query.Tag + } + options.Message = query.Comment + options.Author = query.Author + options.Pause = query.Pause + options.Changes = query.Changes + ctr, err := runtime.LookupContainer(query.Container) + if err != nil { + utils.Error(w, "failed to lookup container", http.StatusNotFound, err) + return + } + + // I know mitr hates this ... but doing for now + if len(query.Repo) > 1 { + destImage = fmt.Sprintf("%s:%s", query.Repo, tag) + } + + commitImage, err := ctr.Commit(r.Context(), destImage, options) + if err != nil && !strings.Contains(err.Error(), "is not running") { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "CommitFailure")) + return + } + utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: commitImage.ID()}) // nolint +} diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index e6ad045a2..87ddf5add 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -978,6 +978,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // name: container // type: string // description: the name or ID of a container + // required: true // - in: query // name: repo // type: string @@ -1000,8 +1001,14 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: pause the container before committing it // - in: query // name: changes + // description: instructions to apply while committing in Dockerfile format (i.e. "CMD=/bin/foo") + // type: array + // items: + // type: string + // - in: query + // name: format // type: string - // description: instructions to apply while committing in Dockerfile format + // description: format of the image manifest and metadata (default "oci") // produces: // - application/json // responses: @@ -1011,6 +1018,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/NoSuchImage' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/commit"), s.APIHandler(compat.CommitContainer)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/commit"), s.APIHandler(libpod.CommitContainer)).Methods(http.MethodPost) return nil } diff --git a/pkg/bindings/containers/commit.go b/pkg/bindings/containers/commit.go new file mode 100644 index 000000000..12c25f842 --- /dev/null +++ b/pkg/bindings/containers/commit.go @@ -0,0 +1,49 @@ +package containers + +import ( + "context" + "net/http" + "net/url" + "strconv" + + "github.com/containers/libpod/pkg/api/handlers" + "github.com/containers/libpod/pkg/bindings" +) + +// Commit creates a container image from a container. The container is defined by nameOrId. Use +// the CommitOptions for finer grain control on characteristics of the resulting image. +func Commit(ctx context.Context, nameOrId string, options CommitOptions) (handlers.IDResponse, error) { + id := handlers.IDResponse{} + conn, err := bindings.GetClient(ctx) + if err != nil { + return id, err + } + params := url.Values{} + params.Set("container", nameOrId) + if options.Author != nil { + params.Set("author", *options.Author) + } + for _, change := range options.Changes { + params.Set("changes", change) + } + if options.Comment != nil { + params.Set("comment", *options.Comment) + } + if options.Format != nil { + params.Set("format", *options.Format) + } + if options.Pause != nil { + params.Set("pause", strconv.FormatBool(*options.Pause)) + } + if options.Repo != nil { + params.Set("repo", *options.Repo) + } + if options.Tag != nil { + params.Set("tag", *options.Tag) + } + response, err := conn.DoRequest(nil, http.MethodPost, "/commit", params) + if err != nil { + return id, err + } + return id, response.Process(&id) +} diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 87342f7ea..31daaf565 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -11,3 +11,16 @@ type LogOptions struct { Timestamps *bool Until *string } + +// CommitOptions describe details about the resulting commited +// image as defined by repo and tag. None of these options +// are required. +type CommitOptions struct { + Author *string + Changes []string + Comment *string + Format *string + Pause *bool + Repo *string + Tag *string +} diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index 29cfcdbbc..e94048a9c 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -80,8 +80,7 @@ var _ = Describe("Podman pods", func() { // The test validates the list pod endpoint with passing filters as the params. It("List pods with filters", func() { var ( - newpod2 string = "newpod2" - trueFlag = true + newpod2 string = "newpod2" ) bt.Podcreate(&newpod2) _, err = bt.RunTopContainer(nil, &bindings.PTrue, &newpod) |