Initial commit on compatible API

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Create service command

Use cd cmd/service && go build .

$ systemd-socket-activate -l 8081 cmd/service/service &
$ curl http://localhost:8081/v1.24/images/json

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Correct Makefile

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Two more stragglers

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Report errors back as http headers

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Split out handlers, updated output

Output aligned to docker structures

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Refactored routing, added more endpoints and types

* Encapsulated all the routing information in the handler_* files.
* Added more serviceapi/types, including podman additions. See Info

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Cleaned up code, implemented info content

* Move Content-Type check into serviceHandler
* Custom 404 handler showing the url, mostly for debugging
* Refactored images: better method names and explicit http codes
* Added content to /info
* Added podman fields to Info struct
* Added Container struct

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Add a bunch of endpoints

containers: stop, pause, unpause, wait, rm
images: tag, rmi, create (pull only)

Signed-off-by: baude <bbaude@redhat.com>

Add even more handlers

* Add serviceapi/Error() to improve error handling
* Better support for API return payloads
* Renamed unimplemented to unsupported these are generic endpoints
  we don't intend to ever support.  Swarm broken out since it uses
  different HTTP codes to signal that the node is not in a swarm.
* Added more types
* API Version broken out so it can be validated in the future

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Refactor to introduce ServiceWriter

Signed-off-by: Jhon Honce <jhonce@redhat.com>

populate pods endpoints

/libpod/pods/..

exists, kill, pause, prune, restart, remove, start, stop, unpause

Signed-off-by: baude <bbaude@redhat.com>

Add components to Version, fix Error body

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Add images pull output, fix swarm routes

* docker-py tests/integration/api_client_test.py pass 100%
* docker-py tests/integration/api_image_test.py pass 4/16
+ Test failures include services podman does not support

Signed-off-by: Jhon Honce <jhonce@redhat.com>

pods endpoint submission 2

add create and others; only top and stats is left.

Signed-off-by: baude <bbaude@redhat.com>

Update pull image to work from empty registry

Signed-off-by: Jhon Honce <jhonce@redhat.com>

pod create and container create

first pass at pod and container create.  the container create does not
quite work yet but it is very close.  pod create needs a partial
rewrite.  also broken off the DELETE (rm/rmi) to specific handler funcs.

Signed-off-by: baude <bbaude@redhat.com>

Add docker-py demos, GET .../containers/json

* Update serviceapi/types to reflect libpod not podman
* Refactored removeImage() to provide non-streaming return

Signed-off-by: Jhon Honce <jhonce@redhat.com>

create container part2

finished minimal config needed for create container.  started demo.py
for upcoming talk

Signed-off-by: baude <bbaude@redhat.com>

Stop server after honoring request

* Remove casting for method calls
* Improve WriteResponse()
* Update Container API type to match docker API

Signed-off-by: Jhon Honce <jhonce@redhat.com>

fix namespace assumptions

cleaned up namespace issues with libpod.

Signed-off-by: baude <bbaude@redhat.com>

wip

Signed-off-by: baude <bbaude@redhat.com>

Add sliding window when shutting down server

* Added a Timeout rather than closing down service on each call
* Added gorilla/schema dependency for Decode'ing query parameters
* Improved error handling
* Container logs returned and multiplexed for stdout and stderr
  * .../containers/{name}/logs?stdout=True&stderr=True
* Container stats
  * .../containers/{name}/stats

Signed-off-by: Jhon Honce <jhonce@redhat.com>

Improve error handling

* Add check for at least one std stream required for /containers/{id}/logs
* Add check for state in /containers/{id}/top
* Fill in more fields for /info
* Fixed error checking in service start code

Signed-off-by: Jhon Honce <jhonce@redhat.com>

get rest  of image tests for pass

Signed-off-by: baude <bbaude@redhat.com>

linting our content

Signed-off-by: baude <bbaude@redhat.com>

more linting

Signed-off-by: baude <bbaude@redhat.com>

more linting

Signed-off-by: baude <bbaude@redhat.com>

pruning

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]apiv2 pods

migrate from using args in the url to using a json struct in body for
pod create.

Signed-off-by: baude <bbaude@redhat.com>

fix handler_images prune

prune's api changed slightly to deal with filters.

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]enabled base container create tests

enabling the base container create tests which allow us to get more into
the stop, kill, etc tests. many new tests now pass.

Signed-off-by: baude <bbaude@redhat.com>

serviceapi errors: append error message to API message

I dearly hope this is not breaking any other tests but debugging
"Internal Server Error" is not helpful to any user.  In case, it
breaks tests, we can rever the commit - that's why it's a small one.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>

serviceAPI: add containers/prune endpoint

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>

add `service` make target

Also remove the non-functional sub-Makefile.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>

add make targets for testing the service

 * `sudo make run-service` for running the service.

 * `DOCKERPY_TEST="tests/integration/api_container_test.py::ListContainersTest" \
 	make run-docker-py-tests`
   for running a specific tests.  Run all tests by leaving the env
   variable empty.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>

Split handlers and server packages

The files were split to help contain bloat. The api/server package will
contain all code related to the functioning of the server while
api/handlers will have all the code related to implementing the end
points.

