mirror of
https://github.com/containers/podman.git
synced 2025-05-21 17:16:22 +08:00

This ended up more complicated then expected. Lets start first with the problem to show why I am doing this: Currently we simply execute ps(1) in the container. This has some drawbacks. First, obviously you need to have ps(1) in the container image. That is no always the case especially in small images. Second, even if you do it will often be only busybox's ps which supports far less options. Now we also have psgo which is used by default but that only supports a small subset of ps(1) options. Implementing all options there is way to much work. Docker on the other hand executes ps(1) directly on the host and tries to filter pids with `-q` an option which is not supported by busybox's ps and conflicts with other ps(1) arguments. That means they fall back to full ps(1) on the host and then filter based on the pid in the output. This is kinda ugly and fails short because users can modify the ps output and it may not even include the pid in the output which causes an error. So every solution has a different drawback, but what if we can combine them somehow?! This commit tries exactly that. We use ps(1) from the host and execute that in the container's pid namespace. There are some security concerns that must be addressed: - mount the executable paths for ps and podman itself readonly to prevent the container from overwriting it via /proc/self/exe. - set NO_NEW_PRIVS, SET_DUMPABLE and PDEATHSIG - close all non std fds to prevent leaking files in that the caller had open - unset all environment variables to not leak any into the contianer Technically this could be a breaking change if somebody does not have ps on the host and only in the container but I find that very unlikely, we still have the exec in container fallback. Because this can be insecure when the contianer has CAP_SYS_PTRACE we still only use the podman exec version in that case. This updates the docs accordingly, note that podman pod top never falls back to executing ps in the container as this makes no sense with multiple containers so I fixed the docs there as well. Fixes #19001 Fixes https://bugzilla.redhat.com/show_bug.cgi?id=2215572 Signed-off-by: Paul Holzinger <pholzing@redhat.com>
352 lines
11 KiB
Go
352 lines
11 KiB
Go
package server
|
||
|
||
import (
|
||
"net/http"
|
||
|
||
"github.com/containers/podman/v4/pkg/api/handlers/libpod"
|
||
"github.com/gorilla/mux"
|
||
)
|
||
|
||
func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
|
||
// swagger:operation GET /libpod/pods/json pods PodListLibpod
|
||
// ---
|
||
// summary: List pods
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: query
|
||
// name: filters
|
||
// type: string
|
||
// description: |
|
||
// JSON encoded value of the filters (a map[string][]string) to process on the pods list. Available filters:
|
||
// - `id=<pod-id>` Matches all of pod id.
|
||
// - `label=<key>` or `label=<key>:<value>` Matches pods based on the presence of a label alone or a label and a value.
|
||
// - `name=<pod-name>` Matches all of pod name.
|
||
// - `until=<timestamp>` List pods created before this timestamp. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time.
|
||
// - `status=<pod-status>` Pod's status: `stopped`, `running`, `paused`, `exited`, `dead`, `created`, `degraded`.
|
||
// - `network=<pod-network>` Name or full ID of network.
|
||
// - `ctr-names=<pod-ctr-names>` Container name within the pod.
|
||
// - `ctr-ids=<pod-ctr-ids>` Container ID within the pod.
|
||
// - `ctr-status=<pod-ctr-status>` Container status within the pod.
|
||
// - `ctr-number=<pod-ctr-number>` Number of containers in the pod.
|
||
// responses:
|
||
// 200:
|
||
// $ref: "#/responses/podsListResponse"
|
||
// 400:
|
||
// $ref: "#/responses/badParamError"
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/json"), s.APIHandler(libpod.Pods)).Methods(http.MethodGet)
|
||
// swagger:operation POST /libpod/pods/create pods PodCreateLibpod
|
||
// ---
|
||
// summary: Create a pod
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: body
|
||
// name: create
|
||
// description: attributes for creating a pod
|
||
// schema:
|
||
// $ref: "#/definitions/PodSpecGenerator"
|
||
// responses:
|
||
// 201:
|
||
// schema:
|
||
// $ref: "#/definitions/IDResponse"
|
||
// 400:
|
||
// $ref: "#/responses/badParamError"
|
||
// 409:
|
||
// description: status conflict
|
||
// schema:
|
||
// type: string
|
||
// description: message describing error
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/create"), s.APIHandler(libpod.PodCreate)).Methods(http.MethodPost)
|
||
// swagger:operation POST /libpod/pods/prune pods PodPruneLibpod
|
||
// ---
|
||
// summary: Prune unused pods
|
||
// produces:
|
||
// - application/json
|
||
// responses:
|
||
// 200:
|
||
// $ref: '#/responses/podPruneResponse'
|
||
// 400:
|
||
// $ref: "#/responses/badParamError"
|
||
// 409:
|
||
// description: pod already exists
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/prune"), s.APIHandler(libpod.PodPrune)).Methods(http.MethodPost)
|
||
// swagger:operation DELETE /libpod/pods/{name} pods PodDeleteLibpod
|
||
// ---
|
||
// summary: Remove pod
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: path
|
||
// name: name
|
||
// type: string
|
||
// required: true
|
||
// description: the name or ID of the pod
|
||
// - in: query
|
||
// name: force
|
||
// type: boolean
|
||
// description : force removal of a running pod by first stopping all containers, then removing all containers in the pod
|
||
// responses:
|
||
// 200:
|
||
// $ref: '#/responses/podRmResponse'
|
||
// 400:
|
||
// $ref: "#/responses/badParamError"
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/{name}"), s.APIHandler(libpod.PodDelete)).Methods(http.MethodDelete)
|
||
// swagger:operation GET /libpod/pods/{name}/json pods PodInspectLibpod
|
||
// ---
|
||
// summary: Inspect pod
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: path
|
||
// name: name
|
||
// type: string
|
||
// required: true
|
||
// description: the name or ID of the pod
|
||
// responses:
|
||
// 200:
|
||
// $ref: "#/responses/podInspectResponse"
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/{name}/json"), s.APIHandler(libpod.PodInspect)).Methods(http.MethodGet)
|
||
// swagger:operation GET /libpod/pods/{name}/exists pods PodExistsLibpod
|
||
// ---
|
||
// summary: Pod exists
|
||
// description: Check if a pod exists by name or ID
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: path
|
||
// name: name
|
||
// type: string
|
||
// required: true
|
||
// description: the name or ID of the pod
|
||
// responses:
|
||
// 204:
|
||
// description: pod exists
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/{name}/exists"), s.APIHandler(libpod.PodExists)).Methods(http.MethodGet)
|
||
// swagger:operation POST /libpod/pods/{name}/kill pods PodKillLibpod
|
||
// ---
|
||
// summary: Kill a pod
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: path
|
||
// name: name
|
||
// type: string
|
||
// required: true
|
||
// description: the name or ID of the pod
|
||
// - in: query
|
||
// name: signal
|
||
// type: string
|
||
// description: signal to be sent to pod
|
||
// default: SIGKILL
|
||
// responses:
|
||
// 200:
|
||
// $ref: "#/responses/podKillResponse"
|
||
// 400:
|
||
// $ref: "#/responses/badParamError"
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 409:
|
||
// $ref: "#/responses/podKillResponse"
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/{name}/kill"), s.APIHandler(libpod.PodKill)).Methods(http.MethodPost)
|
||
// swagger:operation POST /libpod/pods/{name}/pause pods PodPauseLibpod
|
||
// ---
|
||
// summary: Pause a pod
|
||
// description: Pause a pod
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: path
|
||
// name: name
|
||
// type: string
|
||
// required: true
|
||
// description: the name or ID of the pod
|
||
// responses:
|
||
// 200:
|
||
// $ref: '#/responses/podPauseResponse'
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 409:
|
||
// $ref: '#/responses/podPauseResponse'
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/{name}/pause"), s.APIHandler(libpod.PodPause)).Methods(http.MethodPost)
|
||
// swagger:operation POST /libpod/pods/{name}/restart pods PodRestartLibpod
|
||
// ---
|
||
// summary: Restart a pod
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: path
|
||
// name: name
|
||
// type: string
|
||
// required: true
|
||
// description: the name or ID of the pod
|
||
// responses:
|
||
// 200:
|
||
// $ref: '#/responses/podRestartResponse'
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 409:
|
||
// $ref: "#/responses/podRestartResponse"
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/{name}/restart"), s.APIHandler(libpod.PodRestart)).Methods(http.MethodPost)
|
||
// swagger:operation POST /libpod/pods/{name}/start pods PodStartLibpod
|
||
// ---
|
||
// summary: Start a pod
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: path
|
||
// name: name
|
||
// type: string
|
||
// required: true
|
||
// description: the name or ID of the pod
|
||
// responses:
|
||
// 200:
|
||
// $ref: '#/responses/podStartResponse'
|
||
// 304:
|
||
// $ref: "#/responses/podAlreadyStartedError"
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 409:
|
||
// $ref: '#/responses/podStartResponse'
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/{name}/start"), s.APIHandler(libpod.PodStart)).Methods(http.MethodPost)
|
||
// swagger:operation POST /libpod/pods/{name}/stop pods PodStopLibpod
|
||
// ---
|
||
// summary: Stop a pod
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: path
|
||
// name: name
|
||
// type: string
|
||
// required: true
|
||
// description: the name or ID of the pod
|
||
// - in: query
|
||
// name: t
|
||
// type: integer
|
||
// description: timeout
|
||
// responses:
|
||
// 200:
|
||
// $ref: '#/responses/podStopResponse'
|
||
// 304:
|
||
// $ref: "#/responses/podAlreadyStoppedError"
|
||
// 400:
|
||
// $ref: "#/responses/badParamError"
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 409:
|
||
// $ref: "#/responses/podStopResponse"
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/{name}/stop"), s.APIHandler(libpod.PodStop)).Methods(http.MethodPost)
|
||
// swagger:operation POST /libpod/pods/{name}/unpause pods PodUnpauseLibpod
|
||
// ---
|
||
// summary: Unpause a pod
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: path
|
||
// name: name
|
||
// type: string
|
||
// required: true
|
||
// description: the name or ID of the pod
|
||
// responses:
|
||
// 200:
|
||
// $ref: '#/responses/podUnpauseResponse'
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 409:
|
||
// $ref: '#/responses/podUnpauseResponse'
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/{name}/unpause"), s.APIHandler(libpod.PodUnpause)).Methods(http.MethodPost)
|
||
// swagger:operation GET /libpod/pods/{name}/top pods PodTopLibpod
|
||
// ---
|
||
// summary: List processes
|
||
// description: List processes running inside a pod
|
||
// produces:
|
||
// - application/json
|
||
// parameters:
|
||
// - in: path
|
||
// name: name
|
||
// type: string
|
||
// required: true
|
||
// description: Name of pod to query for processes
|
||
// - in: query
|
||
// name: stream
|
||
// type: boolean
|
||
// description: when true, repeatedly stream the latest output (As of version 4.0)
|
||
// - in: query
|
||
// name: delay
|
||
// type: integer
|
||
// description: if streaming, delay in seconds between updates. Must be >1. (As of version 4.0)
|
||
// default: 5
|
||
// - in: query
|
||
// name: ps_args
|
||
// type: string
|
||
// default:
|
||
// description: |
|
||
// arguments to pass to ps such as aux.
|
||
// responses:
|
||
// 200:
|
||
// $ref: "#/responses/podTopResponse"
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/{name}/top"), s.APIHandler(libpod.PodTop)).Methods(http.MethodGet)
|
||
// swagger:operation GET /libpod/pods/stats pods PodStatsAllLibpod
|
||
// ---
|
||
// tags:
|
||
// - pods
|
||
// summary: Statistics for one or more pods
|
||
// description: Display a live stream of resource usage statistics for the containers in one or more pods
|
||
// parameters:
|
||
// - in: query
|
||
// name: all
|
||
// description: Provide statistics for all running pods.
|
||
// type: boolean
|
||
// - in: query
|
||
// name: namesOrIDs
|
||
// description: Names or IDs of pods.
|
||
// type: array
|
||
// items:
|
||
// type: string
|
||
// produces:
|
||
// - application/json
|
||
// responses:
|
||
// 200:
|
||
// $ref: "#/responses/podStatsResponse"
|
||
// 404:
|
||
// $ref: "#/responses/podNotFound"
|
||
// 500:
|
||
// $ref: "#/responses/internalError"
|
||
r.Handle(VersionedPath("/libpod/pods/stats"), s.APIHandler(libpod.PodStats)).Methods(http.MethodGet)
|
||
return nil
|
||
}
|