diff options
Diffstat (limited to 'pkg/api')
-rw-r--r-- | pkg/api/handlers/compat/containers.go | 7 | ||||
-rw-r--r-- | pkg/api/handlers/compat/containers_archive.go | 319 | ||||
-rw-r--r-- | pkg/api/handlers/compat/containers_pause.go | 2 | ||||
-rw-r--r-- | pkg/api/handlers/compat/containers_restart.go | 2 | ||||
-rw-r--r-- | pkg/api/handlers/compat/containers_start.go | 4 | ||||
-rw-r--r-- | pkg/api/handlers/compat/containers_stop.go | 4 | ||||
-rw-r--r-- | pkg/api/handlers/compat/containers_unpause.go | 2 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images_push.go | 1 | ||||
-rw-r--r-- | pkg/api/handlers/types.go | 16 |
9 files changed, 79 insertions, 278 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 5886455e7..7a3e5dd84 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -17,6 +17,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" + "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -73,7 +74,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - utils.WriteResponse(w, http.StatusNoContent, "") + utils.WriteResponse(w, http.StatusNoContent, nil) } func ListContainers(w http.ResponseWriter, r *http.Request) { @@ -207,7 +208,7 @@ func KillContainer(w http.ResponseWriter, r *http.Request) { } } // Success - utils.WriteResponse(w, http.StatusNoContent, "") + utils.WriteResponse(w, http.StatusNoContent, nil) } func WaitContainer(w http.ResponseWriter, r *http.Request) { @@ -215,8 +216,10 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) { // /{version}/containers/(name)/wait exitCode, err := utils.WaitContainer(w, r) if err != nil { + logrus.Warnf("failed to wait on container %q: %v", mux.Vars(r)["name"], err) return } + utils.WriteResponse(w, http.StatusOK, handlers.ContainerWaitOKBody{ StatusCode: int(exitCode), Error: struct { diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go index 1dd563393..223eb2cd5 100644 --- a/pkg/api/handlers/compat/containers_archive.go +++ b/pkg/api/handlers/compat/containers_archive.go @@ -5,24 +5,16 @@ import ( "encoding/base64" "encoding/json" "fmt" - "path/filepath" - "strings" - - "github.com/containers/buildah/copier" - "github.com/containers/buildah/pkg/chrootuser" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/api/handlers/utils" - "github.com/containers/storage/pkg/idtools" - "github.com/opencontainers/runtime-spec/specs-go" - "net/http" "os" "time" + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/api/handlers/utils" + "github.com/containers/podman/v2/pkg/copy" "github.com/gorilla/schema" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) func Archive(w http.ResponseWriter, r *http.Request) { @@ -32,14 +24,14 @@ func Archive(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodPut: handlePut(w, r, decoder, runtime) - case http.MethodGet, http.MethodHead: - handleHeadOrGet(w, r, decoder, runtime) + case http.MethodHead, http.MethodGet: + handleHeadAndGet(w, r, decoder, runtime) default: - utils.Error(w, fmt.Sprintf("not implemented, method: %v", r.Method), http.StatusNotImplemented, errors.New(fmt.Sprintf("not implemented, method: %v", r.Method))) + utils.Error(w, fmt.Sprintf("unsupported method: %v", r.Method), http.StatusNotImplemented, errors.New(fmt.Sprintf("unsupported method: %v", r.Method))) } } -func handleHeadOrGet(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { +func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { query := struct { Path string `schema:"path"` }{} @@ -66,170 +58,62 @@ func handleHeadOrGet(w http.ResponseWriter, r *http.Request, decoder *schema.Dec return } - mountPoint, err := ctr.Mount() + source, err := copy.CopyItemForContainer(ctr, query.Path, true, true) + defer source.CleanUp() if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to mount the container")) + utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrapf(err, "error stating container path %q", query.Path)) return } - defer func() { - if err := ctr.Unmount(false); err != nil { - logrus.Warnf("failed to unmount container %s: %q", containerName, err) - } - }() - - opts := copier.StatOptions{} - - mountPoint, path, err := fixUpMountPointAndPath(runtime, ctr, mountPoint, query.Path) + // NOTE: Docker always sets the header. + info, err := source.Stat() if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrapf(err, "error stating container path %q", query.Path)) return } - - stats, err := copier.Stat(mountPoint, "", opts, []string{filepath.Join(mountPoint, path)}) + statHeader, err := fileInfoToDockerStats(info) if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to get stats about file")) - return - } - - if len(stats) <= 0 || len(stats[0].Globbed) <= 0 { - errs := make([]string, 0, len(stats)) - - for _, stat := range stats { - if stat.Error != "" { - errs = append(errs, stat.Error) - } - } - - utils.Error(w, "Not found.", http.StatusNotFound, fmt.Errorf("file doesn't exist (errs: %q)", strings.Join(errs, ";"))) - - return - } - - statHeader, err := statsToHeader(stats[0].Results[stats[0].Globbed[0]]) - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } - w.Header().Add("X-Docker-Container-Path-Stat", statHeader) - if r.Method == http.MethodGet { - idMappingOpts, err := ctr.IDMappings() - if err != nil { - utils.Error(w, "Not found.", http.StatusInternalServerError, - errors.Wrapf(err, "error getting IDMappingOptions")) - return - } - - destOwner := idtools.IDPair{UID: os.Getuid(), GID: os.Getgid()} - - opts := copier.GetOptions{ - UIDMap: idMappingOpts.UIDMap, - GIDMap: idMappingOpts.GIDMap, - ChownDirs: &destOwner, - ChownFiles: &destOwner, - KeepDirectoryNames: true, - } - - w.WriteHeader(http.StatusOK) - - err = copier.Get(mountPoint, "", opts, []string{filepath.Join(mountPoint, path)}, w) - if err != nil { - logrus.Error(errors.Wrapf(err, "failed to copy from the %s container path %s", containerName, query.Path)) - return - } - } else { + // Our work is done when the user is interested in the header only. + if r.Method == http.MethodHead { w.WriteHeader(http.StatusOK) - } -} - -func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { - query := struct { - Path string `schema:"path"` - // TODO handle params below - NoOverwriteDirNonDir bool `schema:"noOverwriteDirNonDir"` - CopyUIDGID bool `schema:"copyUIDGID"` - }{} - - err := decoder.Decode(&query, r.URL.Query()) - if err != nil { - utils.Error(w, "Bad Request.", http.StatusBadRequest, errors.Wrap(err, "couldn't decode the query")) return } - ctrName := utils.GetName(r) - - ctr, err := runtime.LookupContainer(ctrName) - if err != nil { - utils.Error(w, "Not found", http.StatusNotFound, errors.Wrapf(err, "the %s container doesn't exists", ctrName)) - return - } - - mountPoint, err := ctr.Mount() - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, errors.Wrapf(err, "failed to mount the %s container", ctrName)) - return - } - - defer func() { - if err := ctr.Unmount(false); err != nil { - logrus.Warnf("failed to unmount container %s", ctrName) - } - }() - - user, err := getUser(mountPoint, ctr.User()) - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) - return - } - - idMappingOpts, err := ctr.IDMappings() - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, errors.Wrapf(err, "error getting IDMappingOptions")) - return - } - - destOwner := idtools.IDPair{UID: int(user.UID), GID: int(user.GID)} - - opts := copier.PutOptions{ - UIDMap: idMappingOpts.UIDMap, - GIDMap: idMappingOpts.GIDMap, - ChownDirs: &destOwner, - ChownFiles: &destOwner, - } - - mountPoint, path, err := fixUpMountPointAndPath(runtime, ctr, mountPoint, query.Path) + // Alright, the users wants data from the container. + destination, err := copy.CopyItemForWriter(w) if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } w.WriteHeader(http.StatusOK) - - err = copier.Put(mountPoint, filepath.Join(mountPoint, path), opts, r.Body) - if err != nil { - logrus.Error(errors.Wrapf(err, "failed to copy to the %s container path %s", ctrName, query.Path)) + if err := copy.Copy(&source, &destination, false); err != nil { + utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } } -func statsToHeader(stats *copier.StatForItem) (string, error) { - statsDTO := struct { +func fileInfoToDockerStats(info *copy.FileInfo) (string, error) { + dockerStats := struct { Name string `json:"name"` Size int64 `json:"size"` Mode os.FileMode `json:"mode"` ModTime time.Time `json:"mtime"` LinkTarget string `json:"linkTarget"` }{ - Name: filepath.Base(stats.Name), - Size: stats.Size, - Mode: stats.Mode, - ModTime: stats.ModTime, - LinkTarget: stats.ImmediateTarget, + Name: info.Name, + Size: info.Size, + Mode: info.Mode, + ModTime: info.ModTime, + LinkTarget: info.LinkTarget, } - jsonBytes, err := json.Marshal(&statsDTO) + jsonBytes, err := json.Marshal(&dockerStats) if err != nil { return "", errors.Wrap(err, "failed to serialize file stats") } @@ -250,130 +134,45 @@ func statsToHeader(stats *copier.StatForItem) (string, error) { return buff.String(), nil } -// the utility functions below are copied from abi/cp.go - -func getUser(mountPoint string, userspec string) (specs.User, error) { - uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) - u := specs.User{ - UID: uid, - GID: gid, - Username: userspec, - } - - if !strings.Contains(userspec, ":") { - groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID)) - if err2 != nil { - if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil { - err = err2 - } - } else { - u.AdditionalGids = groups - } - } - - return u, err -} - -func fixUpMountPointAndPath(runtime *libpod.Runtime, ctr *libpod.Container, mountPoint, ctrPath string) (string, string, error) { - if !filepath.IsAbs(ctrPath) { - endsWithSep := strings.HasSuffix(ctrPath, string(filepath.Separator)) - ctrPath = filepath.Join(ctr.WorkingDir(), ctrPath) - - if endsWithSep { - ctrPath = ctrPath + string(filepath.Separator) - } - } - if isVol, volDestName, volName := isVolumeDestName(ctrPath, ctr); isVol { //nolint(gocritic) - newMountPoint, path, err := pathWithVolumeMount(runtime, volDestName, volName, ctrPath) - if err != nil { - return "", "", errors.Wrapf(err, "error getting source path from volume %s", volDestName) - } - - mountPoint = newMountPoint - ctrPath = path - } else if isBindMount, mount := isBindMountDestName(ctrPath, ctr); isBindMount { //nolint(gocritic) - newMountPoint, path := pathWithBindMountSource(mount, ctrPath) - mountPoint = newMountPoint - ctrPath = path - } - - return mountPoint, ctrPath, nil -} - -func isVolumeDestName(path string, ctr *libpod.Container) (bool, string, string) { - separator := string(os.PathSeparator) - - if filepath.IsAbs(path) { - path = strings.TrimPrefix(path, separator) - } - - if path == "" { - return false, "", "" - } - - for _, vol := range ctr.Config().NamedVolumes { - volNamePath := strings.TrimPrefix(vol.Dest, separator) - if matchVolumePath(path, volNamePath) { - return true, vol.Dest, vol.Name - } - } - - return false, "", "" -} +func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { + query := struct { + Path string `schema:"path"` + // TODO handle params below + NoOverwriteDirNonDir bool `schema:"noOverwriteDirNonDir"` + CopyUIDGID bool `schema:"copyUIDGID"` + }{} -func pathWithVolumeMount(runtime *libpod.Runtime, volDestName, volName, path string) (string, string, error) { - destVolume, err := runtime.GetVolume(volName) + err := decoder.Decode(&query, r.URL.Query()) if err != nil { - return "", "", errors.Wrapf(err, "error getting volume destination %s", volName) - } - - if !filepath.IsAbs(path) { - path = filepath.Join(string(os.PathSeparator), path) + utils.Error(w, "Bad Request.", http.StatusBadRequest, errors.Wrap(err, "couldn't decode the query")) + return } - return destVolume.MountPoint(), strings.TrimPrefix(path, volDestName), err -} - -func isBindMountDestName(path string, ctr *libpod.Container) (bool, specs.Mount) { - separator := string(os.PathSeparator) - - if filepath.IsAbs(path) { - path = strings.TrimPrefix(path, string(os.PathSeparator)) - } + ctrName := utils.GetName(r) - if path == "" { - return false, specs.Mount{} + ctr, err := runtime.LookupContainer(ctrName) + if err != nil { + utils.Error(w, "Not found", http.StatusNotFound, errors.Wrapf(err, "the %s container doesn't exists", ctrName)) + return } - for _, m := range ctr.Config().Spec.Mounts { - if m.Type != "bind" { - continue - } - - mDest := strings.TrimPrefix(m.Destination, separator) - if matchVolumePath(path, mDest) { - return true, m - } + destination, err := copy.CopyItemForContainer(ctr, query.Path, true, false) + defer destination.CleanUp() + if err != nil { + utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + return } - return false, specs.Mount{} -} - -func matchVolumePath(path, target string) bool { - pathStr := filepath.Clean(path) - target = filepath.Clean(target) - - for len(pathStr) > len(target) && strings.Contains(pathStr, string(os.PathSeparator)) { - pathStr = pathStr[:strings.LastIndex(pathStr, string(os.PathSeparator))] + source, err := copy.CopyItemForReader(r.Body) + defer source.CleanUp() + if err != nil { + utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + return } - return pathStr == target -} - -func pathWithBindMountSource(m specs.Mount, path string) (string, string) { - if !filepath.IsAbs(path) { - path = filepath.Join(string(os.PathSeparator), path) + w.WriteHeader(http.StatusOK) + if err := copy.Copy(&source, &destination, false); err != nil { + utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + return } - - return m.Source, strings.TrimPrefix(path, m.Destination) } diff --git a/pkg/api/handlers/compat/containers_pause.go b/pkg/api/handlers/compat/containers_pause.go index 8712969c0..a7e0a66f1 100644 --- a/pkg/api/handlers/compat/containers_pause.go +++ b/pkg/api/handlers/compat/containers_pause.go @@ -24,5 +24,5 @@ func PauseContainer(w http.ResponseWriter, r *http.Request) { return } // Success - utils.WriteResponse(w, http.StatusNoContent, "") + utils.WriteResponse(w, http.StatusNoContent, nil) } diff --git a/pkg/api/handlers/compat/containers_restart.go b/pkg/api/handlers/compat/containers_restart.go index f4d8f06a1..e8928596a 100644 --- a/pkg/api/handlers/compat/containers_restart.go +++ b/pkg/api/handlers/compat/containers_restart.go @@ -41,5 +41,5 @@ func RestartContainer(w http.ResponseWriter, r *http.Request) { } // Success - utils.WriteResponse(w, http.StatusNoContent, "") + utils.WriteResponse(w, http.StatusNoContent, nil) } diff --git a/pkg/api/handlers/compat/containers_start.go b/pkg/api/handlers/compat/containers_start.go index 6236b1357..726da6f99 100644 --- a/pkg/api/handlers/compat/containers_start.go +++ b/pkg/api/handlers/compat/containers_start.go @@ -39,12 +39,12 @@ func StartContainer(w http.ResponseWriter, r *http.Request) { return } if state == define.ContainerStateRunning { - utils.WriteResponse(w, http.StatusNotModified, "") + utils.WriteResponse(w, http.StatusNotModified, nil) return } if err := con.Start(r.Context(), len(con.PodID()) > 0); err != nil { utils.InternalServerError(w, err) return } - utils.WriteResponse(w, http.StatusNoContent, "") + utils.WriteResponse(w, http.StatusNoContent, nil) } diff --git a/pkg/api/handlers/compat/containers_stop.go b/pkg/api/handlers/compat/containers_stop.go index 13fe25338..8bc58cf59 100644 --- a/pkg/api/handlers/compat/containers_stop.go +++ b/pkg/api/handlers/compat/containers_stop.go @@ -40,7 +40,7 @@ func StopContainer(w http.ResponseWriter, r *http.Request) { } // If the Container is stopped already, send a 304 if state == define.ContainerStateStopped || state == define.ContainerStateExited { - utils.WriteResponse(w, http.StatusNotModified, "") + utils.WriteResponse(w, http.StatusNotModified, nil) return } @@ -56,5 +56,5 @@ func StopContainer(w http.ResponseWriter, r *http.Request) { } // Success - utils.WriteResponse(w, http.StatusNoContent, "") + utils.WriteResponse(w, http.StatusNoContent, nil) } diff --git a/pkg/api/handlers/compat/containers_unpause.go b/pkg/api/handlers/compat/containers_unpause.go index f87b95b64..760e85814 100644 --- a/pkg/api/handlers/compat/containers_unpause.go +++ b/pkg/api/handlers/compat/containers_unpause.go @@ -24,5 +24,5 @@ func UnpauseContainer(w http.ResponseWriter, r *http.Request) { } // Success - utils.WriteResponse(w, http.StatusNoContent, "") + utils.WriteResponse(w, http.StatusNoContent, nil) } diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index 12593a68c..0f3da53e8 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -81,5 +81,4 @@ func PushImage(w http.ResponseWriter, r *http.Request) { } utils.WriteResponse(w, http.StatusOK, "") - } diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index 40cf16807..25e7640b6 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -145,13 +145,14 @@ type PodCreateConfig struct { Share string `json:"share"` } +// HistoryResponse provides details on image layers type HistoryResponse struct { - ID string `json:"Id"` - Created int64 `json:"Created"` - CreatedBy string `json:"CreatedBy"` - Tags []string `json:"Tags"` - Size int64 `json:"Size"` - Comment string `json:"Comment"` + ID string `json:"Id"` + Created int64 + CreatedBy string + Tags []string + Size int64 + Comment string } type ImageLayer struct{} @@ -213,6 +214,7 @@ func ImageToImageSummary(l *libpodImage.Image) (*entities.ImageSummary, error) { ID: l.ID(), ParentId: l.Parent, RepoTags: repoTags, + RepoDigests: digests, Created: l.Created().Unix(), Size: int64(*size), SharedSize: 0, @@ -223,7 +225,6 @@ func ImageToImageSummary(l *libpodImage.Image) (*entities.ImageSummary, error) { Dangling: l.Dangling(), Names: l.Names(), Digest: string(l.Digest()), - Digests: digests, ConfigDigest: string(l.ConfigDigest), History: l.NamesHistory(), } @@ -329,7 +330,6 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI dockerImageInspect.Parent = d.Parent.String() } return &ImageInspect{dockerImageInspect}, nil - } // portsToPortSet converts libpods exposed ports to dockers structs |