api/server/register_* will contain the methods for registering
endpoints.  Additionally, they will have the comments for generating the
swagger spec file.

See api/handlers/version.go for a small example handler,
api/handlers/containers.go contains much more complex handlers.

Signed-off-by: Jhon Honce <jhonce@redhat.com>

[CI:DOCS]enabled more tests

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]libpod endpoints

small refactor for libpod inclusion and began adding endpoints.

Signed-off-by: baude <bbaude@redhat.com>

Implement /build and /events

* Include crypto libraries for future ssh work

Signed-off-by: Jhon Honce <jhonce@redhat.com>

[CI:DOCS]more image implementations

convert from using for to query structs among other changes including
new endpoints.

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]add bindings for golang

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]add volume endpoints for libpod

create, inspect, ls, prune, and rm

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]apiv2 healthcheck enablement

wire up container healthchecks for the api.

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]Add mount endpoints

via the api, allow ability to mount a container and list container
mounts.

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]Add search endpoint

add search endpoint with golang bindings

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]more apiv2 development

misc population of methods, etc

Signed-off-by: baude <bbaude@redhat.com>

rebase cleanup and epoch reset

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]add more network endpoints

also, add some initial error handling and convenience functions for
standard endpoints.

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]use helper funcs for bindings

use the methods developed to make writing bindings less duplicative and
easier to use.

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]add return info for prereview

begin to add return info and status codes for errors so that we can
review the apiv2

Signed-off-by: baude <bbaude@redhat.com>

[CI:DOCS]first pass at adding swagger docs for api

Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
Jhon Honce
2019-11-01 13:03:34 -07:00
committed by baude
parent 6ed88e0475
commit d924494f56
100 changed files with 9008 additions and 41 deletions

View File

@ -0,0 +1,37 @@
package server
import (
"context"
"net/http"
log "github.com/sirupsen/logrus"
)
// APIHandler is a wrapper to enhance HandlerFunc's and remove redundant code
func APIHandler(ctx context.Context, h http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Debugf("APIHandler -- Method: %s URL: %s", r.Method, r.URL.String())
if err := r.ParseForm(); err != nil {
log.Infof("Failed Request: unable to parse form: %q", err)
}
// TODO: Use ConnContext when ported to go 1.13
c := context.WithValue(r.Context(), "decoder", ctx.Value("decoder"))
c = context.WithValue(c, "runtime", ctx.Value("runtime"))
c = context.WithValue(c, "shutdownFunc", ctx.Value("shutdownFunc"))
r = r.WithContext(c)
h(w, r)
shutdownFunc := r.Context().Value("shutdownFunc").(func() error)
if err := shutdownFunc(); err != nil {
log.Errorf("Failed to shutdown Server in APIHandler(): %s", err.Error())
}
})
}
// VersionedPath prepends the version parsing code
// any handler may override this default when registering URL(s)
func VersionedPath(p string) string {
return "/v{version:[0-9][0-9.]*}" + p
}

View File

@ -0,0 +1,11 @@
package server
import (
"github.com/containers/libpod/pkg/api/handlers"
"github.com/gorilla/mux"
)
func (s *APIServer) RegisterAuthHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/auth"), APIHandler(s.Context, handlers.UnsupportedHandler))
return nil
}

View File

