mirror of
				https://github.com/containers/podman.git
				synced 2025-11-04 08:56:05 +08:00 
			
		
		
		
	The ListContainers API previously had a Pod parameter, which determined if pod name was returned (but, notably, not Pod ID, which was returned unconditionally). This was fairly confusing, so we decided to deprecate/remove the parameter and return it unconditionally. To do this without serious performance implications, we need to avoid expensive JSON decodes of pod configuration in the DB. The way our Bolt tables are structured, retrieving name given ID is actually quite cheap, but we did not expose this via the Libpod API. Add a new GetName API to do this. Fixes #7214 Signed-off-by: Matthew Heon <matthew.heon@pm.me>
		
			
				
	
	
		
			321 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			321 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package libpod
 | 
						|
 | 
						|
import (
 | 
						|
	"io/ioutil"
 | 
						|
	"net/http"
 | 
						|
	"os"
 | 
						|
	"strconv"
 | 
						|
 | 
						|
	"github.com/containers/podman/v2/libpod"
 | 
						|
	"github.com/containers/podman/v2/libpod/define"
 | 
						|
	"github.com/containers/podman/v2/pkg/api/handlers/compat"
 | 
						|
	"github.com/containers/podman/v2/pkg/api/handlers/utils"
 | 
						|
	"github.com/containers/podman/v2/pkg/domain/entities"
 | 
						|
	"github.com/containers/podman/v2/pkg/ps"
 | 
						|
	"github.com/gorilla/schema"
 | 
						|
	"github.com/pkg/errors"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
)
 | 
						|
 | 
						|
