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

     1  package compat
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strconv"
     8  	"strings"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/containers/podman/v2/libpod"
    13  	"github.com/containers/podman/v2/libpod/define"
    14  	"github.com/containers/podman/v2/pkg/api/handlers"
    15  	"github.com/containers/podman/v2/pkg/api/handlers/utils"
    16  	"github.com/containers/podman/v2/pkg/signal"
    17  	"github.com/docker/docker/api/types"
    18  	"github.com/docker/docker/api/types/container"
    19  	"github.com/docker/go-connections/nat"
    20  	"github.com/gorilla/schema"
    21  	"github.com/pkg/errors"
    22  	"github.com/sirupsen/logrus"
    23  )
    24  
    25  func RemoveContainer(w http.ResponseWriter, r *http.Request) {
    26  	decoder := r.Context().Value("decoder").(*schema.Decoder)
    27  	query := struct {
    28  		Force bool `schema:"force"`
    29  		Vols  bool `schema:"v"`
    30  		Link  bool `schema:"link"`
    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  	if query.Link && !utils.IsLibpodRequest(r) {
    42  		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
    43  			utils.ErrLinkNotSupport)
    44  		return
    45  	}
    46  
    47  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
    48  	name := utils.GetName(r)
    49  	con, err := runtime.LookupContainer(name)
    50  	if err != nil && errors.Cause(err) == define.ErrNoSuchCtr {
    51  		// Failed to get container. If force is specified, get the container's ID
    52  		// and evict it
    53  		if !query.Force {
    54  			utils.ContainerNotFound(w, name, err)
    55  			return
    56  		}
    57  
    58  		if _, err := runtime.EvictContainer(r.Context(), name, query.Vols); err != nil {
    59  			if errors.Cause(err) == define.ErrNoSuchCtr {
    60  				logrus.Debugf("Ignoring error (--allow-missing): %q", err)
    61  				w.WriteHeader(http.StatusNoContent)
    62  				return
    63  			}
    64  			logrus.Warn(errors.Wrapf(err, "failed to evict container: %q", name))
    65  			utils.InternalServerError(w, err)
    66  			return
    67  		}
    68  		w.WriteHeader(http.StatusNoContent)
    69  		return
    70  	}
    71  
    72  	if err := runtime.RemoveContainer(r.Context(), con, query.Force, query.Vols); err != nil {
    73  		utils.InternalServerError(w, err)
    74  		return
    75  	}
    76  	utils.WriteResponse(w, http.StatusNoContent, "")
    77  }
    78  
    79  func ListContainers(w http.ResponseWriter, r *http.Request) {
    80  	var (
    81  		containers []*libpod.Container
    82  		err        error
    83  	)
    84  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
    85  	decoder := r.Context().Value("decoder").(*schema.Decoder)
    86  	query := struct {
    87  		All     bool                `schema:"all"`
    88  		Limit   int                 `schema:"limit"`
    89  		Size    bool                `schema:"size"`
    90  		Filters map[string][]string `schema:"filters"`
    91  	}{
    92  		// override any golang type defaults
    93  	}
    94  
    95  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
    96  		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
    97  		return
    98  	}
    99  	if query.All {
   100  		containers, err = runtime.GetAllContainers()
   101  	} else {
   102  		containers, err = runtime.GetRunningContainers()
   103  	}
   104  	if err != nil {
   105  		utils.InternalServerError(w, err)
   106  		return
   107  	}
   108  	if _, found := r.URL.Query()["limit"]; found && query.Limit > 0 {
   109  		last := query.Limit
   110  		if len(containers) > last {
   111  			containers = containers[len(containers)-last:]
   112  		}
   113  	}
   114  	// TODO filters still need to be applied
   115  	var list = make([]*handlers.Container, len(containers))
   116  	for i, ctnr := range containers {
   117  		api, err := LibpodToContainer(ctnr, query.Size)
   118  		if err != nil {
   119  			utils.InternalServerError(w, err)
   120  			return
   121  		}
   122  		list[i] = api
   123  	}
   124  	utils.WriteResponse(w, http.StatusOK, list)
   125  }
   126  
   127  func GetContainer(w http.ResponseWriter, r *http.Request) {
   128  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   129  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   130  	query := struct {
   131  		Size bool `schema:"size"`
   132  	}{
   133  		// override any golang type defaults
   134  	}
   135  
   136  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   137  		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   138  		return
   139  	}
   140  
   141  	name := utils.GetName(r)
   142  	ctnr, err := runtime.LookupContainer(name)
   143  	if err != nil {
   144  		utils.ContainerNotFound(w, name, err)
   145  		return
   146  	}
   147  	api, err := LibpodToContainerJSON(ctnr, query.Size)
   148  	if err != nil {
   149  		utils.InternalServerError(w, err)
   150  		return
   151  	}
   152  	utils.WriteResponse(w, http.StatusOK, api)
   153  }
   154  
   155  func KillContainer(w http.ResponseWriter, r *http.Request) {
   156  	// /{version}/containers/(name)/kill
   157  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
   158  	decoder := r.Context().Value("decoder").(*schema.Decoder)
   159  	query := struct {
   160  		Signal string `schema:"signal"`
   161  	}{
   162  		Signal: "KILL",
   163  	}
   164  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
   165  		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
   166  		return
   167  	}
   168  
   169  	sig, err := signal.ParseSignalNameOrNumber(query.Signal)
   170  	if err != nil {
   171  		utils.InternalServerError(w, err)
   172  		return
   173  	}
   174  	name := utils.GetName(r)
   175  	con, err := runtime.LookupContainer(name)
   176  	if err != nil {
   177  		utils.ContainerNotFound(w, name, err)
   178  		return
   179  	}
   180  
   181  	state, err := con.State()
   182  	if err != nil {
   183  		utils.InternalServerError(w, err)
   184  		return
   185  	}
   186  
   187  	// If the Container is stopped already, send a 409
   188  	if state == define.ContainerStateStopped || state == define.ContainerStateExited {
   189  		utils.Error(w, fmt.Sprintf("Container %s is not running", name), http.StatusConflict, errors.New(fmt.Sprintf("Cannot kill Container %s, it is not running", name)))
   190  		return
   191  	}
   192  
   193  	signal := uint(sig)
   194  
   195  	err = con.Kill(signal)
   196  	if err != nil {
   197  		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "unable to kill Container %s", name))
   198  		return
   199  	}
   200  
   201  	// Docker waits for the container to stop if the signal is 0 or
   202  	// SIGKILL.
   203  	if !utils.IsLibpodRequest(r) && (signal == 0 || syscall.Signal(signal) == syscall.SIGKILL) {
   204  		if _, err = con.Wait(); err != nil {
   205  			utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to wait for Container %s", con.ID()))
   206  			return
   207  		}
   208  	}
   209  	// Success
   210  	utils.WriteResponse(w, http.StatusNoContent, "")
   211  }
   212  
   213  func WaitContainer(w http.ResponseWriter, r *http.Request) {
   214  	var msg string
   215  	// /{version}/containers/(name)/wait
   216  	exitCode, err := utils.WaitContainer(w, r)
   217  	if err != nil {
   218  		return
   219  	}
   220  	utils.WriteResponse(w, http.StatusOK, handlers.ContainerWaitOKBody{
   221  		StatusCode: int(exitCode),
   222  		Error: struct {
   223  			Message string
   224  		}{
   225  			Message: msg,
   226  		},
   227  	})
   228  }
   229  
   230  func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error) {
   231  	imageID, imageName := l.Image()
   232  
   233  	var (
   234  		err        error
   235  		sizeRootFs int64
   236  		sizeRW     int64
   237  		state      define.ContainerStatus
   238  	)
   239  
   240  	if state, err = l.State(); err != nil {
   241  		return nil, err
   242  	}
   243  	stateStr := state.String()
   244  	if stateStr == "configured" {
   245  		stateStr = "created"
   246  	}
   247  
   248  	if sz {
   249  		if sizeRW, err = l.RWSize(); err != nil {
   250  			return nil, err
   251  		}
   252  		if sizeRootFs, err = l.RootFsSize(); err != nil {
   253  			return nil, err
   254  		}
   255  	}
   256  
   257  	return &handlers.Container{Container: types.Container{
   258  		ID:         l.ID(),
   259  		Names:      []string{fmt.Sprintf("/%s", l.Name())},
   260  		Image:      imageName,
   261  		ImageID:    imageID,
   262  		Command:    strings.Join(l.Command(), " "),
   263  		Created:    l.CreatedTime().Unix(),
   264  		Ports:      nil,
   265  		SizeRw:     sizeRW,
   266  		SizeRootFs: sizeRootFs,
   267  		Labels:     l.Labels(),
   268  		State:      stateStr,
   269  		Status:     "",
   270  		HostConfig: struct {
   271  			NetworkMode string `json:",omitempty"`
   272  		}{
   273  			"host"},
   274  		NetworkSettings: nil,
   275  		Mounts:          nil,
   276  	},
   277  		ContainerCreateConfig: types.ContainerCreateConfig{},
   278  	}, nil
   279  }
   280  
   281  func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, error) {
   282  	_, imageName := l.Image()
   283  	inspect, err := l.Inspect(sz)
   284  	if err != nil {
   285  		return nil, err
   286  	}
   287  	i, err := json.Marshal(inspect.State)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  	state := types.ContainerState{}
   292  	if err := json.Unmarshal(i, &state); err != nil {
   293  		return nil, err
   294  	}
   295  
   296  	// docker considers paused to be running
   297  	if state.Paused {
   298  		state.Running = true
   299  	}
   300  
   301  	formatCapabilities(inspect.HostConfig.CapDrop)
   302  	formatCapabilities(inspect.HostConfig.CapAdd)
   303  
   304  	h, err := json.Marshal(inspect.HostConfig)
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  	hc := container.HostConfig{}
   309  	if err := json.Unmarshal(h, &hc); err != nil {
   310  		return nil, err
   311  	}
   312  	g, err := json.Marshal(inspect.GraphDriver)
   313  	if err != nil {
   314  		return nil, err
   315  	}
   316  	graphDriver := types.GraphDriverData{}
   317  	if err := json.Unmarshal(g, &graphDriver); err != nil {
   318  		return nil, err
   319  	}
   320  
   321  	cb := types.ContainerJSONBase{
   322  		ID:              l.ID(),
   323  		Created:         l.CreatedTime().Format(time.RFC3339Nano),
   324  		Path:            inspect.Path,
   325  		Args:            inspect.Args,
   326  		State:           &state,
   327  		Image:           imageName,
   328  		ResolvConfPath:  inspect.ResolvConfPath,
   329  		HostnamePath:    inspect.HostnamePath,
   330  		HostsPath:       inspect.HostsPath,
   331  		LogPath:         l.LogPath(),
   332  		Node:            nil,
   333  		Name:            fmt.Sprintf("/%s", l.Name()),
   334  		RestartCount:    int(inspect.RestartCount),
   335  		Driver:          inspect.Driver,
   336  		Platform:        "linux",
   337  		MountLabel:      inspect.MountLabel,
   338  		ProcessLabel:    inspect.ProcessLabel,
   339  		AppArmorProfile: inspect.AppArmorProfile,
   340  		ExecIDs:         inspect.ExecIDs,
   341  		HostConfig:      &hc,
   342  		GraphDriver:     graphDriver,
   343  		SizeRw:          inspect.SizeRw,
   344  		SizeRootFs:      &inspect.SizeRootFs,
   345  	}
   346  
   347  	// set Path and Args
   348  	processArgs := l.Config().Spec.Process.Args
   349  	if len(processArgs) > 0 {
   350  		cb.Path = processArgs[0]
   351  	}
   352  	if len(processArgs) > 1 {
   353  		cb.Args = processArgs[1:]
   354  	}
   355  	stopTimeout := int(l.StopTimeout())
   356  
   357  	exposedPorts := make(nat.PortSet)
   358  	for ep := range inspect.HostConfig.PortBindings {
   359  		splitp := strings.SplitN(ep, "/", 2)
   360  		if len(splitp) != 2 {
   361  			return nil, errors.Errorf("PORT/PROTOCOL Format required for %q", ep)
   362  		}
   363  		exposedPort, err := nat.NewPort(splitp[1], splitp[0])
   364  		if err != nil {
   365  			return nil, err
   366  		}
   367  		exposedPorts[exposedPort] = struct{}{}
   368  	}
   369  
   370  	config := container.Config{
   371  		Hostname:        l.Hostname(),
   372  		Domainname:      inspect.Config.DomainName,
   373  		User:            l.User(),
   374  		AttachStdin:     inspect.Config.AttachStdin,
   375  		AttachStdout:    inspect.Config.AttachStdout,
   376  		AttachStderr:    inspect.Config.AttachStderr,
   377  		ExposedPorts:    exposedPorts,
   378  		Tty:             inspect.Config.Tty,
   379  		OpenStdin:       inspect.Config.OpenStdin,
   380  		StdinOnce:       inspect.Config.StdinOnce,
   381  		Env:             inspect.Config.Env,
   382  		Cmd:             l.Command(),
   383  		Healthcheck:     nil,
   384  		ArgsEscaped:     false,
   385  		Image:           imageName,
   386  		Volumes:         nil,
   387  		WorkingDir:      l.WorkingDir(),
   388  		Entrypoint:      l.Entrypoint(),
   389  		NetworkDisabled: false,
   390  		MacAddress:      "",
   391  		OnBuild:         nil,
   392  		Labels:          l.Labels(),
   393  		StopSignal:      strconv.Itoa(int(l.StopSignal())),
   394  		StopTimeout:     &stopTimeout,
   395  		Shell:           nil,
   396  	}
   397  
   398  	m, err := json.Marshal(inspect.Mounts)
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  	mounts := []types.MountPoint{}
   403  	if err := json.Unmarshal(m, &mounts); err != nil {
   404  		return nil, err
   405  	}
   406  
   407  	p, err := json.Marshal(inspect.NetworkSettings.Ports)
   408  	if err != nil {
   409  		return nil, err
   410  	}
   411  	ports := nat.PortMap{}
   412  	if err := json.Unmarshal(p, &ports); err != nil {
   413  		return nil, err
   414  	}
   415  
   416  	n, err := json.Marshal(inspect.NetworkSettings)
   417  	if err != nil {
   418  		return nil, err
   419  	}
   420  
   421  	networkSettings := types.NetworkSettings{}
   422  	if err := json.Unmarshal(n, &networkSettings); err != nil {
   423  		return nil, err
   424  	}
   425  
   426  	c := types.ContainerJSON{
   427  		ContainerJSONBase: &cb,
   428  		Mounts:            mounts,
   429  		Config:            &config,
   430  		NetworkSettings:   &networkSettings,
   431  	}
   432  	return &c, nil
   433  }
   434  
   435  func formatCapabilities(slice []string) {
   436  	for i := range slice {
   437  		slice[i] = strings.TrimPrefix(slice[i], "CAP_")
   438  	}
   439  }