@ -0,0 +1,889 @@
package server
import (
"net/http"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/generic"
"github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/gorilla/mux"
)
func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
// swagger:operation POST /containers/create containers createContainer
//
// Create a container
//
// ---
// produces:
// - application/json
// parameters:
// - in: query
// name: name
// type: string
// description: container name
// responses:
// '201':
// schema:
// items:
// "$ref": "#/ctrCreateResponse"
// '400':
// description: bad parameter
// schema:
// "$ref": "#/types/ErrorModel"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '409':
// description: conflict
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/create"), APIHandler(s.Context, generic.CreateContainer)).Methods(http.MethodPost)
// swagger:operation GET /containers/json containers listContainers
//
// List containers
//
// ---
// produces:
// - application/json
// responses:
// '200':
// schema:
// type: array
// items:
// "$ref": "#/types/Container"
// '400':
// description: bad parameter
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/json"), APIHandler(s.Context, generic.ListContainers)).Methods(http.MethodGet)
// swagger:operation POST /containers/prune containers pruneContainers
//
// Prune unused containers
//
// ---
// parameters:
// - in: query
// name: filters
// type: map[string][]string
// description: something
// produces:
// - application/json
// responses:
// '200':
// schema:
// "$ref": "#/types/ContainerPruneReport"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/prune"), APIHandler(s.Context, generic.PruneContainers)).Methods(http.MethodPost)
// swagger:operation DELETE /containers/{nameOrID} containers removeContainer
//
// Delete container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: force
// type: bool
// description: need something
// - in: query
// name: v
// type: bool
// description: need something
// - in: query
// name: link
// type: bool
// description: not supported
// produces:
// - application/json
// responses:
// '200':
// schema:
// type: array
// items:
// "$ref": "#/types/Container"
// '400':
// description: bad parameter
// schema:
// "$ref": "#/types/ErrorModel"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '409':
// description: conflict
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}"), APIHandler(s.Context, generic.RemoveContainer)).Methods(http.MethodDelete)
// swagger:operation GET /containers/{nameOrID}/json containers getContainer
//
// Inspect Container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/types/ContainerJSON"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/json"), APIHandler(s.Context, generic.GetContainer)).Methods(http.MethodGet)
// swagger:operation POST /containers/{nameOrID}/kill containers killContainer
//
// Kill Container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: signal
// type: int
// description: signal to be sent to container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '409':
// description: conflict
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/kill"), APIHandler(s.Context, generic.KillContainer)).Methods(http.MethodPost)
// swagger:operation GET /containers/{nameOrID}/logs containers LogsFromContainer
//
// Get logs from container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: follow
// type: bool
// description: needs description
// - in: query
// name: stdout
// type: bool
// description: needs description
// - in: query
// name: stderr
// type: bool
// description: needs description
// - in: query
// name: since
// type: string
// description: needs description
// - in: query
// name: until
// type: string
// description: needs description
// - in: query
// name: timestamps
// type: bool
// description: needs description
// - in: query
// name: tail
// type: string
// description: needs description
// produces:
// - application/json
// responses:
// '200':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/logs"), APIHandler(s.Context, generic.LogsFromContainer)).Methods(http.MethodGet)
// swagger:operation POST /containers/{nameOrID}/pause containers pauseContainer
//
// Pause Container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/pause"), APIHandler(s.Context, handlers.PauseContainer)).Methods(http.MethodPost)
r.HandleFunc(VersionedPath("/containers/{name:..*}/rename"), APIHandler(s.Context, handlers.UnsupportedHandler)).Methods(http.MethodPost)
// swagger:operation POST /containers/{nameOrID}/restart containers restartContainer
//
// Restart Container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: t
// type: int
// description: timeout before sending kill signal to container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/restart"), APIHandler(s.Context, handlers.RestartContainer)).Methods(http.MethodPost)
// swagger:operation POST /containers/{nameOrID}/start containers startContainer
//
// Start a container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: detachKeys
// type: string
// description: needs description
// produces:
// - application/json
// responses:
// '204':
// description: no error
// '304':
// description: container already started
// schema:
// "$ref": "#/types/ErrorModel"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/start"), APIHandler(s.Context, handlers.StartContainer)).Methods(http.MethodPost)
// swagger:operation GET /containers/{nameOrID}/stats containers statsContainer
//
// Get stats for a contrainer
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: stream
// type: bool
// description: needs description
// produces:
// - application/json
// responses:
// '200':
// description: no error
// schema:
// "ref": "#/handler/stats"
// '304':
// description: container already started
// schema:
// "$ref": "#/types/ErrorModel"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/stats"), APIHandler(s.Context, generic.StatsContainer)).Methods(http.MethodGet)
// swagger:operation POST /containers/{nameOrID}/stop containers stopContainer
//
// Stop a container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: t
// type: int
// description: number of seconds to wait before killing container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// '304':
// description: container already stopped
// schema:
// "$ref": "#/types/ErrorModel"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/stop"), APIHandler(s.Context, handlers.StopContainer)).Methods(http.MethodPost)
// swagger:operation GET /containers/{nameOrID}/top containers topContainer
//
// List processes running inside a container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: ps_args
// type: string
// description: arguments to pass to ps such as aux
// produces:
// - application/json
// responses:
// '200':
// description: no error
// schema:
// "ref": "#/types/ContainerTopBody"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/top"), APIHandler(s.Context, handlers.TopContainer)).Methods(http.MethodGet)
// swagger:operation POST /containers/{nameOrID}/unpause containers unpauseContainer
//
// Unpause Container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/unpause"), APIHandler(s.Context, handlers.UnpauseContainer)).Methods(http.MethodPost)
// swagger:operation POST /containers/{nameOrID}/wait containers waitContainer
//
// Wait on a container to exit
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: condition
// type: string
// description: Wait until the container reaches the given condition
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/containers/{name:..*}/wait"), APIHandler(s.Context, generic.WaitContainer)).Methods(http.MethodPost)
/*
libpod endpoints
*/
r.HandleFunc(VersionedPath("/libpod/containers/create"), APIHandler(s.Context, libpod.CreateContainer)).Methods(http.MethodPost)
// swagger:operation GET /libpod/containers/json containers listContainers
//
// List containers
//
// ---
// produces:
// - application/json
// responses:
// '200':
// schema:
// type: array
// items:
// "$ref": "#/shared/GetPsContainerOutput"
// '400':
// description: bad parameter
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/json"), APIHandler(s.Context, libpod.ListContainers)).Methods(http.MethodGet)
// swagger:operation POST /libpod/containers/prune containers pruneContainers
//
// Prune unused containers
//
// ---
// parameters:
// - in: query
// name: force
// type: bool
// description: something
// - in: query
// name: filters
// type: map[string][]string
// description: something
// produces:
// - application/json
// responses:
// '200':
// schema:
// "$ref": "#/types/ContainerPruneReport"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/prune"), APIHandler(s.Context, libpod.PruneContainers)).Methods(http.MethodPost)
// swagger:operation GET /libpod/containers/showmounted containers showMounterContainers
//
// Show mounted containers
//
// ---
// produces:
// - application/json
// responses:
// '200':
// schema:
// "$ref": "TBD"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/showmounted"), APIHandler(s.Context, libpod.ShowMountedContainers)).Methods(http.MethodGet)
// swagger:operation DELETE /libpod/containers/json containers removeContainer
//
// Delete container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: force
// type: bool
// description: need something
// - in: query
// name: v
// type: bool
// description: need something
// produces:
// - application/json
// responses:
// '200':
// schema:
// type: array
// items:
// "$ref": "#/types/Container"
// '400':
// description: bad parameter
// schema:
// "$ref": "#/types/ErrorModel"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '409':
// description: conflict
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}"), APIHandler(s.Context, libpod.RemoveContainer)).Methods(http.MethodDelete)
// swagger:operation GET /libpod/containers/{nameOrID}/json containers getContainer
//
// Inspect Container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: size
// type: bool
// description: display filesystem usage
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#InspectContainerData"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/json"), APIHandler(s.Context, libpod.GetContainer)).Methods(http.MethodGet)
// swagger:operation POST /libpod/containers/{nameOrID}/kill containers killContainer
//
// Kill Container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: signal
// type: int
// default: 15
// description: signal to be sent to container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '409':
// description: conflict
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/kill"), APIHandler(s.Context, libpod.KillContainer)).Methods(http.MethodGet)
// swagger:operation GET /libpod/containers/{nameOrID}/mount containers mountContainer
//
// Mount a container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "string"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/mount"), APIHandler(s.Context, libpod.LogsFromContainer)).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 containers pauseContainer
//
// Pause Container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/pause"), APIHandler(s.Context, handlers.PauseContainer)).Methods(http.MethodPost)
// swagger:operation POST /libpod/containers/{nameOrID}/restart containers restartContainer
//
// Restart Container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: t
// type: int
// description: timeout before sending kill signal to container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/restart"), APIHandler(s.Context, handlers.RestartContainer)).Methods(http.MethodPost)
// swagger:operation POST /libpod/containers/{nameOrID}/start containers startContainer
//
// Start a container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: detachKeys
// type: string
// description: needs description
// produces:
// - application/json
// responses:
// '204':
// description: no error
// '304':
// description: container already started
// schema:
// "$ref": "#/types/ErrorModel"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/start"), APIHandler(s.Context, handlers.StartContainer)).Methods(http.MethodPost)
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/stats"), APIHandler(s.Context, libpod.StatsContainer)).Methods(http.MethodGet)
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/top"), APIHandler(s.Context, handlers.TopContainer)).Methods(http.MethodGet)
// swagger:operation POST /libpod/containers/{nameOrID}/unpause containers unpauseContainer
//
// Unpause Container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/unpause"), APIHandler(s.Context, handlers.UnpauseContainer)).Methods(http.MethodPost)
// swagger:operation POST /libpod/containers/{nameOrID}/wait containers waitContainer
//
// Wait on a container to exit
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: condition
// type: string
// description: Wait until the container reaches the given condition
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/wait"), APIHandler(s.Context, libpod.WaitContainer)).Methods(http.MethodPost)
// swagger:operation POST /libpod/containers/{nameOrID}/exists containers containerExists
//
// Check if container exists
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// schema:
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/exists"), APIHandler(s.Context, libpod.ContainerExists)).Methods(http.MethodGet)
// swagger:operation POST /libpod/containers/{nameOrID}/stop containers stopContainer
//
// Stop a container
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: t
// type: int
// description: number of seconds to wait before killing container
// produces:
// - application/json
// responses:
// '204':
// description: no error
// '304':
// description: container already stopped
// schema:
// "$ref": "#/types/ErrorModel"
// '404':
// description: no such container
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/stop"), APIHandler(s.Context, handlers.StopContainer)).Methods(http.MethodPost)
return nil
}