func ContainerExists(w http.ResponseWriter, r *http.Request) {
 | 
						|
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | 
						|
	name := utils.GetName(r)
 | 
						|
	_, err := runtime.LookupContainer(name)
 | 
						|
	if err != nil {
 | 
						|
		if errors.Cause(err) == define.ErrNoSuchCtr {
 | 
						|
			utils.ContainerNotFound(w, name, err)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		utils.InternalServerError(w, err)
 | 
						|
		return
 | 
						|
 | 
						|
	}
 | 
						|
	utils.WriteResponse(w, http.StatusNoContent, "")
 | 
						|
}
 | 
						|
 | 
						|
func ListContainers(w http.ResponseWriter, r *http.Request) {
 | 
						|
	decoder := r.Context().Value("decoder").(*schema.Decoder)
 | 
						|
	query := struct {
 | 
						|
		All       bool                `schema:"all"`
 | 
						|
		Filters   map[string][]string `schema:"filters"`
 | 
						|
		Last      int                 `schema:"last"` // alias for limit
 | 
						|
		Limit     int                 `schema:"limit"`
 | 
						|
		Namespace bool                `schema:"namespace"`
 | 
						|
		Size      bool                `schema:"size"`
 | 
						|
		Sync      bool                `schema:"sync"`
 | 
						|
	}{
 | 
						|
		// override any golang type defaults
 | 
						|
	}
 | 
						|
 | 
						|
	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
 | 
						|
		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
 | 
						|
			errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	limit := query.Limit
 | 
						|
	// Support `last` as an alias for `limit`.  While Podman uses --last in
 | 
						|
	// the CLI, the API is using `limit`.  As we first used `last` in the
 | 
						|
	// API as well, we decided to go with aliasing to prevent any
 | 
						|
	// regression. See github.com/containers/podman/issues/6413.
 | 
						|
	if _, found := r.URL.Query()["last"]; found {
 | 
						|
		logrus.Info("List containers: received `last` parameter - overwriting `limit`")
 | 
						|
		limit = query.Last
 | 
						|
	}
 | 
						|
 | 
						|
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | 
						|
	opts := entities.ContainerListOptions{
 | 
						|
		All:       query.All,
 | 
						|
		Filters:   query.Filters,
 | 
						|
		Last:      limit,
 | 
						|
		Size:      query.Size,
 | 
						|
		Sort:      "",
 | 
						|
		Namespace: query.Namespace,
 | 
						|
		Pod:       true,
 | 
						|
		Sync:      query.Sync,
 | 
						|
	}
 | 
						|
	pss, err := ps.GetContainerLists(runtime, opts)
 | 
						|
	if err != nil {
 | 
						|
		utils.InternalServerError(w, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if len(pss) == 0 {
 | 
						|
		utils.WriteResponse(w, http.StatusOK, "[]")
 | 
						|
		return
 | 
						|
	}
 | 
						|
	utils.WriteResponse(w, http.StatusOK, pss)
 | 
						|
}
 | 
						|
 | 
						|
func GetContainer(w http.ResponseWriter, r *http.Request) {
 | 
						|
	decoder := r.Context().Value("decoder").(*schema.Decoder)
 | 
						|
	query := struct {
 | 
						|
		Size bool `schema:"size"`
 | 
						|
	}{
 | 
						|
		// override any golang type defaults
 | 
						|
	}
 | 
						|
 | 
						|
	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
 | 
						|
		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
 | 
						|
			errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
 | 
						|
		return
 | 
						|
	}
 | 
						|
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | 
						|
	name := utils.GetName(r)
 | 
						|
	container, err := runtime.LookupContainer(name)
 | 
						|
	if err != nil {
 | 
						|
		utils.ContainerNotFound(w, name, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	data, err := container.Inspect(query.Size)
 | 
						|
	if err != nil {
 | 
						|
		utils.InternalServerError(w, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	utils.WriteResponse(w, http.StatusOK, data)
 | 
						|
}
 | 
						|
 | 
						|
func WaitContainer(w http.ResponseWriter, r *http.Request) {
 | 
						|
	exitCode, err := utils.WaitContainer(w, r)
 | 
						|
	if err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	utils.WriteResponse(w, http.StatusOK, strconv.Itoa(int(exitCode)))
 | 
						|
}
 | 
						|
 | 
						|
func UnmountContainer(w http.ResponseWriter, r *http.Request) {
 | 
						|
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | 
						|
	name := utils.GetName(r)
 | 
						|
	conn, err := runtime.LookupContainer(name)
 | 
						|
	if err != nil {
 | 
						|
		utils.ContainerNotFound(w, name, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	// TODO In future it might be an improvement that libpod unmount return a
 | 
						|
	// "container not mounted" error so we can surface that to the endpoint user
 | 
						|
	if err := conn.Unmount(false); err != nil {
 | 
						|
		utils.InternalServerError(w, err)
 | 
						|
	}
 | 
						|
	utils.WriteResponse(w, http.StatusNoContent, "")
 | 
						|
 | 
						|
}
 | 
						|
func MountContainer(w http.ResponseWriter, r *http.Request) {
 | 
						|
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | 
						|
	name := utils.GetName(r)
 | 
						|
	conn, err := runtime.LookupContainer(name)
 | 
						|
	if err != nil {
 | 
						|
		utils.ContainerNotFound(w, name, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	m, err := conn.Mount()
 | 
						|
	if err != nil {
 | 
						|
		utils.InternalServerError(w, err)
 | 
						|
	}
 | 
						|
	utils.WriteResponse(w, http.StatusOK, m)
 | 
						|
}
 | 
						|
 | 
						|
func ShowMountedContainers(w http.ResponseWriter, r *http.Request) {
 | 
						|
	response := make(map[string]string)
 | 
						|
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | 
						|
	conns, err := runtime.GetAllContainers()
 | 
						|
	if err != nil {
 | 
						|
		utils.InternalServerError(w, err)
 | 
						|
	}
 | 
						|
	for _, conn := range conns {
 | 
						|
		mounted, mountPoint, err := conn.Mounted()
 | 
						|
		if err != nil {
 | 
						|
			utils.InternalServerError(w, err)
 | 
						|
		}
 | 
						|
		if !mounted {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		response[conn.ID()] = mountPoint
 | 
						|
	}
 | 
						|
	utils.WriteResponse(w, http.StatusOK, response)
 | 
						|
}
 | 
						|
 | 
						|
func Checkpoint(w http.ResponseWriter, r *http.Request) {
 | 
						|
	var targetFile string
 | 
						|
	decoder := r.Context().Value("decoder").(*schema.Decoder)
 | 
						|
	query := struct {
 | 
						|
		Keep           bool `schema:"keep"`
 | 
						|
		LeaveRunning   bool `schema:"leaveRunning"`
 | 
						|
		TCPEstablished bool `schema:"tcpEstablished"`
 | 
						|
		Export         bool `schema:"export"`
 | 
						|
		IgnoreRootFS   bool `schema:"ignoreRootFS"`
 | 
						|
	}{
 | 
						|
		// override any golang type defaults
 | 
						|
	}
 | 
						|
 | 
						|
	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
 | 
						|
		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
 | 
						|
			errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
 | 
						|
		return
 | 
						|
	}
 | 
						|
	name := utils.GetName(r)
 | 
						|
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | 
						|
	ctr, err := runtime.LookupContainer(name)
 | 
						|
	if err != nil {
 | 
						|
		utils.ContainerNotFound(w, name, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if query.Export {
 | 
						|
		tmpFile, err := ioutil.TempFile("", "checkpoint")
 | 
						|
		if err != nil {
 | 
						|
			utils.InternalServerError(w, err)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		defer os.Remove(tmpFile.Name())
 | 
						|
		if err := tmpFile.Close(); err != nil {
 | 
						|
			utils.InternalServerError(w, err)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		targetFile = tmpFile.Name()
 | 
						|
	}
 | 
						|
	options := libpod.ContainerCheckpointOptions{
 | 
						|
		Keep:           query.Keep,
 | 
						|
		KeepRunning:    query.LeaveRunning,
 | 
						|
		TCPEstablished: query.TCPEstablished,
 | 
						|
		IgnoreRootfs:   query.IgnoreRootFS,
 | 
						|
	}
 | 
						|
	if query.Export {
 | 
						|
		options.TargetFile = targetFile
 | 
						|
	}
 | 
						|
	err = ctr.Checkpoint(r.Context(), options)
 | 
						|
	if err != nil {
 | 
						|
		utils.InternalServerError(w, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if query.Export {
 | 
						|
		f, err := os.Open(targetFile)
 | 
						|
		if err != nil {
 | 
						|
			utils.InternalServerError(w, err)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		defer f.Close()
 | 
						|
		utils.WriteResponse(w, http.StatusOK, f)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	utils.WriteResponse(w, http.StatusOK, entities.CheckpointReport{Id: ctr.ID()})
 | 
						|
}
 | 
						|
 | 
						|
func Restore(w http.ResponseWriter, r *http.Request) {
 | 
						|
	var (
 | 
						|
		targetFile string
 | 
						|
	)
 | 
						|
	decoder := r.Context().Value("decoder").(*schema.Decoder)
 | 
						|
	query := struct {
 | 
						|
		Keep            bool   `schema:"keep"`
 | 
						|
		TCPEstablished  bool   `schema:"tcpEstablished"`
 | 
						|
		Import          bool   `schema:"import"`
 | 
						|
		Name            string `schema:"name"`
 | 
						|
		IgnoreRootFS    bool   `schema:"ignoreRootFS"`
 | 
						|
		IgnoreStaticIP  bool   `schema:"ignoreStaticIP"`
 | 
						|
		IgnoreStaticMAC bool   `schema:"ignoreStaticMAC"`
 | 
						|
	}{
 | 
						|
		// override any golang type defaults
 | 
						|
	}
 | 
						|
	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
 | 
						|
		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
 | 
						|
			errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
 | 
						|
		return
 | 
						|
	}
 | 
						|
	name := utils.GetName(r)
 | 
						|
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | 
						|
	ctr, err := runtime.LookupContainer(name)
 | 
						|
	if err != nil {
 | 
						|
		utils.ContainerNotFound(w, name, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if query.Import {
 | 
						|
		t, err := ioutil.TempFile("", "restore")
 | 
						|
		if err != nil {
 | 
						|
			utils.InternalServerError(w, err)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		defer t.Close()
 | 
						|
		if err := compat.SaveFromBody(t, r); err != nil {
 | 
						|
			utils.InternalServerError(w, err)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		targetFile = t.Name()
 | 
						|
	}
 | 
						|
 | 
						|
	options := libpod.ContainerCheckpointOptions{
 | 
						|
		Keep:            query.Keep,
 | 
						|
		TCPEstablished:  query.TCPEstablished,
 | 
						|
		IgnoreRootfs:    query.IgnoreRootFS,
 | 
						|
		IgnoreStaticIP:  query.IgnoreStaticIP,
 | 
						|
		IgnoreStaticMAC: query.IgnoreStaticMAC,
 | 
						|
	}
 | 
						|
	if query.Import {
 | 
						|
		options.TargetFile = targetFile
 | 
						|
		options.Name = query.Name
 | 
						|
	}
 | 
						|
	err = ctr.Restore(r.Context(), options)
 | 
						|
	if err != nil {
 | 
						|
		utils.InternalServerError(w, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	utils.WriteResponse(w, http.StatusOK, entities.RestoreReport{Id: ctr.ID()})
 | 
						|
}
 | 
						|
 | 
						|
func InitContainer(w http.ResponseWriter, r *http.Request) {
 | 
						|
	name := utils.GetName(r)
 | 
						|
	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | 
						|
	ctr, err := runtime.LookupContainer(name)
 | 
						|
	if err != nil {
 | 
						|
		utils.ContainerNotFound(w, name, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	err = ctr.Init(r.Context(), ctr.PodID() != "")
 | 
						|
	if errors.Cause(err) == define.ErrCtrStateInvalid {
 | 
						|
		utils.Error(w, "container already initialized", http.StatusNotModified, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		utils.InternalServerError(w, err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	utils.WriteResponse(w, http.StatusNoContent, "")
 | 
						|
}
 |