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

     1  package libpod
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     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"
    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/specgen"
    16  	"github.com/containers/podman/v2/pkg/specgen/generate"
    17  	"github.com/containers/podman/v2/pkg/util"
    18  	"github.com/gorilla/schema"
    19  	"github.com/pkg/errors"
    20  	"github.com/sirupsen/logrus"
    21  )
    22  
    23  func PodCreate(w http.ResponseWriter, r *http.Request) {
    24  	var (
    25  		runtime = r.Context().Value("runtime").(*libpod.Runtime)
    26  		err     error
    27  	)
    28  	var psg specgen.PodSpecGenerator
    29  	if err := json.NewDecoder(r.Body).Decode(&psg); err != nil {
    30  		utils.Error(w, "failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen"))
    31  		return
    32  	}
    33  	pod, err := generate.MakePod(&psg, runtime)
    34  	if err != nil {
    35  		httpCode := http.StatusInternalServerError
    36  		if errors.Cause(err) == define.ErrPodExists {
    37  			httpCode = http.StatusConflict
    38  		}
    39  		utils.Error(w, "Something went wrong.", httpCode, err)
    40  		return
    41  	}
    42  	utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: pod.ID()})
    43  }
    44  
    45  func Pods(w http.ResponseWriter, r *http.Request) {
    46  	decoder := r.Context().Value("decoder").(*schema.Decoder)
    47  	query := struct {
    48  		Filters map[string][]string `schema:"filters"`
    49  	}{
    50  		// override any golang type defaults
    51  	}
    52  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
    53  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
    54  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
    55  		return
    56  	}
    57  
    58  	pods, err := utils.GetPods(w, r)
    59  	if err != nil {
    60  		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
    61  		return
    62  	}
    63  	utils.WriteResponse(w, http.StatusOK, pods)
    64  }
    65  
    66  func PodInspect(w http.ResponseWriter, r *http.Request) {
    67  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
    68  	name := utils.GetName(r)
    69  	pod, err := runtime.LookupPod(name)
    70  	if err != nil {
    71  		utils.PodNotFound(w, name, err)
    72  		return
    73  	}
    74  	podData, err := pod.Inspect()
    75  	if err != nil {
    76  		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
    77  		return
    78  	}
    79  
    80  	report := entities.PodInspectReport{
    81  		InspectPodData: podData,
    82  	}
    83  	utils.WriteResponse(w, http.StatusOK, report)
    84  }
    85  
    86  func PodStop(w http.ResponseWriter, r *http.Request) {
    87  	var (
    88  		stopError error
    89  		runtime   = r.Context().Value("runtime").(*libpod.Runtime)
    90  		decoder   = r.Context().Value("decoder").(*schema.Decoder)
    91  		responses map[string]error
    92  	)
    93  	query := struct {
    94  		Timeout int `schema:"t"`
    95  	}{
    96  		// override any golang type defaults
    97  	}
    98  
    99  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   100  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   101  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   102  		return
   103  	}
   104  	name := utils.GetName(r)
   105  	pod, err := runtime.LookupPod(name)
   106  	if err != nil {
   107  		utils.PodNotFound(w, name, err)
   108  		return
   109  	}
   110  
   111  	status, err := pod.GetPodStatus()
   112  	if err != nil {
   113  		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
   114  		return
   115  	}
   116  	if status != define.PodStateRunning {
   117  		utils.WriteResponse(w, http.StatusNotModified, "")
   118  		return
   119  	}
   120  
   121  	if query.Timeout > 0 {
   122  		responses, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout)
   123  	} else {
   124  		responses, stopError = pod.Stop(r.Context(), false)
   125  	}
   126  	if stopError != nil && errors.Cause(stopError) != define.ErrPodPartialFail {
   127  		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
   128  		return
   129  	}
   130  	// Try to clean up the pod - but only warn on failure, it's nonfatal.
   131  	if cleanupCtrs, cleanupErr := pod.Cleanup(r.Context()); cleanupErr != nil {
   132  		logrus.Errorf("Error cleaning up pod %s: %v", pod.ID(), cleanupErr)
   133  		for id, err := range cleanupCtrs {
   134  			logrus.Errorf("Error cleaning up pod %s container %s: %v", pod.ID(), id, err)
   135  		}
   136  	}
   137  	var errs []error //nolint
   138  	for id, err := range responses {
   139  		errs = append(errs, errors.Wrapf(err, "error stopping container %s", id))
   140  	}
   141  	report := entities.PodStopReport{
   142  		Errs: errs,
   143  		Id:   pod.ID(),
   144  	}
   145  	utils.WriteResponse(w, http.StatusOK, report)
   146  }
   147  
   148  func PodStart(w http.ResponseWriter, r *http.Request) {
   149  	var errs []error //nolint
   150  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   151  	name := utils.GetName(r)
   152  	pod, err := runtime.LookupPod(name)
   153  	if err != nil {
   154  		utils.PodNotFound(w, name, err)
   155  		return
   156  	}
   157  	status, err := pod.GetPodStatus()
   158  	if err != nil {
   159  		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
   160  		return
   161  	}
   162  	if status == define.PodStateRunning {
   163  		utils.WriteResponse(w, http.StatusNotModified, "")
   164  		return
   165  	}
   166  	responses, err := pod.Start(r.Context())
   167  	if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
   168  		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
   169  		return
   170  	}
   171  	for id, err := range responses {
   172  		errs = append(errs, errors.Wrapf(err, "error starting container %s", id))
   173  	}
   174  	report := entities.PodStartReport{
   175  		Errs: errs,
   176  		Id:   pod.ID(),
   177  	}
   178  	utils.WriteResponse(w, http.StatusOK, report)
   179  }
   180  
   181  func PodDelete(w http.ResponseWriter, r *http.Request) {
   182  	var (
   183  		runtime = r.Context().Value("runtime").(*libpod.Runtime)
   184  		decoder = r.Context().Value("decoder").(*schema.Decoder)
   185  	)
   186  	query := struct {
   187  		Force bool `schema:"force"`
   188  	}{
   189  		// override any golang type defaults
   190  	}
   191  
   192  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   193  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   194  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   195  		return
   196  	}
   197  	name := utils.GetName(r)
   198  	pod, err := runtime.LookupPod(name)
   199  	if err != nil {
   200  		utils.PodNotFound(w, name, err)
   201  		return
   202  	}
   203  	if err := runtime.RemovePod(r.Context(), pod, true, query.Force); err != nil {
   204  		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
   205  		return
   206  	}
   207  	report := entities.PodRmReport{
   208  		Id: pod.ID(),
   209  	}
   210  	utils.WriteResponse(w, http.StatusOK, report)
   211  }
   212  
   213  func PodRestart(w http.ResponseWriter, r *http.Request) {
   214  	var errs []error //nolint
   215  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   216  	name := utils.GetName(r)
   217  	pod, err := runtime.LookupPod(name)
   218  	if err != nil {
   219  		utils.PodNotFound(w, name, err)
   220  		return
   221  	}
   222  	responses, err := pod.Restart(r.Context())
   223  	if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
   224  		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
   225  		return
   226  	}
   227  	for id, err := range responses {
   228  		errs = append(errs, errors.Wrapf(err, "error restarting container %s", id))
   229  	}
   230  	report := entities.PodRestartReport{
   231  		Errs: errs,
   232  		Id:   pod.ID(),
   233  	}
   234  	utils.WriteResponse(w, http.StatusOK, report)
   235  }
   236  
   237  func PodPrune(w http.ResponseWriter, r *http.Request) {
   238  	reports, err := PodPruneHelper(w, r)
   239  	if err != nil {
   240  		utils.InternalServerError(w, err)
   241  		return
   242  	}
   243  	utils.WriteResponse(w, http.StatusOK, reports)
   244  }
   245  
   246  func PodPruneHelper(w http.ResponseWriter, r *http.Request) ([]*entities.PodPruneReport, error) {
   247  	var (
   248  		runtime = r.Context().Value("runtime").(*libpod.Runtime)
   249  	)
   250  	responses, err := runtime.PrunePods(r.Context())
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  	reports := make([]*entities.PodPruneReport, 0, len(responses))
   255  	for k, v := range responses {
   256  		reports = append(reports, &entities.PodPruneReport{
   257  			Err: v,
   258  			Id:  k,
   259  		})
   260  	}
   261  	return reports, nil
   262  }
   263  
   264  func PodPause(w http.ResponseWriter, r *http.Request) {
   265  	var errs []error //nolint
   266  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   267  	name := utils.GetName(r)
   268  	pod, err := runtime.LookupPod(name)
   269  	if err != nil {
   270  		utils.PodNotFound(w, name, err)
   271  		return
   272  	}
   273  	responses, err := pod.Pause(r.Context())
   274  	if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
   275  		utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
   276  		return
   277  	}
   278  	for id, v := range responses {
   279  		errs = append(errs, errors.Wrapf(v, "error pausing container %s", id))
   280  	}
   281  	report := entities.PodPauseReport{
   282  		Errs: errs,
   283  		Id:   pod.ID(),
   284  	}
   285  	utils.WriteResponse(w, http.StatusOK, report)
   286  }
   287  
   288  func PodUnpause(w http.ResponseWriter, r *http.Request) {
   289  	var errs []error //nolint
   290  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   291  	name := utils.GetName(r)
   292  	pod, err := runtime.LookupPod(name)
   293  	if err != nil {
   294  		utils.PodNotFound(w, name, err)
   295  		return
   296  	}
   297  	responses, err := pod.Unpause(r.Context())
   298  	if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
   299  		utils.Error(w, "failed to pause pod", http.StatusInternalServerError, err)
   300  		return
   301  	}
   302  	for id, v := range responses {
   303  		errs = append(errs, errors.Wrapf(v, "error unpausing container %s", id))
   304  	}
   305  	report := entities.PodUnpauseReport{
   306  		Errs: errs,
   307  		Id:   pod.ID(),
   308  	}
   309  	utils.WriteResponse(w, http.StatusOK, &report)
   310  }
   311  
   312  func PodTop(w http.ResponseWriter, r *http.Request) {
   313  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   314  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   315  
   316  	query := struct {
   317  		PsArgs string `schema:"ps_args"`
   318  	}{
   319  		PsArgs: "",
   320  	}
   321  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   322  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   323  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   324  		return
   325  	}
   326  
   327  	name := utils.GetName(r)
   328  	pod, err := runtime.LookupPod(name)
   329  	if err != nil {
   330  		utils.PodNotFound(w, name, err)
   331  		return
   332  	}
   333  
   334  	args := []string{}
   335  	if query.PsArgs != "" {
   336  		args = append(args, query.PsArgs)
   337  	}
   338  	output, err := pod.GetPodPidInformation(args)
   339  	if err != nil {
   340  		utils.InternalServerError(w, err)
   341  		return
   342  	}
   343  
   344  	var body = handlers.PodTopOKBody{}
   345  	if len(output) > 0 {
   346  		body.Titles = strings.Split(output[0], "\t")
   347  		for _, line := range output[1:] {
   348  			body.Processes = append(body.Processes, strings.Split(line, "\t"))
   349  		}
   350  	}
   351  	utils.WriteJSON(w, http.StatusOK, body)
   352  }
   353  
   354  func PodKill(w http.ResponseWriter, r *http.Request) {
   355  	var (
   356  		runtime = r.Context().Value("runtime").(*libpod.Runtime)
   357  		decoder = r.Context().Value("decoder").(*schema.Decoder)
   358  		signal  = "SIGKILL"
   359  		errs    []error //nolint
   360  	)
   361  	query := struct {
   362  		Signal string `schema:"signal"`
   363  	}{
   364  		// override any golang type defaults
   365  	}
   366  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   367  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   368  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   369  		return
   370  	}
   371  	if _, found := r.URL.Query()["signal"]; found {
   372  		signal = query.Signal
   373  	}
   374  
   375  	sig, err := util.ParseSignal(signal)
   376  	if err != nil {
   377  		utils.InternalServerError(w, errors.Wrapf(err, "unable to parse signal value"))
   378  		return
   379  	}
   380  	name := utils.GetName(r)
   381  	pod, err := runtime.LookupPod(name)
   382  	if err != nil {
   383  		utils.PodNotFound(w, name, err)
   384  		return
   385  	}
   386  	logrus.Debugf("Killing pod %s with signal %d", pod.ID(), sig)
   387  	podStates, err := pod.Status()
   388  	if err != nil {
   389  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
   390  		return
   391  	}
   392  	hasRunning := false
   393  	for _, s := range podStates {
   394  		if s == define.ContainerStateRunning {
   395  			hasRunning = true
   396  			break
   397  		}
   398  	}
   399  	if !hasRunning {
   400  		msg := fmt.Sprintf("Container %s is not running", pod.ID())
   401  		utils.Error(w, msg, http.StatusConflict, errors.Errorf("cannot kill a pod with no running containers: %s", pod.ID()))
   402  		return
   403  	}
   404  
   405  	responses, err := pod.Kill(r.Context(), uint(sig))
   406  	if err != nil && errors.Cause(err) != define.ErrPodPartialFail {
   407  		utils.Error(w, "failed to kill pod", http.StatusInternalServerError, err)
   408  		return
   409  	}
   410  
   411  	for _, v := range responses {
   412  		if v != nil {
   413  			errs = append(errs, v)
   414  		}
   415  	}
   416  	report := &entities.PodKillReport{
   417  		Errs: errs,
   418  		Id:   pod.ID(),
   419  	}
   420  	utils.WriteResponse(w, http.StatusOK, report)
   421  }
   422  
   423  func PodExists(w http.ResponseWriter, r *http.Request) {
   424  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   425  	name := utils.GetName(r)
   426  	_, err := runtime.LookupPod(name)
   427  	if err != nil {
   428  		utils.PodNotFound(w, name, err)
   429  		return
   430  	}
   431  	utils.WriteResponse(w, http.StatusNoContent, "")
   432  }
   433  
   434  func PodStats(w http.ResponseWriter, r *http.Request) {
   435  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   436  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   437  
   438  	query := struct {
   439  		NamesOrIDs []string `schema:"namesOrIDs"`
   440  		All        bool     `schema:"all"`
   441  	}{
   442  		// default would go here
   443  	}
   444  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   445  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
   446  			errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   447  		return
   448  	}
   449  
   450  	// Validate input.
   451  	options := entities.PodStatsOptions{All: query.All}
   452  	if err := entities.ValidatePodStatsOptions(query.NamesOrIDs, &options); err != nil {
   453  		utils.InternalServerError(w, err)
   454  	}
   455  
   456  	// Collect the stats and send them over the wire.
   457  	containerEngine := abi.ContainerEngine{Libpod: runtime}
   458  	reports, err := containerEngine.PodStats(r.Context(), query.NamesOrIDs, options)
   459  
   460  	// Error checks as documented in swagger.
   461  	switch errors.Cause(err) {
   462  	case define.ErrNoSuchPod:
   463  		utils.Error(w, "one or more pods not found", http.StatusNotFound, err)
   464  		return
   465  	case nil:
   466  		// Nothing to do.
   467  	default:
   468  		utils.InternalServerError(w, err)
   469  		return
   470  	}
   471  
   472  	utils.WriteResponse(w, http.StatusOK, reports)
   473  }