View File

@ -0,0 +1,11 @@
package server
import (
"github.com/containers/libpod/pkg/api/handlers"
"github.com/gorilla/mux"
)
func (s *APIServer) RegisterDistributionHandlers(r *mux.Router) error {
r.HandleFunc(VersionedPath("/distribution/{name:..*}/json"), handlers.UnsupportedHandler)
return nil
}

View File

@ -0,0 +1,32 @@
package server
import (
"github.com/containers/libpod/pkg/api/handlers"
"github.com/gorilla/mux"
)
func (s *APIServer) RegisterEventsHandlers(r *mux.Router) error {
// swagger:operation GET /events system getEvents
// ---
// summary: Returns events filtered on query parameters
// produces:
// - application/json
// parameters:
// - name: since
// in: query
// description: start streaming events from this time
// - name: until
// in: query
// description: stop streaming events later than this
// - name: filters
// in: query
// description: JSON encoded map[string][]string of constraints
// responses:
// "200":
// description: OK
// "500":
// description: Failed
// "$ref": "#/types/errorModel"
r.Handle(VersionedPath("/events"), APIHandler(s.Context, handlers.GetEvents))
return nil
}

View File

@ -0,0 +1,13 @@
package server
import (
"net/http"
"github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/gorilla/mux"
)
func (s *APIServer) registerHealthCheckHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/libpod/containers/{name:..*}/runhealthcheck"), APIHandler(s.Context, libpod.RunHealthCheck)).Methods(http.MethodGet)
return nil
}

