mirror of
				https://github.com/containers/podman.git
				synced 2025-10-31 10:00:01 +08:00 
			
		
		
		
	 6f7b7060e8
			
		
	
	6f7b7060e8
	
	
	
		
			
			These are the first fixes that are needed for development environments like Eclipse or IntelliJ that have Docker plug-ins and use the Docker API to speak with container engine (#7857) Signed-off-by: Milivoje Legenovic <m.legenovic@gmail.com>
		
			
				
	
	
		
			440 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			440 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package compat
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"syscall"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/containers/podman/v2/libpod"
 | |
| 	"github.com/containers/podman/v2/libpod/define"
 | |
| 	"github.com/containers/podman/v2/pkg/api/handlers"
 | |
| 	"github.com/containers/podman/v2/pkg/api/handlers/utils"
 | |
| 	"github.com/containers/podman/v2/pkg/signal"
 | |
| 	"github.com/docker/docker/api/types"
 | |
| 	"github.com/docker/docker/api/types/container"
 | |
| 	"github.com/docker/go-connections/nat"
 | |
| 	"github.com/gorilla/schema"
 | |
| 	"github.com/pkg/errors"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| )
 | |
| 
 | |
| func RemoveContainer(w http.ResponseWriter, r *http.Request) {
 | |
| 	decoder := r.Context().Value("decoder").(*schema.Decoder)
 | |
| 	query := struct {
 | |
| 		Force bool `schema:"force"`
 | |
| 		Vols  bool `schema:"v"`
 | |
| 		Link  bool `schema:"link"`
 | |
| 	}{
 | |
| 		// 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
 | |
| 	}
 | |
| 
 | |
| 	if query.Link && !utils.IsLibpodRequest(r) {
 | |
| 		utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
 | |
| 			utils.ErrLinkNotSupport)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | |
| 	name := utils.GetName(r)
 | |
| 	con, err := runtime.LookupContainer(name)
 | |
| 	if err != nil && errors.Cause(err) == define.ErrNoSuchCtr {
 | |
| 		// Failed to get container. If force is specified, get the container's ID
 | |
| 		// and evict it
 | |
| 		if !query.Force {
 | |
| 			utils.ContainerNotFound(w, name, err)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		if _, err := runtime.EvictContainer(r.Context(), name, query.Vols); err != nil {
 | |
| 			if errors.Cause(err) == define.ErrNoSuchCtr {
 | |
| 				logrus.Debugf("Ignoring error (--allow-missing): %q", err)
 | |
| 				w.WriteHeader(http.StatusNoContent)
 | |
| 				return
 | |
| 			}
 | |
| 			logrus.Warn(errors.Wrapf(err, "failed to evict container: %q", name))
 | |
| 			utils.InternalServerError(w, err)
 | |
| 			return
 | |
| 		}
 | |
| 		w.WriteHeader(http.StatusNoContent)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if err := runtime.RemoveContainer(r.Context(), con, query.Force, query.Vols); err != nil {
 | |
| 		utils.InternalServerError(w, err)
 | |
| 		return
 | |
| 	}
 | |
| 	utils.WriteResponse(w, http.StatusNoContent, "")
 | |
| }
 | |
| 
 | |
| func ListContainers(w http.ResponseWriter, r *http.Request) {
 | |
| 	var (
 | |
| 		containers []*libpod.Container
 | |
| 		err        error
 | |
| 	)
 | |
| 	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | |
| 	decoder := r.Context().Value("decoder").(*schema.Decoder)
 | |
| 	query := struct {
 | |
| 		All     bool                `schema:"all"`
 | |
| 		Limit   int                 `schema:"limit"`
 | |
| 		Size    bool                `schema:"size"`
 | |
| 		Filters map[string][]string `schema:"filters"`
 | |
| 	}{
 | |
| 		// override any golang type defaults
 | |
| 	}
 | |
| 
 | |
| 	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
 | |
| 		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
 | |
| 		return
 | |
| 	}
 | |
| 	if query.All {
 | |
| 		containers, err = runtime.GetAllContainers()
 | |
| 	} else {
 | |
| 		containers, err = runtime.GetRunningContainers()
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		utils.InternalServerError(w, err)
 | |
| 		return
 | |
| 	}
 | |
| 	if _, found := r.URL.Query()["limit"]; found && query.Limit > 0 {
 | |
| 		last := query.Limit
 | |
| 		if len(containers) > last {
 | |
| 			containers = containers[len(containers)-last:]
 | |
| 		}
 | |
| 	}
 | |
| 	// TODO filters still need to be applied
 | |
| 	var list = make([]*handlers.Container, len(containers))
 | |
| 	for i, ctnr := range containers {
 | |
| 		api, err := LibpodToContainer(ctnr, query.Size)
 | |
| 		if err != nil {
 | |
| 			utils.InternalServerError(w, err)
 | |
| 			return
 | |
| 		}
 | |
| 		list[i] = api
 | |
| 	}
 | |
| 	utils.WriteResponse(w, http.StatusOK, list)
 | |
| }
 | |
| 
 | |
| func GetContainer(w http.ResponseWriter, r *http.Request) {
 | |
| 	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | |
| 	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, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	name := utils.GetName(r)
 | |
| 	ctnr, err := runtime.LookupContainer(name)
 | |
| 	if err != nil {
 | |
| 		utils.ContainerNotFound(w, name, err)
 | |
| 		return
 | |
| 	}
 | |
| 	api, err := LibpodToContainerJSON(ctnr, query.Size)
 | |
| 	if err != nil {
 | |
| 		utils.InternalServerError(w, err)
 | |
| 		return
 | |
| 	}
 | |
| 	utils.WriteResponse(w, http.StatusOK, api)
 | |
| }
 | |
| 
 | |
| func KillContainer(w http.ResponseWriter, r *http.Request) {
 | |
| 	// /{version}/containers/(name)/kill
 | |
| 	runtime := r.Context().Value("runtime").(*libpod.Runtime)
 | |
| 	decoder := r.Context().Value("decoder").(*schema.Decoder)
 | |
| 	query := struct {
 | |
| 		Signal string `schema:"signal"`
 | |
| 	}{
 | |
| 		Signal: "KILL",
 | |
| 	}
 | |
| 	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
 | |
| 		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	sig, err := signal.ParseSignalNameOrNumber(query.Signal)
 | |
| 	if err != nil {
 | |
| 		utils.InternalServerError(w, err)
 | |
| 		return
 | |
| 	}
 | |
| 	name := utils.GetName(r)
 | |
| 	con, err := runtime.LookupContainer(name)
 | |
| 	if err != nil {
 | |
| 		utils.ContainerNotFound(w, name, err)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	state, err := con.State()
 | |
| 	if err != nil {
 | |
| 		utils.InternalServerError(w, err)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// If the Container is stopped already, send a 409
 | |
| 	if state == define.ContainerStateStopped || state == define.ContainerStateExited {
 | |
| 		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)))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	signal := uint(sig)
 | |
| 
 | |
| 	err = con.Kill(signal)
 | |
| 	if err != nil {
 | |
| 		utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "unable to kill Container %s", name))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Docker waits for the container to stop if the signal is 0 or
 | |
| 	// SIGKILL.
 | |
| 	if !utils.IsLibpodRequest(r) && (signal == 0 || syscall.Signal(signal) == syscall.SIGKILL) {
 | |
| 		if _, err = con.Wait(); err != nil {
 | |
| 			utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to wait for Container %s", con.ID()))
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	// Success
 | |
| 	utils.WriteResponse(w, http.StatusNoContent, "")
 | |
| }
 | |
| 
 | |
| func WaitContainer(w http.ResponseWriter, r *http.Request) {
 | |
| 	var msg string
 | |
| 	// /{version}/containers/(name)/wait
 | |
| 	exitCode, err := utils.WaitContainer(w, r)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	utils.WriteResponse(w, http.StatusOK, handlers.ContainerWaitOKBody{
 | |
| 		StatusCode: int(exitCode),
 | |
| 		Error: struct {
 | |
| 			Message string
 | |
| 		}{
 | |
| 			Message: msg,
 | |
| 		},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error) {
 | |
| 	imageID, imageName := l.Image()
 | |
| 
 | |
| 	var (
 | |
| 		err        error
 | |
| 		sizeRootFs int64
 | |
| 		sizeRW     int64
 | |
| 		state      define.ContainerStatus
 | |
| 	)
 | |
| 
 | |
| 	if state, err = l.State(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	stateStr := state.String()
 | |
| 	if stateStr == "configured" {
 | |
| 		stateStr = "created"
 | |
| 	}
 | |
| 
 | |
| 	if sz {
 | |
| 		if sizeRW, err = l.RWSize(); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if sizeRootFs, err = l.RootFsSize(); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return &handlers.Container{Container: types.Container{
 | |
| 		ID:         l.ID(),
 | |
| 		Names:      []string{fmt.Sprintf("/%s", l.Name())},
 | |
| 		Image:      imageName,
 | |
| 		ImageID:    imageID,
 | |
| 		Command:    strings.Join(l.Command(), " "),
 | |
| 		Created:    l.CreatedTime().Unix(),
 | |
| 		Ports:      nil,
 | |
| 		SizeRw:     sizeRW,
 | |
| 		SizeRootFs: sizeRootFs,
 | |
| 		Labels:     l.Labels(),
 | |
| 		State:      stateStr,
 | |
| 		Status:     "",
 | |
| 		HostConfig: struct {
 | |
| 			NetworkMode string `json:",omitempty"`
 | |
| 		}{
 | |
| 			"host"},
 | |
| 		NetworkSettings: nil,
 | |
| 		Mounts:          nil,
 | |
| 	},
 | |
| 		ContainerCreateConfig: types.ContainerCreateConfig{},
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, error) {
 | |
| 	_, imageName := l.Image()
 | |
| 	inspect, err := l.Inspect(sz)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	i, err := json.Marshal(inspect.State)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	state := types.ContainerState{}
 | |
| 	if err := json.Unmarshal(i, &state); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// docker considers paused to be running
 | |
| 	if state.Paused {
 | |
| 		state.Running = true
 | |
| 	}
 | |
| 
 | |
| 	formatCapabilities(inspect.HostConfig.CapDrop)
 | |
| 	formatCapabilities(inspect.HostConfig.CapAdd)
 | |
| 
 | |
| 	h, err := json.Marshal(inspect.HostConfig)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	hc := container.HostConfig{}
 | |
| 	if err := json.Unmarshal(h, &hc); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	g, err := json.Marshal(inspect.GraphDriver)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	graphDriver := types.GraphDriverData{}
 | |
| 	if err := json.Unmarshal(g, &graphDriver); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	cb := types.ContainerJSONBase{
 | |
| 		ID:              l.ID(),
 | |
| 		Created:         l.CreatedTime().Format(time.RFC3339Nano),
 | |
| 		Path:            inspect.Path,
 | |
| 		Args:            inspect.Args,
 | |
| 		State:           &state,
 | |
| 		Image:           imageName,
 | |
| 		ResolvConfPath:  inspect.ResolvConfPath,
 | |
| 		HostnamePath:    inspect.HostnamePath,
 | |
| 		HostsPath:       inspect.HostsPath,
 | |
| 		LogPath:         l.LogPath(),
 | |
| 		Node:            nil,
 | |
| 		Name:            fmt.Sprintf("/%s", l.Name()),
 | |
| 		RestartCount:    int(inspect.RestartCount),
 | |
| 		Driver:          inspect.Driver,
 | |
| 		Platform:        "linux",
 | |
| 		MountLabel:      inspect.MountLabel,
 | |
| 		ProcessLabel:    inspect.ProcessLabel,
 | |
| 		AppArmorProfile: inspect.AppArmorProfile,
 | |
| 		ExecIDs:         inspect.ExecIDs,
 | |
| 		HostConfig:      &hc,
 | |
| 		GraphDriver:     graphDriver,
 | |
| 		SizeRw:          inspect.SizeRw,
 | |
| 		SizeRootFs:      &inspect.SizeRootFs,
 | |
| 	}
 | |
| 
 | |
| 	// set Path and Args
 | |
| 	processArgs := l.Config().Spec.Process.Args
 | |
| 	if len(processArgs) > 0 {
 | |
| 		cb.Path = processArgs[0]
 | |
| 	}
 | |
| 	if len(processArgs) > 1 {
 | |
| 		cb.Args = processArgs[1:]
 | |
| 	}
 | |
| 	stopTimeout := int(l.StopTimeout())
 | |
| 
 | |
| 	exposedPorts := make(nat.PortSet)
 | |
| 	for ep := range inspect.HostConfig.PortBindings {
 | |
| 		splitp := strings.SplitN(ep, "/", 2)
 | |
| 		if len(splitp) != 2 {
 | |
| 			return nil, errors.Errorf("PORT/PROTOCOL Format required for %q", ep)
 | |
| 		}
 | |
| 		exposedPort, err := nat.NewPort(splitp[1], splitp[0])
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		exposedPorts[exposedPort] = struct{}{}
 | |
| 	}
 | |
| 
 | |
| 	config := container.Config{
 | |
| 		Hostname:        l.Hostname(),
 | |
| 		Domainname:      inspect.Config.DomainName,
 | |
| 		User:            l.User(),
 | |
| 		AttachStdin:     inspect.Config.AttachStdin,
 | |
| 		AttachStdout:    inspect.Config.AttachStdout,
 | |
| 		AttachStderr:    inspect.Config.AttachStderr,
 | |
| 		ExposedPorts:    exposedPorts,
 | |
| 		Tty:             inspect.Config.Tty,
 | |
| 		OpenStdin:       inspect.Config.OpenStdin,
 | |
| 		StdinOnce:       inspect.Config.StdinOnce,
 | |
| 		Env:             inspect.Config.Env,
 | |
| 		Cmd:             l.Command(),
 | |
| 		Healthcheck:     nil,
 | |
| 		ArgsEscaped:     false,
 | |
| 		Image:           imageName,
 | |
| 		Volumes:         nil,
 | |
| 		WorkingDir:      l.WorkingDir(),
 | |
| 		Entrypoint:      l.Entrypoint(),
 | |
| 		NetworkDisabled: false,
 | |
| 		MacAddress:      "",
 | |
| 		OnBuild:         nil,
 | |
| 		Labels:          l.Labels(),
 | |
| 		StopSignal:      strconv.Itoa(int(l.StopSignal())),
 | |
| 		StopTimeout:     &stopTimeout,
 | |
| 		Shell:           nil,
 | |
| 	}
 | |
| 
 | |
| 	m, err := json.Marshal(inspect.Mounts)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	mounts := []types.MountPoint{}
 | |
| 	if err := json.Unmarshal(m, &mounts); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	p, err := json.Marshal(inspect.NetworkSettings.Ports)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	ports := nat.PortMap{}
 | |
| 	if err := json.Unmarshal(p, &ports); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	n, err := json.Marshal(inspect.NetworkSettings)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	networkSettings := types.NetworkSettings{}
 | |
| 	if err := json.Unmarshal(n, &networkSettings); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	c := types.ContainerJSON{
 | |
| 		ContainerJSONBase: &cb,
 | |
| 		Mounts:            mounts,
 | |
| 		Config:            &config,
 | |
| 		NetworkSettings:   &networkSettings,
 | |
| 	}
 | |
| 	return &c, nil
 | |
| }
 | |
| 
 | |
| func formatCapabilities(slice []string) {
 | |
| 	for i := range slice {
 | |
| 		slice[i] = strings.TrimPrefix(slice[i], "CAP_")
 | |
| 	}
 | |
| }
 |