github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/api/handlers/libpod/containers.go (about)

     1  package libpod
     2  
     3  import (
     4  	"io/ioutil"
     5  	"net/http"
     6  	"os"
     7  	"strconv"
     8  
     9  	"github.com/containers/podman/v2/libpod"
    10  	"github.com/containers/podman/v2/libpod/define"
    11  	"github.com/containers/podman/v2/pkg/api/handlers/compat"
    12  	"github.com/containers/podman/v2/pkg/api/handlers/utils"
    13  	"github.com/containers/podman/v2/pkg/domain/entities"
    14  	"github.com/containers/podman/v2/pkg/domain/infra/abi"
    15  	"github.com/containers/podman/v2/pkg/ps"
    16  	"github.com/gorilla/schema"
    17  	"github.com/pkg/errors"
    18  	"github.com/sirupsen/logrus"
    19  )
    20  
    21  func ContainerExists(w http.ResponseWriter, r *http.Request) {
    22  	decoder := r.Context().Value("decoder").(*schema.Decoder)
    23  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
    24  	// Now use the ABI implementation to prevent us from having duplicate
    25  	// code.
    26  	containerEngine := abi.ContainerEngine{Libpod: runtime}
    27  
    28  	name := utils.GetName(r)
    29  	query := struct {
    30  		External bool `schema:"external"`
    31  	}{
    32  		// override any golang type defaults
    33  	}
    34  
    35  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
    36  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
    37  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
    38  		return
    39  	}
    40  
    41  	options := entities.ContainerExistsOptions{
    42  		External: query.External,
    43  	}
    44  
    45  	report, err := containerEngine.ContainerExists(r.Context(), name, options)
    46  	if err != nil {
    47  		if errors.Cause(err) == define.ErrNoSuchCtr {
    48  			utils.ContainerNotFound(w, name, err)
    49  			return
    50  		}
    51  		utils.InternalServerError(w, err)
    52  		return
    53  
    54  	}
    55  	if report.Value {
    56  		utils.WriteResponse(w, http.StatusNoContent, "")
    57  	} else {
    58  		utils.ContainerNotFound(w, name, define.ErrNoSuchCtr)
    59  	}
    60  }
    61  
    62  func ListContainers(w http.ResponseWriter, r *http.Request) {
    63  	decoder := r.Context().Value("decoder").(*schema.Decoder)
    64  	query := struct {
    65  		All       bool                `schema:"all"`
    66  		Filters   map[string][]string `schema:"filters"`
    67  		Last      int                 `schema:"last"` // alias for limit
    68  		Limit     int                 `schema:"limit"`
    69  		Namespace bool                `schema:"namespace"`
    70  		Size      bool                `schema:"size"`
    71  		Sync      bool                `schema:"sync"`
    72  	}{
    73  		// override any golang type defaults
    74  	}
    75  
    76  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
    77  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
    78  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
    79  		return
    80  	}
    81  
    82  	limit := query.Limit
    83  	// Support `last` as an alias for `limit`.  While Podman uses --last in
    84  	// the CLI, the API is using `limit`.  As we first used `last` in the
    85  	// API as well, we decided to go with aliasing to prevent any
    86  	// regression. See github.com/containers/podman/issues/6413.
    87  	if _, found := r.URL.Query()["last"]; found {
    88  		logrus.Info("List containers: received `last` parameter - overwriting `limit`")
    89  		limit = query.Last
    90  	}
    91  
    92  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
    93  	opts := entities.ContainerListOptions{
    94  		All:       query.All,
    95  		Filters:   query.Filters,
    96  		Last:      limit,
    97  		Size:      query.Size,
    98  		Sort:      "",
    99  		Namespace: query.Namespace,
   100  		Pod:       true,
   101  		Sync:      query.Sync,
   102  	}
   103  	pss, err := ps.GetContainerLists(runtime, opts)
   104  	if err != nil {
   105  		utils.InternalServerError(w, err)
   106  		return
   107  	}
   108  	if len(pss) == 0 {
   109  		utils.WriteResponse(w, http.StatusOK, "[]")
   110  		return
   111  	}
   112  	utils.WriteResponse(w, http.StatusOK, pss)
   113  }
   114  
   115  func GetContainer(w http.ResponseWriter, r *http.Request) {
   116  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   117  	query := struct {
   118  		Size bool `schema:"size"`
   119  	}{
   120  		// override any golang type defaults
   121  	}
   122  
   123  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   124  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   125  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   126  		return
   127  	}
   128  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   129  	name := utils.GetName(r)
   130  	container, err := runtime.LookupContainer(name)
   131  	if err != nil {
   132  		utils.ContainerNotFound(w, name, err)
   133  		return
   134  	}
   135  	data, err := container.Inspect(query.Size)
   136  	if err != nil {
   137  		utils.InternalServerError(w, err)
   138  		return
   139  	}
   140  	utils.WriteResponse(w, http.StatusOK, data)
   141  }
   142  
   143  func WaitContainer(w http.ResponseWriter, r *http.Request) {
   144  	exitCode, err := utils.WaitContainer(w, r)
   145  	if err != nil {
   146  		return
   147  	}
   148  	utils.WriteResponse(w, http.StatusOK, strconv.Itoa(int(exitCode)))
   149  }
   150  
   151  func UnmountContainer(w http.ResponseWriter, r *http.Request) {
   152  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   153  	name := utils.GetName(r)
   154  	conn, err := runtime.LookupContainer(name)
   155  	if err != nil {
   156  		utils.ContainerNotFound(w, name, err)
   157  		return
   158  	}
   159  	// TODO In future it might be an improvement that libpod unmount return a
   160  	// "container not mounted" error so we can surface that to the endpoint user
   161  	if err := conn.Unmount(false); err != nil {
   162  		utils.InternalServerError(w, err)
   163  	}
   164  	utils.WriteResponse(w, http.StatusNoContent, "")
   165  
   166  }
   167  func MountContainer(w http.ResponseWriter, r *http.Request) {
   168  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   169  	name := utils.GetName(r)
   170  	conn, err := runtime.LookupContainer(name)
   171  	if err != nil {
   172  		utils.ContainerNotFound(w, name, err)
   173  		return
   174  	}
   175  	m, err := conn.Mount()
   176  	if err != nil {
   177  		utils.InternalServerError(w, err)
   178  	}
   179  	utils.WriteResponse(w, http.StatusOK, m)
   180  }
   181  
   182  func ShowMountedContainers(w http.ResponseWriter, r *http.Request) {
   183  	response := make(map[string]string)
   184  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   185  	conns, err := runtime.GetAllContainers()
   186  	if err != nil {
   187  		utils.InternalServerError(w, err)
   188  	}
   189  	for _, conn := range conns {
   190  		mounted, mountPoint, err := conn.Mounted()
   191  		if err != nil {
   192  			utils.InternalServerError(w, err)
   193  		}
   194  		if !mounted {
   195  			continue
   196  		}
   197  		response[conn.ID()] = mountPoint
   198  	}
   199  	utils.WriteResponse(w, http.StatusOK, response)
   200  }
   201  
   202  func Checkpoint(w http.ResponseWriter, r *http.Request) {
   203  	var targetFile string
   204  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   205  	query := struct {
   206  		Keep           bool `schema:"keep"`
   207  		LeaveRunning   bool `schema:"leaveRunning"`
   208  		TCPEstablished bool `schema:"tcpEstablished"`
   209  		Export         bool `schema:"export"`
   210  		IgnoreRootFS   bool `schema:"ignoreRootFS"`
   211  	}{
   212  		// override any golang type defaults
   213  	}
   214  
   215  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   216  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   217  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   218  		return
   219  	}
   220  	name := utils.GetName(r)
   221  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   222  	ctr, err := runtime.LookupContainer(name)
   223  	if err != nil {
   224  		utils.ContainerNotFound(w, name, err)
   225  		return
   226  	}
   227  	if query.Export {
   228  		tmpFile, err := ioutil.TempFile("", "checkpoint")
   229  		if err != nil {
   230  			utils.InternalServerError(w, err)
   231  			return
   232  		}
   233  		defer os.Remove(tmpFile.Name())
   234  		if err := tmpFile.Close(); err != nil {
   235  			utils.InternalServerError(w, err)
   236  			return
   237  		}
   238  		targetFile = tmpFile.Name()
   239  	}
   240  	options := libpod.ContainerCheckpointOptions{
   241  		Keep:           query.Keep,
   242  		KeepRunning:    query.LeaveRunning,
   243  		TCPEstablished: query.TCPEstablished,
   244  		IgnoreRootfs:   query.IgnoreRootFS,
   245  	}
   246  	if query.Export {
   247  		options.TargetFile = targetFile
   248  	}
   249  	err = ctr.Checkpoint(r.Context(), options)
   250  	if err != nil {
   251  		utils.InternalServerError(w, err)
   252  		return
   253  	}
   254  	if query.Export {
   255  		f, err := os.Open(targetFile)
   256  		if err != nil {
   257  			utils.InternalServerError(w, err)
   258  			return
   259  		}
   260  		defer f.Close()
   261  		utils.WriteResponse(w, http.StatusOK, f)
   262  		return
   263  	}
   264  	utils.WriteResponse(w, http.StatusOK, entities.CheckpointReport{Id: ctr.ID()})
   265  }
   266  
   267  func Restore(w http.ResponseWriter, r *http.Request) {
   268  	var (
   269  		targetFile string
   270  	)
   271  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   272  	query := struct {
   273  		Keep            bool   `schema:"keep"`
   274  		TCPEstablished  bool   `schema:"tcpEstablished"`
   275  		Import          bool   `schema:"import"`
   276  		Name            string `schema:"name"`
   277  		IgnoreRootFS    bool   `schema:"ignoreRootFS"`
   278  		IgnoreStaticIP  bool   `schema:"ignoreStaticIP"`
   279  		IgnoreStaticMAC bool   `schema:"ignoreStaticMAC"`
   280  	}{
   281  		// override any golang type defaults
   282  	}
   283  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   284  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   285  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   286  		return
   287  	}
   288  	name := utils.GetName(r)
   289  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   290  	ctr, err := runtime.LookupContainer(name)
   291  	if err != nil {
   292  		utils.ContainerNotFound(w, name, err)
   293  		return
   294  	}
   295  	if query.Import {
   296  		t, err := ioutil.TempFile("", "restore")
   297  		if err != nil {
   298  			utils.InternalServerError(w, err)
   299  			return
   300  		}
   301  		defer t.Close()
   302  		if err := compat.SaveFromBody(t, r); err != nil {
   303  			utils.InternalServerError(w, err)
   304  			return
   305  		}
   306  		targetFile = t.Name()
   307  	}
   308  
   309  	options := libpod.ContainerCheckpointOptions{
   310  		Keep:            query.Keep,
   311  		TCPEstablished:  query.TCPEstablished,
   312  		IgnoreRootfs:    query.IgnoreRootFS,
   313  		IgnoreStaticIP:  query.IgnoreStaticIP,
   314  		IgnoreStaticMAC: query.IgnoreStaticMAC,
   315  	}
   316  	if query.Import {
   317  		options.TargetFile = targetFile
   318  		options.Name = query.Name
   319  	}
   320  	err = ctr.Restore(r.Context(), options)
   321  	if err != nil {
   322  		utils.InternalServerError(w, err)
   323  		return
   324  	}
   325  	utils.WriteResponse(w, http.StatusOK, entities.RestoreReport{Id: ctr.ID()})
   326  }
   327  
   328  func InitContainer(w http.ResponseWriter, r *http.Request) {
   329  	name := utils.GetName(r)
   330  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   331  	ctr, err := runtime.LookupContainer(name)
   332  	if err != nil {
   333  		utils.ContainerNotFound(w, name, err)
   334  		return
   335  	}
   336  	err = ctr.Init(r.Context(), ctr.PodID() != "")
   337  	if errors.Cause(err) == define.ErrCtrStateInvalid {
   338  		utils.Error(w, "container already initialized", http.StatusNotModified, err)
   339  		return
   340  	}
   341  	if err != nil {
   342  		utils.InternalServerError(w, err)
   343  		return
   344  	}
   345  	utils.WriteResponse(w, http.StatusNoContent, "")
   346  }
   347  
   348  func ShouldRestart(w http.ResponseWriter, r *http.Request) {
   349  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   350  	// Now use the ABI implementation to prevent us from having duplicate
   351  	// code.
   352  	containerEngine := abi.ContainerEngine{Libpod: runtime}
   353  
   354  	name := utils.GetName(r)
   355  	report, err := containerEngine.ShouldRestart(r.Context(), name)
   356  	if err != nil {
   357  		if errors.Cause(err) == define.ErrNoSuchCtr {
   358  			utils.ContainerNotFound(w, name, err)
   359  			return
   360  		}
   361  		utils.InternalServerError(w, err)
   362  		return
   363  
   364  	}
   365  	if report.Value {
   366  		utils.WriteResponse(w, http.StatusNoContent, "")
   367  	} else {
   368  		utils.ContainerNotFound(w, name, define.ErrNoSuchCtr)
   369  	}
   370  }