View File

@ -0,0 +1,663 @@
package server
import (
"net/http"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/generic"
"github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/gorilla/mux"
)
func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// swagger:operation POST /images/create images createImage
//
// Create an image from an image
//
// ---
// produces:
// - application/json
// parameters:
// - in: query
// name: fromImage
// type: string
// description: needs description
// - in: query
// name: tag
// type: string
// description: needs description
// responses:
// '200':
// schema:
// items:
// "$ref": "TBD"
// '404':
// description: repo or image does not exist
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/create"), APIHandler(s.Context, generic.CreateImageFromImage)).Methods(http.MethodPost).Queries("fromImage", "{fromImage}")
// swagger:operation POST /images/create images createImage
//
// Create an image from Source
//
// ---
// produces:
// - application/json
// parameters:
// - in: query
// name: fromSrc
// type: string
// description: needs description
// - in: query
// name: changes
// type: TBD
// description: needs description
// responses:
// '200':
// schema:
// items:
// "$ref": "TBD"
// '404':
// description: repo or image does not exist
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/create"), APIHandler(s.Context, generic.CreateImageFromSrc)).Methods(http.MethodPost).Queries("fromSrc", "{fromSrc}")
// swagger:operation GET /images/json images listImages
//
// List Images
//
// ---
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/types/ImageSummary"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/json"), APIHandler(s.Context, generic.GetImages)).Methods(http.MethodGet)
// swagger:operation POST /images/load images loadImage
//
// Import image
//
// ---
// parameters:
// - in: query
// name: quiet
// type: bool
// description: not supported
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/types/ImageSummary"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/load"), APIHandler(s.Context, handlers.LoadImage)).Methods(http.MethodPost)
// swagger:operation POST /images/prune images pruneImages
//
// Prune unused images
//
// ---
// parameters:
// - in: query
// name: filters
// type: map[string][]string
// description: not supported
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/ImageDeleteResponse"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/prune"), APIHandler(s.Context, generic.PruneImages)).Methods(http.MethodPost)
// swagger:operation GET /images/search images searchImages
//
// Search images
//
// ---
// parameters:
// - in: query
// name: term
// type: string
// description: term to search
// - in: query
// name: limit
// type: int
// description: maximum number of results
// - in: query
// name: filters
// type: map[string][]string
// description: TBD
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/images.SearchResult"
// description: no error
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/search"), APIHandler(s.Context, handlers.SearchImages)).Methods(http.MethodGet)
// swagger:operation DELETE /images/{nameOrID} images removeImage
//
// Remove Image
//
// ---
// parameters:
// - in: query
// name: force
// type: bool
// description: remove the image even if used by containers or has other tags
// - in: query
// name: noprune
// type: bool
// description: not supported
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "TBD"
// description: no error
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '409':
// description: conflict
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/{name:..*}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
// swagger:operation GET /images/{nameOrID}/get images exportImage
//
// Export an image
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "TBD"
// description: no error
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/{name:..*}/get"), APIHandler(s.Context, generic.ExportImage)).Methods(http.MethodGet)
// swagger:operation GET /images/{nameOrID}/history images imageHistory
//
// History of an image
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/types/HistoryResponse"
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/{name:..*}/history"), APIHandler(s.Context, handlers.HistoryImage)).Methods(http.MethodGet)
// swagger:operation GET /images/{nameOrID}/json images inspectImage
//
// Inspect an image
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/types/imageInspect"
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/{name:..*}/json"), APIHandler(s.Context, generic.GetImage))
// swagger:operation POST /images/{nameOrID}/tag images tagImage
//
// Tag an image
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: repo
// type: string
// description: the repository to tag in
// - in: query
// name: tag
// type: string
// description: the name of the new tag
// produces:
// - application/json
// responses:
// '201':
// description: no error
// '400':
// description: bad parameter
// schema:
// "$ref": "#/types/ErrorModel"
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '409':
// description: conflict
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/images/{name:..*}/tag"), APIHandler(s.Context, handlers.TagImage)).Methods(http.MethodPost)
// swagger:operation POST /commit/ commit commitContainer
//
// Create a new image from a container
//
// ---
// parameters:
// - in: query
// name: container
// type: string
// description: the name or ID of a container
// - in: query
// name: repo
// type: string
// description: the repository name for the created image
// - in: query
// name: tag
// type: string
// description: tag name for the created image
// - in: query
// name: comment
// type: string
// description: commit message
// - in: query
// name: author
// type: string
// description: author of the image
// - in: query
// name: pause
// type: bool
// description: pause the container before committing it
// - in: query
// name: changes
// type: string
// description: instructions to apply while committing in Dockerfile format
// produces:
// - application/json
// responses:
// '201':
// description: no error
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/commit"), APIHandler(s.Context, generic.CommitContainer)).Methods(http.MethodPost)
/*
libpod endpoints
*/
// swagger:operation POST /libpod/images/{nameOrID}/exists images imageExists
//
// Check if image exists in local store
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// parameters:
// - in: query
// name: fromImage
// type: string
// description: needs description
// - in: query
// name: tag
// type: string
// description: needs description
// responses:
// '204':
// description: image exists
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/libpod/images/{name:..*}/exists"), APIHandler(s.Context, libpod.ImageExists))
r.Handle(VersionedPath("/libpod/images/{name:..*}/tree"), APIHandler(s.Context, libpod.ImageTree))
// swagger:operation GET /libpod/images/{nameOrID}/history images imageHistory
//
// History of an image
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/types/HistoryResponse"
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/libpod/images/history"), APIHandler(s.Context, handlers.HistoryImage)).Methods(http.MethodGet)
// swagger:operation GET /libpod/images/json images listImages
//
// List Images
//
// ---
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/types/ImageSummary"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/libpod/images/json"), APIHandler(s.Context, libpod.GetImages)).Methods(http.MethodGet)
// swagger:operation POST /libpod/images/load images loadImage
//
// Import image
//
// ---
// parameters:
// - in: query
// name: quiet
// type: bool
// description: not supported
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/types/ImageSummary"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/libpod/images/load"), APIHandler(s.Context, handlers.LoadImage)).Methods(http.MethodPost)
// swagger:operation POST /libpod/images/prune images pruneImages
//
// Prune unused images
//
// ---
// parameters:
// - in: query
// name: filters
// type: map[string][]string
// description: image filters
// - in: query
// name: all
// type: bool
// description: prune all images
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/ImageDeleteResponse"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/libpod/images/prune"), APIHandler(s.Context, libpod.PruneImages)).Methods(http.MethodPost)
// swagger:operation GET /libpod/images/search images searchImages
//
// Search images
//
// ---
// parameters:
// - in: query
// name: term
// type: string
// description: term to search
// - in: query
// name: limit
// type: int
// description: maximum number of results
// - in: query
// name: filters
// type: map[string][]string
// description: TBD
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/images.SearchResult"
// description: no error
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/libpod/images/search"), APIHandler(s.Context, handlers.SearchImages)).Methods(http.MethodGet)
// swagger:operation DELETE /libpod/images/{nameOrID} images removeImage
//
// Remove Image
//
// ---
// parameters:
// - in: query
// name: force
// type: bool
// description: remove the image even if used by containers or has other tags
// - in: query
// name: noprune
// type: bool
// description: not supported
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "TBD"
// description: no error
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '409':
// description: conflict
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/libpod/images/{name:..*}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
// swagger:operation GET /libpod/images/{nameOrID}/get images exportImage
//
// Export an image
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: format
// type: string
// description: format for exported image
// - in: query
// name: compress
// type: bool
// description: use compression on image
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "TBD"
// description: no error
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/libpod/images/{name:..*}/get"), APIHandler(s.Context, libpod.ExportImage)).Methods(http.MethodGet)
// swagger:operation GET /libpod/images/{nameOrID}/json images inspectImage
//
// Inspect an image
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// produces:
// - application/json
// responses:
// '200':
// schema:
// items:
// "$ref": "#/inspect/ImageData"
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/libpod/images/{name:..*}/json"), APIHandler(s.Context, libpod.GetImage))
// swagger:operation POST /libpod/images/{nameOrID}/tag images tagImage
//
// Tag an image
//
// ---
// parameters:
// - in: path
// name: nameOrID
// required: true
// description: the name or ID of the container
// - in: query
// name: repo
// type: string
// description: the repository to tag in
// - in: query
// name: tag
// type: string
// description: the name of the new tag
// produces:
// - application/json
// responses:
// '201':
// description: no error
// '400':
// description: bad parameter
// schema:
// "$ref": "#/types/ErrorModel"
// '404':
// description: no such image
// schema:
// "$ref": "#/types/ErrorModel"
// '409':
// description: conflict
// schema:
// "$ref": "#/types/ErrorModel"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/libpod/images/{name:..*}/tag"), APIHandler(s.Context, handlers.TagImage)).Methods(http.MethodPost)
r.Handle(VersionedPath("/build"), APIHandler(s.Context, handlers.BuildImage)).Methods(http.MethodPost)
return nil
}

