summaryrefslogtreecommitdiff
path: root/pkg/api
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api')
-rw-r--r--pkg/api/Makefile6
-rw-r--r--pkg/api/handlers/containers_attach.go159
-rw-r--r--pkg/api/handlers/images.go17
-rw-r--r--pkg/api/handlers/libpod/containers.go16
-rw-r--r--pkg/api/handlers/libpod/pods.go17
-rw-r--r--pkg/api/handlers/swagger.go4
-rw-r--r--pkg/api/handlers/utils/errors.go5
-rw-r--r--pkg/api/server/listener_api.go31
-rw-r--r--pkg/api/server/register_containers.go200
-rw-r--r--pkg/api/server/register_images.go12
-rw-r--r--pkg/api/server/register_pods.go3
-rw-r--r--pkg/api/server/server.go60
12 files changed, 497 insertions, 33 deletions
diff --git a/pkg/api/Makefile b/pkg/api/Makefile
index 5fb4e7da5..8a1556800 100644
--- a/pkg/api/Makefile
+++ b/pkg/api/Makefile
@@ -1,3 +1,7 @@
+export GO111MODULE=off
+
+SWAGGER_OUT ?= swagger.yaml
+
swagger:
- swagger generate spec -o swagger.yaml -w ./
+ swagger generate spec -o ${SWAGGER_OUT} -w ./
cat tags.yaml >> swagger.yaml
diff --git a/pkg/api/handlers/containers_attach.go b/pkg/api/handlers/containers_attach.go
new file mode 100644
index 000000000..eb306348b
--- /dev/null
+++ b/pkg/api/handlers/containers_attach.go
@@ -0,0 +1,159 @@
+package handlers
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/gorilla/mux"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+func AttachContainer(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+
+ query := struct {
+ DetachKeys string `schema:"detachKeys"`
+ Logs bool `schema:"logs"`
+ Stream bool `schema:"stream"`
+ Stdin bool `schema:"stdin"`
+ Stdout bool `schema:"stdout"`
+ Stderr bool `schema:"stderr"`
+ }{}
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, "Error parsing parameters", http.StatusBadRequest, err)
+ return
+ }
+
+ muxVars := mux.Vars(r)
+
+ // Detach keys: explicitly set to "" is very different from unset
+ // TODO: Our format for parsing these may be different from Docker.
+ var detachKeys *string
+ if _, found := muxVars["detachKeys"]; found {
+ detachKeys = &query.DetachKeys
+ }
+
+ streams := new(libpod.HTTPAttachStreams)
+ streams.Stdout = true
+ streams.Stderr = true
+ streams.Stdin = true
+ useStreams := false
+ if _, found := muxVars["stdin"]; found {
+ streams.Stdin = query.Stdin
+ useStreams = true
+ }
+ if _, found := muxVars["stdout"]; found {
+ streams.Stdout = query.Stdout
+ useStreams = true
+ }
+ if _, found := muxVars["stderr"]; found {
+ streams.Stderr = query.Stderr
+ useStreams = true
+ }
+ if !useStreams {
+ streams = nil
+ }
+ if useStreams && !streams.Stdout && !streams.Stderr && !streams.Stdin {
+ utils.Error(w, "Parameter conflict", http.StatusBadRequest, errors.Errorf("at least one of stdin, stdout, stderr must be true"))
+ return
+ }
+
+ // TODO: Investigate supporting these.
+ // Logs replays container logs over the attach socket.
+ // Stream seems to break things up somehow? Not 100% clear.
+ if query.Logs {
+ utils.Error(w, "Unsupported parameter", http.StatusBadRequest, errors.Errorf("the logs parameter to attach is not presently supported"))
+ return
+ }
+ // We only support stream=true or unset
+ if _, found := muxVars["stream"]; found && query.Stream {
+ utils.Error(w, "Unsupported parameter", http.StatusBadRequest, errors.Errorf("the stream parameter to attach is not presently supported"))
+ return
+ }
+
+ name := getName(r)
+ ctr, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+
+ state, err := ctr.State()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ if !(state == define.ContainerStateCreated || state == define.ContainerStateRunning) {
+ utils.InternalServerError(w, errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers"))
+ return
+ }
+
+ // Hijack the connection
+ hijacker, ok := w.(http.Hijacker)
+ if !ok {
+ utils.InternalServerError(w, errors.Errorf("unable to hijack connection"))
+ return
+ }
+
+ w.WriteHeader(http.StatusSwitchingProtocols)
+
+ connection, buffer, err := hijacker.Hijack()
+ if err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "error hijacking connection"))
+ return
+ }
+
+ logrus.Debugf("Hijack for attach of container %s successful", ctr.ID())
+
+ // Perform HTTP attach.
+ // HTTPAttach will handle everything about the connection from here on
+ // (including closing it and writing errors to it).
+ if err := ctr.HTTPAttach(connection, buffer, streams, detachKeys, nil); err != nil {
+ // We can't really do anything about errors anymore. HTTPAttach
+ // should be writing them to the connection.
+ logrus.Errorf("Error attaching to container %s: %v", ctr.ID(), err)
+ }
+
+ logrus.Debugf("Attach for container %s completed successfully", ctr.ID())
+}
+
+func ResizeContainer(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+
+ query := struct {
+ Height uint16 `schema:"h"`
+ Width uint16 `schema:"w"`
+ }{}
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ // This is not a 400, despite the fact that is should be, for
+ // compatibility reasons.
+ utils.InternalServerError(w, errors.Wrapf(err, "error parsing query options"))
+ return
+ }
+
+ name := getName(r)
+ ctr, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+
+ newSize := remotecommand.TerminalSize{
+ Width: query.Width,
+ Height: query.Height,
+ }
+ if err := ctr.AttachResize(newSize); err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ // This is not a 204, even though we write nothing, for compatibility
+ // reasons.
+ utils.WriteResponse(w, http.StatusOK, "")
+}
diff --git a/pkg/api/handlers/images.go b/pkg/api/handlers/images.go
index d4cddbfb2..b4acdc312 100644
--- a/pkg/api/handlers/images.go
+++ b/pkg/api/handlers/images.go
@@ -74,8 +74,25 @@ func TagImage(w http.ResponseWriter, r *http.Request) {
}
func RemoveImage(w http.ResponseWriter, r *http.Request) {
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ query := struct {
+ noPrune bool
+ }{
+ // This is where you can override the golang default value for one of fields
+ }
+
+ 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
+ }
+ muxVars := mux.Vars(r)
+ if _, found := muxVars["noprune"]; found {
+ if query.noPrune {
+ utils.UnSupportedParameter("noprune")
+ }
+ }
name := mux.Vars(r)["name"]
newImage, err := runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index 388be24b6..e16a4ea1f 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -143,6 +143,22 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
}
+func UnmountContainer(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := mux.Vars(r)["name"]
+ conn, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ // TODO In future it might be an improvement that libpod unmount return a
+ // "container not mounted" error so we can surface that to the endpoint user
+ if err := conn.Unmount(false); err != nil {
+ utils.InternalServerError(w, err)
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+
+}
func MountContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := mux.Vars(r)["name"]
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index daaf9d018..14f8e8de7 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -12,6 +12,7 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/util"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -384,18 +385,27 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
decoder = r.Context().Value("decoder").(*schema.Decoder)
+ signal = "SIGKILL"
)
query := struct {
- signal int `schema:"signal"`
+ signal string `schema:"signal"`
}{
// 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
}
+ muxVars := mux.Vars(r)
+ if _, found := muxVars["signal"]; found {
+ signal = query.signal
+ }
+
+ sig, err := util.ParseSignal(signal)
+ if err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "unable to parse signal value"))
+ }
name := mux.Vars(r)["name"]
pod, err := runtime.LookupPod(name)
if err != nil {
@@ -419,8 +429,7 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
utils.Error(w, msg, http.StatusConflict, errors.Errorf("cannot kill a pod with no running containers: %s", pod.ID()))
return
}
- // TODO How do we differentiate if a signal was sent vs accepting the pod/container default?
- _, err = pod.Kill(uint(query.signal))
+ _, err = pod.Kill(uint(sig))
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go
index c845c8195..0db4e19b6 100644
--- a/pkg/api/handlers/swagger.go
+++ b/pkg/api/handlers/swagger.go
@@ -30,9 +30,7 @@ type swagImageInspect struct {
// swagger:response DocsImageDeleteResponse
type swagImageDeleteResponse struct {
// in:body
- Body struct {
- image.ImageDeleteResponse
- }
+ Body []image.ImageDeleteResponse
}
// Search results
diff --git a/pkg/api/handlers/utils/errors.go b/pkg/api/handlers/utils/errors.go
index 3ec0742bd..b6f125c58 100644
--- a/pkg/api/handlers/utils/errors.go
+++ b/pkg/api/handlers/utils/errors.go
@@ -86,3 +86,8 @@ func (e ErrorModel) Error() string {
func (e ErrorModel) Cause() error {
return errors.New(e.Because)
}
+
+// UnsupportedParameter logs a given param by its string name as not supported.
+func UnSupportedParameter(param string) {
+ log.Infof("API parameter %q: not supported", param)
+}
diff --git a/pkg/api/server/listener_api.go b/pkg/api/server/listener_api.go
new file mode 100644
index 000000000..4984216b8
--- /dev/null
+++ b/pkg/api/server/listener_api.go
@@ -0,0 +1,31 @@
+package server
+
+import (
+ "net"
+ "os"
+ "path/filepath"
+
+ "github.com/pkg/errors"
+)
+
+// ListenUnix follows stdlib net.Listen() API, providing a unix listener for given path
+// ListenUnix will delete and create files/directories as needed
+func ListenUnix(network string, path string) (net.Listener, error) {
+ // setup custom listener for API server
+ err := os.MkdirAll(filepath.Dir(path), 0770)
+ if err != nil {
+ return nil, errors.Wrapf(err, "api.ListenUnix() failed to create %s", filepath.Dir(path))
+ }
+ os.Remove(path)
+
+ listener, err := net.Listen(network, path)
+ if err != nil {
+ return nil, errors.Wrapf(err, "api.ListenUnix() failed to create net.Listen(%s, %s)", network, path)
+ }
+
+ _, err = os.Stat(path)
+ if err != nil {
+ return nil, errors.Wrapf(err, "net.Listen(%s, %s) failed to report the failure to create socket", network, path)
+ }
+ return listener, nil
+}
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index b275fa4d1..833bb5197 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -428,6 +428,91 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
// '500':
// "$ref": "#/responses/InternalError"
r.HandleFunc(VersionedPath("/containers/{name:..*}/wait"), APIHandler(s.Context, generic.WaitContainer)).Methods(http.MethodPost)
+ // swagger:operation POST /containers/{nameOrID}/attach compat attach
+ // ---
+ // tags:
+ // - containers (compat)
+ // summary: Attach to a container
+ // description: Hijacks the connection to forward the container's standard streams to the client.
+ // parameters:
+ // - in: path
+ // name: nameOrID
+ // required: true
+ // description: the name or ID of the container
+ // - in: query
+ // name: detachKeys
+ // required: false
+ // type: string
+ // description: keys to use for detaching from the container
+ // - in: query
+ // name: logs
+ // required: false
+ // type: bool
+ // description: Not yet supported
+ // - in: query
+ // name: stream
+ // required: false
+ // type: bool
+ // default: true
+ // description: If passed, must be set to true; stream=false is not yet supported
+ // - in: query
+ // name: stdout
+ // required: false
+ // type: bool
+ // description: Attach to container STDOUT
+ // - in: query
+ // name: stderr
+ // required: false
+ // type: bool
+ // description: Attach to container STDERR
+ // - in: query
+ // name: stdin
+ // required: false
+ // type: bool
+ // description: Attach to container STDIN
+ // produces:
+ // - application/json
+ // responses:
+ // '101':
+ // description: No error, connection has been hijacked for transporting streams.
+ // '400':
+ // "$ref": "#/responses/BadParamError"
+ // '404':
+ // "$ref": "#/responses/NoSuchContainer"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/containers/{name:..*}/attach"), APIHandler(s.Context, handlers.AttachContainer)).Methods(http.MethodPost)
+ // swagger:operation POST /containers/{nameOrID}/resize compat resize
+ // ---
+ // tags:
+ // - containers (compat)
+ // summary: Resize a container's TTY
+ // description: Resize the terminal attached to a container (for use with Attach).
+ // parameters:
+ // - in: path
+ // name: nameOrID
+ // required: true
+ // description: the name or ID of the container
+ // - in: query
+ // name: h
+ // type: int
+ // required: false
+ // description: Height to set for the terminal, in characters
+ // - in: query
+ // name: w
+ // type: int
+ // required: false
+ // description: Width to set for the terminal, in characters
+ // produces:
+ // - application/json
+ // responses:
+ // '200':
+ // description: no error
+ // '404':
+ // "$ref": "#/responses/NoSuchContainer"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/containers/{name:..*}/resize"), APIHandler(s.Context, handlers.ResizeContainer)).Methods(http.MethodPost)
/*
libpod endpoints
@@ -580,7 +665,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
// '500':
// "$ref": "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/kill"), APIHandler(s.Context, libpod.KillContainer)).Methods(http.MethodGet)
- // swagger:operation GET /libpod/containers/{nameOrID}/mount libpod mountContainer
+ // swagger:operation POST /libpod/containers/{nameOrID}/mount libpod mountContainer
// ---
// tags:
// - containers
@@ -599,12 +684,33 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
// schema:
// description: id
// type: string
- // example: 3c784de79b791b4ebd3ac55e511f97fedc042328499554937a3f8bfd9c1a2cb8
+ // example: /var/lib/containers/storage/overlay/f3f693bd88872a1e3193f4ebb925f4c282e8e73aadb8ab3e7492754dda3a02a4/merged
// '404':
// "$ref": "#/responses/NoSuchContainer"
// '500':
// "$ref": "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/mount"), APIHandler(s.Context, libpod.MountContainer)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/containers/{nameOrID}/unmount libpod unmountContainer
+ // ---
+ // tags:
+ // - containers
+ // summary: Unmount a container
+ // description: Unmount a container from the filesystem
+ // parameters:
+ // - in: path
+ // name: nameOrID
+ // required: true
+ // description: the name or ID of the container
+ // produces:
+ // - application/json
+ // responses:
+ // '204':
+ // description: no error
+ // '404':
+ // "$ref": "#/responses/NoSuchContainer"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/unmount"), APIHandler(s.Context, libpod.UnmountContainer)).Methods(http.MethodPost)
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/logs"), APIHandler(s.Context, libpod.LogsFromContainer)).Methods(http.MethodGet)
// swagger:operation POST /libpod/containers/{nameOrID}/pause libpod libpodPauseContainer
// ---
@@ -703,11 +809,13 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
// '500':
// "$ref": "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/stats"), APIHandler(s.Context, generic.StatsContainer)).Methods(http.MethodGet)
- // swagger:operation GET /libpod/containers/{nameOrID}/top containers topContainer
+ // swagger:operation GET /libpod/containers/{nameOrID}/top libpod libpodTopContainer
//
// List processes running inside a container. Note
//
// ---
+ // tags:
+ // - containers
// parameters:
// - in: path
// name: nameOrID
@@ -725,7 +833,6 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
// - application/json
// responses:
// '200':
- // description: no error
// "ref": "#/responses/DockerTopResponse"
// '404':
// "$ref": "#/responses/NoSuchContainer"
@@ -823,5 +930,90 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
// '500':
// "$ref": "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/stop"), APIHandler(s.Context, handlers.StopContainer)).Methods(http.MethodPost)
+ // swagger:operation POST /libpod/containers/{nameOrID}/attach libpod attach
+ // ---
+ // tags:
+ // - containers
+ // summary: Attach to a container
+ // description: Hijacks the connection to forward the container's standard streams to the client.
+ // parameters:
+ // - in: path
+ // name: nameOrID
+ // required: true
+ // description: the name or ID of the container
+ // - in: query
+ // name: detachKeys
+ // required: false
+ // type: string
+ // description: keys to use for detaching from the container
+ // - in: query
+ // name: logs
+ // required: false
+ // type: bool
+ // description: Not yet supported
+ // - in: query
+ // name: stream
+ // required: false
+ // type: bool
+ // default: true
+ // description: If passed, must be set to true; stream=false is not yet supported
+ // - in: query
+ // name: stdout
+ // required: false
+ // type: bool
+ // description: Attach to container STDOUT
+ // - in: query
+ // name: stderr
+ // required: false
+ // type: bool
+ // description: Attach to container STDERR
+ // - in: query
+ // name: stdin
+ // required: false
+ // type: bool
+ // description: Attach to container STDIN
+ // produces:
+ // - application/json
+ // responses:
+ // '101':
+ // description: No error, connection has been hijacked for transporting streams.
+ // '400':
+ // "$ref": "#/responses/BadParamError"
+ // '404':
+ // "$ref": "#/responses/NoSuchContainer"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/attach"), APIHandler(s.Context, handlers.AttachContainer)).Methods(http.MethodPost)
+ // swagger:operation POST /libpod/containers/{nameOrID}/resize libpod resize
+ // ---
+ // tags:
+ // - containers
+ // summary: Resize a container's TTY
+ // description: Resize the terminal attached to a container (for use with Attach).
+ // parameters:
+ // - in: path
+ // name: nameOrID
+ // required: true
+ // description: the name or ID of the container
+ // - in: query
+ // name: h
+ // type: int
+ // required: false
+ // description: Height to set for the terminal, in characters
+ // - in: query
+ // name: w
+ // type: int
+ // required: false
+ // description: Width to set for the terminal, in characters
+ // produces:
+ // - application/json
+ // responses:
+ // '200':
+ // description: no error
+ // '404':
+ // "$ref": "#/responses/NoSuchContainer"
+ // '500':
+ // "$ref": "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/resize"), APIHandler(s.Context, handlers.ResizeContainer)).Methods(http.MethodPost)
return nil
}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index cd42afe71..7f1bb4e5c 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -193,8 +193,8 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// responses:
// '200':
// $ref: "#/responses/DocsImageDeleteResponse"
- // '400':
- // $ref: '#/responses/BadParamError'
+ // '404':
+ // $ref: '#/responses/NoSuchImage'
// '409':
// $ref: '#/responses/ConflictError'
// '500':
@@ -506,11 +506,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - application/json
// responses:
// '200':
- // schema:
- // items:
- // $ref: "#/responses/DocsIageDeleteResponse"
- // '400':
- // $ref: "#/responses/BadParamError"
+ // $ref: "#/responses/DocsImageDeleteResponse"
// '404':
// $ref: '#/responses/NoSuchImage'
// '409':
@@ -533,10 +529,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// name: format
// type: string
// description: format for exported image
+ // default: oci-archive
// - in: query
// name: compress
// type: bool
// description: use compression on image
+ // default: false
// produces:
// - application/json
// responses:
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index 5069326b6..4018cfbe8 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -121,8 +121,9 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// description: the name or ID of the pod
// - in: query
// name: signal
- // type: int
+ // type: string
// description: signal to be sent to pod
+ // default: SIGKILL
// responses:
// '204':
// description: no error
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index 2bda5ad01..f3bae0345 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -1,6 +1,8 @@
-// Package serviceapi Provides a Container compatible interface.
+// Package serviceapi Provides a Container compatible interface (EXPERIMENTAL)
//
-// This documentation describes the HTTP LibPod interface
+// This documentation describes the HTTP LibPod interface. It is to be consider
+// only as experimental as this point. The endpoints, parameters, inputs, and
+// return values can all change.
//
// Schemes: http, https
// Host: podman.io
@@ -8,6 +10,10 @@
// Version: 0.0.1
// License: Apache-2.0 https://opensource.org/licenses/Apache-2.0
// Contact: Podman <podman@lists.podman.io> https://podman.io/community/
+// InfoExtensions:
+// x-logo:
+// - url: https://raw.githubusercontent.com/containers/libpod/master/logo/podman-logo.png
+// - altText: "Podman logo"
//
// Consumes:
// - application/json
@@ -48,9 +54,9 @@ import (
)
type APIServer struct {
- http.Server // Where the HTTP work happens
+ http.Server // The HTTP work happens here
*schema.Decoder // Decoder for Query parameters to structs
- context.Context // Context for graceful server shutdown
+ context.Context // Context to carry objects to handlers
*libpod.Runtime // Where the real work happens
net.Listener // mux for routing HTTP API calls to libpod routines
context.CancelFunc // Stop APIServer
@@ -58,14 +64,37 @@ type APIServer struct {
time.Duration // Duration of client access sliding window
}
-// NewServer will create and configure a new API HTTP server
+// Number of seconds to wait for next request, if exceeded shutdown server
+const (
+ DefaultServiceDuration = 300 * time.Second
+ UnlimitedServiceDuration = 0 * time.Second
+)
+
+// NewServer will create and configure a new API server with all defaults
func NewServer(runtime *libpod.Runtime) (*APIServer, error) {
- listeners, err := activation.Listeners()
- if err != nil {
- return nil, errors.Wrap(err, "Cannot retrieve file descriptors from systemd")
- }
- if len(listeners) != 1 {
- return nil, errors.Errorf("Wrong number of file descriptors from systemd for socket activation (%d != 1)", len(listeners))
+ return newServer(runtime, DefaultServiceDuration, nil)
+}
+
+// NewServerWithSettings will create and configure a new API server using provided settings
+func NewServerWithSettings(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) {
+ return newServer(runtime, duration, listener)
+}
+
+func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) {
+ // If listener not provided try socket activation protocol
+ if listener == nil {
+ if _, found := os.LookupEnv("LISTEN_FDS"); !found {
+ return nil, errors.Errorf("Cannot create Server, no listener provided and socket activation protocol is not active.")
+ }
+
+ listeners, err := activation.Listeners()
+ if err != nil {
+ return nil, errors.Wrap(err, "Cannot retrieve file descriptors from systemd")
+ }
+ if len(listeners) != 1 {
+ return nil, errors.Errorf("Wrong number of file descriptors for socket activation protocol (%d != 1)", len(listeners))
+ }
+ listener = &listeners[0]
}
router := mux.NewRouter()
@@ -80,9 +109,9 @@ func NewServer(runtime *libpod.Runtime) (*APIServer, error) {
Decoder: schema.NewDecoder(),
Context: nil,
Runtime: runtime,
- Listener: listeners[0],
+ Listener: *listener,
CancelFunc: nil,
- Duration: 300 * time.Second,
+ Duration: duration,
}
server.Timer = time.AfterFunc(server.Duration, func() {
if err := server.Shutdown(); err != nil {
@@ -176,6 +205,11 @@ func (s *APIServer) Serve() error {
// Shutdown is a clean shutdown waiting on existing clients
func (s *APIServer) Shutdown() error {
+ // Duration == 0 flags no auto-shutdown of server
+ if s.Duration == 0 {
+ return nil
+ }
+
// We're still in the sliding service window
if s.Timer.Stop() {
s.Timer.Reset(s.Duration)