mirror of
				https://github.com/containers/podman.git
				synced 2025-10-31 01:50:50 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			371 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			371 lines
		
	
	
		
			10 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/domain/infra/abi"
 | |
| 	"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) {
 | |
| 	decoder := r.Context().Value("decoder").(*schema.Decoder)
 | |
| 	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | |
| 	// Now use the ABI implementation to prevent us from having duplicate
 | |
| 	// code.
 | |
| 	containerEngine := abi.ContainerEngine{Libpod: runtime}
 | |
| 
 | |
| 	name := utils.GetName(r)
 | |
| 	query := struct {
 | |
| 		External bool `schema:"external"`
 | |
| 	}{
 | |
| 		// 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
 | |
| 	}
 | |
| 
 | |
| 	options := entities.ContainerExistsOptions{
 | |
| 		External: query.External,
 | |
| 	}
 | |
| 
 | |
| 	report, err := containerEngine.ContainerExists(r.Context(), name, options)
 | |
| 	if err != nil {
 | |
| 		if errors.Cause(err) == define.ErrNoSuchCtr {
 | |
| 			utils.ContainerNotFound(w, name, err)
 | |
| 			return
 | |
| 		}
 | |
| 		utils.InternalServerError(w, err)
 | |
| 		return
 | |
| 
 | |
| 	}
 | |
| 	if report.Value {
 | |
| 		utils.WriteResponse(w, http.StatusNoContent, "")
 | |
| 	} else {
 | |
| 		utils.ContainerNotFound(w, name, define.ErrNoSuchCtr)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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, "")
 | |
| }
 | |
| 
 | |
| func ShouldRestart(w http.ResponseWriter, r *http.Request) {
 | |
| 	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | |
| 	// Now use the ABI implementation to prevent us from having duplicate
 | |
| 	// code.
 | |
| 	containerEngine := abi.ContainerEngine{Libpod: runtime}
 | |
| 
 | |
| 	name := utils.GetName(r)
 | |
| 	report, err := containerEngine.ShouldRestart(r.Context(), name)
 | |
| 	if err != nil {
 | |
| 		if errors.Cause(err) == define.ErrNoSuchCtr {
 | |
| 			utils.ContainerNotFound(w, name, err)
 | |
| 			return
 | |
| 		}
 | |
| 		utils.InternalServerError(w, err)
 | |
| 		return
 | |
| 
 | |
| 	}
 | |
| 	if report.Value {
 | |
| 		utils.WriteResponse(w, http.StatusNoContent, "")
 | |
| 	} else {
 | |
| 		utils.ContainerNotFound(w, name, define.ErrNoSuchCtr)
 | |
| 	}
 | |
| }
 | 