View File

@ -0,0 +1,28 @@
package server
import (
"net/http"
"github.com/containers/libpod/pkg/api/handlers/generic"
"github.com/gorilla/mux"
)
func (s *APIServer) registerInfoHandlers(r *mux.Router) error {
// swagger:operation GET /info libpod getInfo
//
// Returns information on the system and libpod configuration
//
// ---
// produces:
// - application/json
// responses:
// '200':
// schema:
// "$ref": "#/types/Info"
// '500':
// description: unexpected error
// schema:
// "$ref": "#/types/ErrorModel"
r.Handle(VersionedPath("/info"), APIHandler(s.Context, generic.GetInfo)).Methods(http.MethodGet)
return nil
}

View File

@ -0,0 +1,11 @@
package server
import (
"github.com/containers/libpod/pkg/api/handlers"
"github.com/gorilla/mux"
)
func (s *APIServer) RegisterMonitorHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/monitor"), APIHandler(s.Context, handlers.UnsupportedHandler))
return nil
}

View File

@ -0,0 +1,17 @@
package server
import (
"net/http"
"github.com/containers/libpod/pkg/api/handlers/generic"
"github.com/gorilla/mux"
)
func (s *APIServer) registerPingHandlers(r *mux.Router) error {
r.Handle("/_ping", APIHandler(s.Context, generic.PingGET)).Methods(http.MethodGet)
r.Handle("/_ping", APIHandler(s.Context, generic.PingHEAD)).Methods("HEAD")
// libpod
r.Handle("/libpod/_ping", APIHandler(s.Context, generic.PingGET)).Methods(http.MethodGet)
return nil
}

View File

@ -0,0 +1,11 @@
package server
import (
"github.com/containers/libpod/pkg/api/handlers"
"github.com/gorilla/mux"
)
func (s *APIServer) RegisterPluginsHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/plugins"), APIHandler(s.Context, handlers.UnsupportedHandler))
return nil
}

View File

@ -0,0 +1,24 @@
package server
import (
"net/http"
"github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/gorilla/mux"
)
func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/libpod/pods/json"), APIHandler(s.Context, libpod.Pods)).Methods(http.MethodGet)
r.Handle(VersionedPath("/libpod/pods/create"), APIHandler(s.Context, libpod.PodCreate)).Methods(http.MethodPost)
r.Handle(VersionedPath("/libpod/pods/prune"), APIHandler(s.Context, libpod.PodPrune)).Methods(http.MethodPost)
r.Handle(VersionedPath("/libpod/pods/{name:..*}"), APIHandler(s.Context, libpod.PodDelete)).Methods(http.MethodDelete)
r.Handle(VersionedPath("/libpod/pods/{name:..*}"), APIHandler(s.Context, libpod.PodInspect)).Methods(http.MethodGet)
r.Handle(VersionedPath("/libpod/pods/{name:..*}/exists"), APIHandler(s.Context, libpod.PodExists)).Methods(http.MethodGet)
r.Handle(VersionedPath("/libpod/pods/{name:..*}/kill"), APIHandler(s.Context, libpod.PodKill)).Methods(http.MethodPost)
r.Handle(VersionedPath("/libpod/pods/{name:..*}/pause"), APIHandler(s.Context, libpod.PodPause)).Methods(http.MethodPost)
r.Handle(VersionedPath("/libpod/pods/{name:..*}/restart"), APIHandler(s.Context, libpod.PodRestart)).Methods(http.MethodPost)
r.Handle(VersionedPath("/libpod/pods/{name:..*}/start"), APIHandler(s.Context, libpod.PodStart)).Methods(http.MethodPost)
r.Handle(VersionedPath("/libpod/pods/{name:..*}/stop"), APIHandler(s.Context, libpod.PodStop)).Methods(http.MethodPost)
r.Handle(VersionedPath("/libpod/pods/{name:..*}/unpause"), APIHandler(s.Context, libpod.PodUnpause)).Methods(http.MethodPost)
return nil
}

View File

@ -0,0 +1,27 @@
package server
import (
"errors"
"github.com/containers/libpod/pkg/api/handlers/utils"
"net/http"
"github.com/gorilla/mux"
"github.com/sirupsen/logrus"
)
func (s *APIServer) RegisterSwarmHandlers(r *mux.Router) error {
r.PathPrefix("/v{version:[0-9.]+}/configs/").HandlerFunc(noSwarm)
r.PathPrefix("/v{version:[0-9.]+}/nodes/").HandlerFunc(noSwarm)
r.PathPrefix("/v{version:[0-9.]+}/secrets/").HandlerFunc(noSwarm)
r.PathPrefix("/v{version:[0-9.]+}/services/").HandlerFunc(noSwarm)
r.PathPrefix("/v{version:[0-9.]+}/swarm/").HandlerFunc(noSwarm)
r.PathPrefix("/v{version:[0-9.]+}/tasks/").HandlerFunc(noSwarm)
return nil
}
// noSwarm returns http.StatusServiceUnavailable rather than something like http.StatusInternalServerError,
// this allows the client to decide if they still can talk to us
func noSwarm(w http.ResponseWriter, r *http.Request) {
logrus.Errorf("%s is not a podman supported service", r.URL.String())
utils.Error(w, "node is not part of a swarm", http.StatusServiceUnavailable, errors.New("Podman does not support service: "+r.URL.String()))
}

View File

@ -0,0 +1,11 @@
package server
import (
"github.com/containers/libpod/pkg/api/handlers/generic"
"github.com/gorilla/mux"
)
func (s *APIServer) registerSystemHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/system/df"), APIHandler(s.Context, generic.GetDiskUsage))
return nil
}

View File

@ -0,0 +1,12 @@
package server
import (
"github.com/containers/libpod/pkg/api/handlers/generic"
"github.com/gorilla/mux"
)
func (s *APIServer) registerVersionHandlers(r *mux.Router) error {
r.Handle("/version", APIHandler(s.Context, generic.VersionHandler))
r.Handle(VersionedPath("/version"), APIHandler(s.Context, generic.VersionHandler))
return nil
}

View File

@ -0,0 +1,17 @@
package server
import (
"net/http"
"github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/gorilla/mux"
)
func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
r.Handle("/libpod/volumes/create", APIHandler(s.Context, libpod.CreateVolume)).Methods(http.MethodPost)
r.Handle("/libpod/volumes/json", APIHandler(s.Context, libpod.ListVolumes)).Methods(http.MethodGet)
r.Handle("/libpod/volumes/prune", APIHandler(s.Context, libpod.PruneVolumes)).Methods(http.MethodPost)
r.Handle("/libpod/volumes/{name:..*}/json", APIHandler(s.Context, libpod.InspectVolume)).Methods(http.MethodGet)
r.Handle("/libpod/volumes/{name:..*}", APIHandler(s.Context, libpod.RemoveVolume)).Methods(http.MethodDelete)
return nil
}

189
pkg/api/server/server.go Normal file
View File

@ -0,0 +1,189 @@
// Package serviceapi Provides a Container compatible interface.
//
// This documentation describes the HTTP LibPod interface
//
// Schemes: http, https
// Host: podman.io
// BasePath: /
// 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/
//
// Consumes:
// - application/json
// - application/x-tar
//
// Produces:
// - application/json
// - text/plain
// - text/html
//
// tags:
// - name: "Containers"
// description: manage containers
// - name: "Images"
// description: manage images
// - name: "System"
// description: manage system resources
//
// swagger:meta
package server
import (
"context"
"net"
"net/http"
"os"
"os/signal"
"strings"
"time"
"github.com/containers/libpod/libpod"
"github.com/coreos/go-systemd/activation"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
type APIServer struct {
http.Server // Where the HTTP work happens
*schema.Decoder // Decoder for Query parameters to structs
context.Context // Context for graceful server shutdown
*libpod.Runtime // Where the real work happens
net.Listener // mux for routing HTTP API calls to libpod routines
context.CancelFunc // Stop APIServer
*time.Timer // Hold timer for sliding window
time.Duration // Duration of client access sliding window
}
// NewServer will create and configure a new API HTTP server
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))
}
quit := make(chan os.Signal, 1)
signal.Notify(quit)
router := mux.NewRouter()
server := APIServer{
Server: http.Server{
Handler: router,
ReadHeaderTimeout: 20 * time.Second,
ReadTimeout: 20 * time.Second,
WriteTimeout: 2 * time.Minute,
},
Decoder: schema.NewDecoder(),
Context: nil,
Runtime: runtime,
Listener: listeners[0],
CancelFunc: nil,
Duration: 300 * time.Second,
}
server.Timer = time.AfterFunc(server.Duration, func() {
if err := server.Shutdown(); err != nil {
log.Errorf("unable to shutdown server: %q", err)
}
})
ctx, cancelFn := context.WithCancel(context.Background())
// TODO: Use ConnContext when ported to go 1.13
ctx = context.WithValue(ctx, "decoder", server.Decoder)
ctx = context.WithValue(ctx, "runtime", runtime)
ctx = context.WithValue(ctx, "shutdownFunc", server.Shutdown)
server.Context = ctx
server.CancelFunc = cancelFn
server.Decoder.IgnoreUnknownKeys(true)
router.NotFoundHandler = http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
// We can track user errors...
log.Infof("Failed Request: (%d:%s) for %s:'%s'", http.StatusNotFound, http.StatusText(http.StatusNotFound), r.Method, r.URL.String())
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
},
)
for _, fn := range []func(*mux.Router) error{
server.RegisterAuthHandlers,
server.RegisterContainersHandlers,
server.RegisterDistributionHandlers,
server.registerHealthCheckHandlers,
server.registerImagesHandlers,
server.registerInfoHandlers,
server.RegisterMonitorHandlers,
server.registerPingHandlers,
server.RegisterPluginsHandlers,
server.registerPodsHandlers,
server.RegisterSwarmHandlers,
server.registerSystemHandlers,
server.registerVersionHandlers,
server.registerVolumeHandlers,
} {
if err := fn(router); err != nil {
return nil, err
}
}
if log.IsLevelEnabled(log.DebugLevel) {
router.Walk(func(route *mux.Route, r *mux.Router, ancestors []*mux.Route) error { // nolint
path, err := route.GetPathTemplate()
if err != nil {
path = ""
}
methods, err := route.GetMethods()
if err != nil {
methods = []string{}
}
log.Debugf("Methods: %s Path: %s", strings.Join(methods, ", "), path)
return nil
})
}
return &server, nil
}
// Serve starts responding to HTTP requests
func (s *APIServer) Serve() error {
defer s.CancelFunc()
err := s.Server.Serve(s.Listener)
if err != nil && err != http.ErrServerClosed {
return errors.Wrap(err, "Failed to start APIServer")
}
return nil
}
// Shutdown is a clean shutdown waiting on existing clients
func (s *APIServer) Shutdown() error {
// We're still in the sliding service window
if s.Timer.Stop() {
s.Timer.Reset(s.Duration)
return nil
}
// We've been idle for the service window, really shutdown
go func() {
err := s.Server.Shutdown(s.Context)
if err != nil && err != context.Canceled {
log.Errorf("Failed to cleanly shutdown APIServer: %s", err.Error())
}
}()
// Wait for graceful shutdown vs. just killing connections and dropping data
<-s.Context.Done()
return nil
}
// Close immediately stops responding to clients and exits
func (s *APIServer) Close() error {
return s.Server.Close()
}