Merge pull request #4980 from baude/bindingdocs

[CI:DOCS]Binding overhauls
This commit is contained in:
OpenShift Merge Robot
2020-01-28 08:21:18 -08:00
committed by GitHub
30 changed files with 1297 additions and 623 deletions

View File

@ -263,7 +263,7 @@ localunit: test/goecho/goecho varlink_generate
ginkgo \
-r \
$(TESTFLAGS) \
--skipPackage test/e2e,pkg/apparmor,test/endpoint \
--skipPackage test/e2e,pkg/apparmor,test/endpoint,pkg/bindings \
--cover \
--covermode atomic \
--tags "$(BUILDTAGS)" \

View File

@ -3,6 +3,8 @@ package generic
import (
"fmt"
"net/http"
"github.com/containers/libpod/pkg/api/handlers"
)
func PingGET(w http.ResponseWriter, _ *http.Request) {
@ -16,7 +18,7 @@ func PingHEAD(w http.ResponseWriter, _ *http.Request) {
}
func setHeaders(w http.ResponseWriter) {
w.Header().Set("API-Version", DefaultApiVersion)
w.Header().Set("API-Version", handlers.DefaultApiVersion)
w.Header().Set("BuildKit-Version", "")
w.Header().Set("Docker-Experimental", "true")
w.Header().Set("Cache-Control", "no-cache")

View File

@ -23,6 +23,7 @@ func Error(w http.ResponseWriter, apiMessage string, code int, err error) {
em := ErrorModel{
Because: (errors.Cause(err)).Error(),
Message: err.Error(),
ResponseCode: code,
}
WriteJSON(w, code, em)
}
@ -79,6 +80,8 @@ type ErrorModel struct {
// human error message, formatted for a human to read
// example: human error message
Message string `json:"message"`
// http response code
ResponseCode int `json:"response"`
}
func (e ErrorModel) Error() string {
@ -89,6 +92,10 @@ func (e ErrorModel) Cause() error {
return errors.New(e.Because)
}
func (e ErrorModel) Code() int {
return e.ResponseCode
}
// UnsupportedParameter logs a given param by its string name as not supported.
func UnSupportedParameter(param string) {
log.Infof("API parameter %q: not supported", param)

View File

@ -1,4 +1,4 @@
package generic
package handlers
import (
"fmt"
@ -8,7 +8,6 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
docker "github.com/docker/docker/api/types"
"github.com/pkg/errors"
@ -53,7 +52,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
},
}}
utils.WriteResponse(w, http.StatusOK, handlers.Version{Version: docker.Version{
utils.WriteResponse(w, http.StatusOK, Version{Version: docker.Version{
Platform: struct {
Name string
}{

View File

@ -195,7 +195,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: '#/responses/ConflictError'
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/images/name"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
r.Handle(VersionedPath("/images/{name}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
// swagger:operation GET /images/{name}/get compat exportImage
// ---
// tags:
@ -607,13 +607,13 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// summary: List Images
// description: Returns a list of images on the server
// parameters:
// - name: "all"
// in: "query"
// description: "Show all images. Only images from a final layer (no children) are shown by default."
// type: "boolean"
// - name: all
// in: query
// description: Show all images. Only images from a final layer (no children) are shown by default.
// type: boolean
// default: false
// - name: "filters"
// in: "query"
// - name: filters
// in: query
// description: |
// A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters:
// - `before`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`)
@ -621,12 +621,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - `label=key` or `label="key=value"` of an image label
// - `reference`=(`<image-name>[:<tag>]`)
// - `since`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`)
// type: "string"
// - name: "digests"
// in: "query"
// description: Not supported
// type: "boolean"
// default: false
// type: string
// produces:
// - application/json
// responses:
@ -753,7 +748,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: '#/responses/ConflictError'
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/name"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
r.Handle(VersionedPath("/libpod/images/{name}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
// swagger:operation GET /libpod/images/{name}/get libpod libpoodExportImage
// ---
// tags:

View File

@ -1,12 +1,12 @@
package server
import (
"github.com/containers/libpod/pkg/api/handlers/generic"
"github.com/containers/libpod/pkg/api/handlers"
"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))
r.Handle("/version", APIHandler(s.Context, handlers.VersionHandler))
r.Handle(VersionedPath("/version"), APIHandler(s.Context, handlers.VersionHandler))
return nil
}

View File

@ -1,14 +1,22 @@
package bindings
import (
"context"
"fmt"
"io"
"net"
"net/http"
"net/url"
"path/filepath"
"strings"
"github.com/containers/libpod/pkg/api/handlers"
jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
)
const (
defaultConnection string = "http://localhost:8080/v1.24/libpod"
pingConnection string = "http://localhost:8080/_ping"
var (
defaultConnectionPath string = filepath.Join(fmt.Sprintf("v%s", handlers.MinimalApiVersion), "libpod")
)
type APIResponse struct {
@ -17,46 +25,170 @@ type APIResponse struct {
}
type Connection struct {
url string
scheme string
address string
client *http.Client
}
func NewConnection(url string) (Connection, error) {
if len(url) < 1 {
url = defaultConnection
// NewConnection takes a URI as a string and returns a context with the
// Connection embedded as a value. This context needs to be passed to each
// endpoint to work correctly.
//
// A valid URI connection should be scheme://
// For example tcp://localhost:<port>
// or unix://run/podman/podman.sock
func NewConnection(uri string) (context.Context, error) {
u, err := url.Parse(uri)
if err != nil {
return nil, err
}
// TODO once ssh is implemented, remove this block and
// add it to the conditional beneath it
if u.Scheme == "ssh" {
return nil, ErrNotImplemented
}
if u.Scheme != "tcp" && u.Scheme != "unix" {
return nil, errors.Errorf("%s is not a support schema", u.Scheme)
}
if u.Scheme == "tcp" && !strings.HasPrefix(uri, "tcp://") {
return nil, errors.New("tcp URIs should begin with tcp://")
}
address := u.Path
if u.Scheme == "tcp" {
address = u.Host
}
newConn := newConnection(u.Scheme, address)
ctx := context.WithValue(context.Background(), "conn", &newConn)
if err := pingNewConnection(ctx); err != nil {
return nil, err
}
return ctx, nil
}
// pingNewConnection pings to make sure the RESTFUL service is up
// and running. it should only be used where initializing a connection
func pingNewConnection(ctx context.Context) error {
conn, err := GetConnectionFromContext(ctx)
if err != nil {
return err
}
// the ping endpoint sits at / in this case
response, err := conn.DoRequest(nil, http.MethodGet, "../../../_ping", nil)
if err != nil {
return err
}
if response.StatusCode == http.StatusOK {
return nil
}
return errors.Errorf("ping response was %q", response.StatusCode)
}
// newConnection takes a scheme and address and creates a connection from it
func newConnection(scheme, address string) Connection {
client := http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial(scheme, address)
},
},
}
newConn := Connection{
url: url,
client: &http.Client{},
client: &client,
address: address,
scheme: scheme,
}
response, err := http.Get(pingConnection)
if err != nil {
return newConn, err
}
if err := response.Body.Close(); err != nil {
return newConn, err
}
return newConn, err
return newConn
}
func (c Connection) makeEndpoint(u string) string {
return fmt.Sprintf("%s%s", defaultConnection, u)
func (c *Connection) makeEndpoint(u string) string {
// The d character in the url is discarded and is meaningless
return fmt.Sprintf("http://d/%s%s", defaultConnectionPath, u)
}
func (c Connection) newRequest(httpMethod, endpoint string, httpBody io.Reader, params map[string]string) (*APIResponse, error) {
e := c.makeEndpoint(endpoint)
// DoRequest assembles the http request and returns the response
func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, queryParams map[string]string, pathValues ...string) (*APIResponse, error) {
var (
err error
response *http.Response
)
safePathValues := make([]interface{}, len(pathValues))
// Make sure path values are http url safe
for _, pv := range pathValues {
safePathValues = append(safePathValues, url.QueryEscape(pv))
}
safeEndpoint := fmt.Sprintf(endpoint, safePathValues...)
e := c.makeEndpoint(safeEndpoint)
req, err := http.NewRequest(httpMethod, e, httpBody)
if err != nil {
return nil, err
}
if len(params) > 0 {
if len(queryParams) > 0 {
// if more desirable we could use url to form the encoded endpoint with params
r := req.URL.Query()
for k, v := range params {
r.Add(k, v)
for k, v := range queryParams {
r.Add(k, url.QueryEscape(v))
}
req.URL.RawQuery = r.Encode()
}
response, err := c.client.Do(req) // nolint
// Give the Do three chances in the case of a comm/service hiccup
for i := 0; i < 3; i++ {
response, err = c.client.Do(req) // nolint
if err == nil {
break
}
}
return &APIResponse{response, req}, err
}
// GetConnectionFromContext returns a bindings connection from the context
// being passed into each method.
func GetConnectionFromContext(ctx context.Context) (*Connection, error) {
c := ctx.Value("conn")
if c == nil {
return nil, errors.New("unable to get connection from context")
}
conn := c.(Connection)
return &conn, nil
}
// FiltersToHTML converts our typical filter format of a
// map[string][]string to a query/html safe string.
func FiltersToHTML(filters map[string][]string) (string, error) {
lowerCaseKeys := make(map[string][]string)
for k, v := range filters {
lowerCaseKeys[strings.ToLower(k)] = v
}
unsafeString, err := jsoniter.MarshalToString(lowerCaseKeys)
if err != nil {
return "", err
}
return url.QueryEscape(unsafeString), nil
}
// IsInformation returns true if the response code is 1xx
func (h *APIResponse) IsInformational() bool {
return h.Response.StatusCode/100 == 1
}
// IsSuccess returns true if the response code is 2xx
func (h *APIResponse) IsSuccess() bool {
return h.Response.StatusCode/100 == 2
}
// IsRedirection returns true if the response code is 3xx
func (h *APIResponse) IsRedirection() bool {
return h.Response.StatusCode/100 == 3
}
// IsClientError returns true if the response code is 4xx
func (h *APIResponse) IsClientError() bool {
return h.Response.StatusCode/100 == 4
}
// IsServerError returns true if the response code is 5xx
func (h *APIResponse) IsServerError() bool {
return h.Response.StatusCode/100 == 5
}

View File

@ -1,139 +0,0 @@
package bindings
import (
"fmt"
"net/http"
"strconv"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
)
func (c Connection) ListContainers(filter []string, last int, size, sync bool) ([]shared.PsContainerOutput, error) { // nolint:typecheck
images := []shared.PsContainerOutput{}
params := make(map[string]string)
params["last"] = strconv.Itoa(last)
params["size"] = strconv.FormatBool(size)
params["sync"] = strconv.FormatBool(sync)
response, err := c.newRequest(http.MethodGet, "/containers/json", nil, params)
if err != nil {
return images, err
}
return images, response.Process(nil)
}
func (c Connection) PruneContainers() ([]string, error) {
var (
pruned []string
)
response, err := c.newRequest(http.MethodPost, "/containers/prune", nil, nil)
if err != nil {
return pruned, err
}
return pruned, response.Process(nil)
}
func (c Connection) RemoveContainer(nameOrID string, force, volumes bool) error {
params := make(map[string]string)
params["force"] = strconv.FormatBool(force)
params["vols"] = strconv.FormatBool(volumes)
response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/containers/%s", nameOrID), nil, params)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) InspectContainer(nameOrID string, size bool) (*libpod.InspectContainerData, error) {
params := make(map[string]string)
params["size"] = strconv.FormatBool(size)
response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/containers/%s/json", nameOrID), nil, params)
if err != nil {
return nil, err
}
inspect := libpod.InspectContainerData{}
return &inspect, response.Process(&inspect)
}
func (c Connection) KillContainer(nameOrID string, signal int) error {
params := make(map[string]string)
params["signal"] = strconv.Itoa(signal)
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/kill", nameOrID), nil, params)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) ContainerLogs() {}
func (c Connection) PauseContainer(nameOrID string) error {
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/pause", nameOrID), nil, nil)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) RestartContainer(nameOrID string, timeout int) error {
// TODO how do we distinguish between an actual zero value and not wanting to change the timeout value
params := make(map[string]string)
params["timeout"] = strconv.Itoa(timeout)
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/restart", nameOrID), nil, params)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) StartContainer(nameOrID, detachKeys string) error {
params := make(map[string]string)
if len(detachKeys) > 0 {
params["detachKeys"] = detachKeys
}
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/start", nameOrID), nil, params)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) ContainerStats() {}
func (c Connection) ContainerTop() {}
func (c Connection) UnpauseContainer(nameOrID string) error {
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/unpause", nameOrID), nil, nil)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) WaitContainer(nameOrID string) error {
// TODO when returns are ironed out, we can should use the newRequest approach
_, err := http.Post(c.makeEndpoint(fmt.Sprintf("containers/%s/wait", nameOrID)), "application/json", nil) // nolint
return err
}
func (c Connection) ContainerExists(nameOrID string) (bool, error) {
response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/containers/%s/exists", nameOrID))) // nolint
defer closeResponseBody(response)
if err != nil {
return false, err
}
if response.StatusCode == http.StatusOK {
return true, nil
}
return false, nil
}
func (c Connection) StopContainer(nameOrID string, timeout *int) error {
params := make(map[string]string)
if timeout != nil {
params["t"] = strconv.Itoa(*timeout)
}
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/stop", nameOrID), nil, params)
if err != nil {
return err
}
return response.Process(nil)
}

View File

@ -0,0 +1,255 @@
package containers
import (
"context"
"net/http"
"strconv"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/bindings"
)
// List obtains a list of containers in local storage. All parameters to this method are optional.
// The filters are used to determine which containers are listed. The last parameter indicates to only return
// the most recent number of containers. The pod and size booleans indicate that pod information and rootfs
// size information should also be included. Finally, the sync bool synchronizes the OCI runtime and
// container state.
func List(ctx context.Context, filters map[string][]string, last *int, pod, size, sync *bool) ([]*shared.PsContainerOutput, error) { // nolint:typecheck
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
var images []*shared.PsContainerOutput
params := make(map[string]string)
if last != nil {
params["last"] = strconv.Itoa(*last)
}
if pod != nil {
params["pod"] = strconv.FormatBool(*pod)
}
if size != nil {
params["size"] = strconv.FormatBool(*size)
}
if sync != nil {
params["sync"] = strconv.FormatBool(*sync)
}
if filters != nil {
filterString, err := bindings.FiltersToHTML(filters)
if err != nil {
return nil, err
}
params["filters"] = filterString
}
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params)
if err != nil {
return images, err
}
return images, response.Process(nil)
}
// Prune removes stopped and exited containers from local storage. The optional filters can be
// used for more granular selection of containers. The main error returned indicates if there were runtime
// errors like finding containers. Errors specific to the removal of a container are in the PruneContainerResponse
// structure.
func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
var (
pruneResponse []string
)
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
params := make(map[string]string)
if filters != nil {
filterString, err := bindings.FiltersToHTML(filters)
if err != nil {
return nil, err
}
params["filters"] = filterString
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/prune", params)
if err != nil {
return pruneResponse, err
}
return pruneResponse, response.Process(pruneResponse)
}
// Remove removes a container from local storage. The force bool designates
// that the container should be removed forcibly (example, even it is running). The volumes
// bool dictates that a container's volumes should also be removed.
func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
params := make(map[string]string)
if force != nil {
params["force"] = strconv.FormatBool(*force)
}
if volumes != nil {
params["vols"] = strconv.FormatBool(*volumes)
}
response, err := conn.DoRequest(nil, http.MethodDelete, "/containers/%s", params, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
// Inspect returns low level information about a Container. The nameOrID can be a container name
// or a partial/full ID. The size bool determines whether the size of the container's root filesystem
// should be calculated. Calculating the size of a container requires extra work from the filesystem and
// is therefore slower.
func Inspect(ctx context.Context, nameOrID string, size *bool) (*libpod.InspectContainerData, error) {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
params := make(map[string]string)
if size != nil {
params["size"] = strconv.FormatBool(*size)
}
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/json", params, nameOrID)
if err != nil {
return nil, err
}
inspect := libpod.InspectContainerData{}
return &inspect, response.Process(&inspect)
}
// Kill sends a given signal to a given container. The signal should be the string
// representation of a signal like 'SIGKILL'. The nameOrID can be a container name
// or a partial/full ID
func Kill(ctx context.Context, nameOrID string, signal string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
params := make(map[string]string)
params["signal"] = signal
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/kill", params, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
func Logs() {}
// Pause pauses a given container. The nameOrID can be a container name
// or a partial/full ID.
func Pause(ctx context.Context, nameOrID string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/pause", nil, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
// Restart restarts a running container. The nameOrID can be a container name
// or a partial/full ID. The optional timeout specifies the number of seconds to wait
// for the running container to stop before killing it.
func Restart(ctx context.Context, nameOrID string, timeout *int) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
params := make(map[string]string)
if timeout != nil {
params["t"] = strconv.Itoa(*timeout)
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restart", params, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
// Start starts a non-running container.The nameOrID can be a container name
// or a partial/full ID. The optional parameter for detach keys are to override the default
// detach key sequence.
func Start(ctx context.Context, nameOrID string, detachKeys *string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
params := make(map[string]string)
if detachKeys != nil {
params["detachKeys"] = *detachKeys
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/start", params, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
func Stats() {}
func Top() {}
// Unpause resumes the given paused container. The nameOrID can be a container name
// or a partial/full ID.
func Unpause(ctx context.Context, nameOrID string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/unpause", nil, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
// Wait blocks until the given container exits and returns its exit code. The nameOrID can be a container name
// or a partial/full ID.
func Wait(ctx context.Context, nameOrID string) (int32, error) {
var exitCode int32
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return exitCode, err
}
response, err := conn.DoRequest(nil, http.MethodPost, "containers/%s/wait", nil, nameOrID)
if err != nil {
return exitCode, err
}
return exitCode, response.Process(&exitCode)
}
// Exists is a quick, light-weight way to determine if a given container
// exists in local storage. The nameOrID can be a container name
// or a partial/full ID.
func Exists(ctx context.Context, nameOrID string) (bool, error) {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return false, err
}
response, err := conn.DoRequest(nil, http.MethodGet, "containers/%s/exists", nil, nameOrID)
if err != nil {
return false, err
}
return response.IsSuccess(), nil
}
// Stop stops a running container. The timeout is optional. The nameOrID can be a container name
// or a partial/full ID
func Stop(ctx context.Context, nameOrID string, timeout *int) error {
params := make(map[string]string)
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
if timeout != nil {
params["t"] = strconv.Itoa(*timeout)
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/stop", params, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}

View File

@ -0,0 +1,26 @@
package containers
import (
"context"
"github.com/containers/libpod/pkg/bindings"
"net/http"
"github.com/containers/libpod/libpod"
)
// RunHealthCheck executes the container's healthcheck and returns the health status of the
// container.
func RunHealthCheck(ctx context.Context, nameOrID string) (*libpod.HealthCheckStatus, error) {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
var (
status libpod.HealthCheckStatus
)
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/runhealthcheck", nil, nameOrID)
if err != nil {
return nil, err
}
return &status, response.Process(&status)
}

View File

@ -0,0 +1,53 @@
package containers
import (
"context"
"net/http"
"github.com/containers/libpod/pkg/bindings"
)
// Mount mounts an existing container to the filesystem. It returns the path
// of the mounted container in string format.
func Mount(ctx context.Context, nameOrID string) (string, error) {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return "", err
}
var (
path string
)
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/mount", nil, nameOrID)
if err != nil {
return path, err
}
return path, response.Process(&path)
}
// Unmount unmounts a container from the filesystem. The container must not be running
// or the unmount will fail.
func Unmount(ctx context.Context, nameOrID string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/unmount", nil, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
// GetMountedContainerPaths returns a map of mounted containers and their mount locations.
func GetMountedContainerPaths(ctx context.Context) (map[string]string, error) {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
mounts := make(map[string]string)
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/showmounted", nil)
if err != nil {
return mounts, err
}
return mounts, response.Process(&mounts)
}

View File

@ -7,7 +7,6 @@ import (
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var (
@ -37,10 +36,10 @@ func (a APIResponse) Process(unmarshalInto interface{}) error {
return handleError(data)
}
func closeResponseBody(r *http.Response) {
if r != nil {
if err := r.Body.Close(); err != nil {
logrus.Error(errors.Wrap(err, "unable to close response body"))
}
func CheckResponseCode(inError error) (int, error) {
e, ok := inError.(utils.ErrorModel)
if !ok {
return -1, errors.New("error is not type ErrorModel")
}
return e.Code(), nil
}

View File

@ -1,4 +0,0 @@
package bindings
func (c Connection) GenerateKube() {}
func (c Connection) GenerateSystemd() {}

View File

@ -0,0 +1,4 @@
package generate
func GenerateKube() {}
func GenerateSystemd() {}

View File

@ -1,19 +0,0 @@
package bindings
import (
"fmt"
"net/http"
"github.com/containers/libpod/libpod"
)
func (c Connection) RunHealthCheck(nameOrID string) (*libpod.HealthCheckStatus, error) {
var (
status libpod.HealthCheckStatus
)
response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/containers/%s/runhealthcheck", nameOrID), nil, nil)
if err != nil {
return nil, err
}
return &status, response.Process(&status)
}

View File

@ -1,111 +0,0 @@
package bindings
import (
"fmt"
"io"
"net/http"
"strconv"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/inspect"
)
func (c Connection) ImageExists(nameOrID string) (bool, error) {
response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/images/%s/exists", nameOrID))) // nolint
defer closeResponseBody(response)
if err != nil {
return false, err
}
if response.StatusCode == http.StatusOK {
return true, nil
}
return false, nil
}
func (c Connection) ListImages() ([]handlers.ImageSummary, error) {
imageSummary := []handlers.ImageSummary{}
response, err := c.newRequest(http.MethodGet, "/images/json", nil, nil)
if err != nil {
return imageSummary, err
}
return imageSummary, response.Process(&imageSummary)
}
func (c Connection) GetImage(nameOrID string) (*inspect.ImageData, error) {
inspectedData := inspect.ImageData{}
response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/images/%s/json", nameOrID), nil, nil)
if err != nil {
return &inspectedData, err
}
return &inspectedData, response.Process(&inspectedData)
}
func (c Connection) ImageTree(nameOrId string) error {
return ErrNotImplemented
}
func (c Connection) ImageHistory(nameOrID string) ([]handlers.HistoryResponse, error) {
history := []handlers.HistoryResponse{}
response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/images/%s/history", nameOrID), nil, nil)
if err != nil {
return history, err
}
return history, response.Process(&history)
}
func (c Connection) LoadImage(r io.Reader) error {
// TODO this still needs error handling added
_, err := http.Post(c.makeEndpoint("/images/loads"), "application/json", r) //nolint
return err
}
func (c Connection) RemoveImage(nameOrID string, force bool) ([]map[string]string, error) {
deletes := []map[string]string{}
params := make(map[string]string)
params["force"] = strconv.FormatBool(force)
response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/images/%s", nameOrID), nil, params)
if err != nil {
return nil, err
}
return deletes, response.Process(&deletes)
}
func (c Connection) ExportImage(nameOrID string, w io.Writer, format string, compress bool) error {
params := make(map[string]string)
params["format"] = format
params["compress"] = strconv.FormatBool(compress)
response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/images/%s/get", nameOrID), nil, params)
if err != nil {
return err
}
if err := response.Process(nil); err != nil {
return err
}
_, err = io.Copy(w, response.Body)
return err
}
func (c Connection) PruneImages(all bool, filters []string) ([]string, error) {
var (
deleted []string
)
params := make(map[string]string)
// FIXME How do we do []strings?
//params["filters"] = format
response, err := c.newRequest(http.MethodPost, "/images/prune", nil, params)
if err != nil {
return deleted, err
}
return deleted, response.Process(nil)
}
func (c Connection) TagImage(nameOrID string) error {
var ()
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/images/%s/tag", nameOrID), nil, nil)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) BuildImage(nameOrId string) {}

View File

@ -0,0 +1,187 @@
package images
import (
"context"
"io"
"net/http"
"strconv"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/inspect"
)
// Exists a lightweight way to determine if an image exists in local storage. It returns a
// boolean response.
func Exists(ctx context.Context, nameOrID string) (bool, error) {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return false, err
}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/exists", nil, nameOrID)
if err != nil {
return false, err
}
return response.IsSuccess(), nil
}
// List returns a list of images in local storage. The all boolean and filters parameters are optional
// ways to alter the image query.
func List(ctx context.Context, all *bool, filters map[string][]string) ([]*handlers.ImageSummary, error) {
var imageSummary []*handlers.ImageSummary
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
params := make(map[string]string)
if all != nil {
params["all"] = strconv.FormatBool(*all)
}
if filters != nil {
strFilters, err := bindings.FiltersToHTML(filters)
if err != nil {
return nil, err
}
params["filters"] = strFilters
}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/json", params)
if err != nil {
return imageSummary, err
}
return imageSummary, response.Process(&imageSummary)
}
// Get performs an image inspect. To have the on-disk size of the image calculated, you can
// use the optional size parameter.
func GetImage(ctx context.Context, nameOrID string, size *bool) (*inspect.ImageData, error) {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
params := make(map[string]string)
if size != nil {
params["size"] = strconv.FormatBool(*size)
}
inspectedData := inspect.ImageData{}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nameOrID)
if err != nil {
return &inspectedData, err
}
return &inspectedData, response.Process(&inspectedData)
}
func ImageTree(ctx context.Context, nameOrId string) error {
return bindings.ErrNotImplemented
}
// History returns the parent layers of an image.
func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse, error) {
var history []*handlers.HistoryResponse
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/history", nil, nameOrID)
if err != nil {
return history, err
}
return history, response.Process(&history)
}
func Load(ctx context.Context, r io.Reader) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
// TODO this still needs error handling added
//_, err := http.Post(c.makeEndpoint("/images/loads"), "application/json", r) //nolint
_ = conn
return bindings.ErrNotImplemented
}
// Remove deletes an image from local storage. The optional force parameter will forcibly remove
// the image by removing all all containers, including those that are Running, first.
func Remove(ctx context.Context, nameOrID string, force *bool) ([]map[string]string, error) {
var deletes []map[string]string
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
params := make(map[string]string)
if force != nil {
params["force"] = strconv.FormatBool(*force)
}
response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nameOrID)
if err != nil {
return nil, err
}
return deletes, response.Process(&deletes)
}
// Export saves an image from local storage as a tarball or image archive. The optional format
// parameter is used to change the format of the output.
func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, compress *bool) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
params := make(map[string]string)
if format != nil {
params["format"] = *format
}
if compress != nil {
params["compress"] = strconv.FormatBool(*compress)
}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/get", params, nameOrID)
if err != nil {
return err
}
if err := response.Process(nil); err != nil {
return err
}
_, err = io.Copy(w, response.Body)
return err
}
// Prune removes unused images from local storage. The optional filters can be used to further
// define which images should be pruned.
func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
var (
deleted []string
)
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
params := make(map[string]string)
if filters != nil {
stringFilter, err := bindings.FiltersToHTML(filters)
if err != nil {
return nil, err
}
params["filters"] = stringFilter
}
response, err := conn.DoRequest(nil, http.MethodPost, "/images/prune", params)
if err != nil {
return deleted, err
}
return deleted, response.Process(nil)
}
// Tag adds an additional name to locally-stored image. Both the tag and repo parameters are required.
func Tag(ctx context.Context, nameOrID, tag, repo string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
params := make(map[string]string)
params["tag"] = tag
params["repo"] = repo
response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/tag", params, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
func Build(nameOrId string) {}

View File

@ -0,0 +1,40 @@
package images
import (
"context"
"net/http"
"strconv"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/bindings"
)
// Search looks for the given image (term) in container image registries. The optional limit parameter sets
// a maximum number of results returned. The optional filters parameter allow for more specific image
// searches.
func Search(ctx context.Context, term string, limit *int, filters map[string][]string) ([]image.SearchResult, error) {
var (
searchResults []image.SearchResult
)
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
params := make(map[string]string)
params["term"] = term
if limit != nil {
params["limit"] = strconv.Itoa(*limit)
}
if filters != nil {
stringFilter, err := bindings.FiltersToHTML(filters)
if err != nil {
return nil, err
}
params["filters"] = stringFilter
}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params)
if err != nil {
return searchResults, nil
}
return searchResults, response.Process(&searchResults)
}

View File

@ -1,26 +0,0 @@
package bindings
import (
"fmt"
"net/http"
)
func (c Connection) MountContainer(nameOrID string) (string, error) {
var (
path string
)
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/mount", nameOrID), nil, nil)
if err != nil {
return path, err
}
return path, response.Process(&path)
}
func (c Connection) GetMountedContainerPaths() (map[string]string, error) {
mounts := make(map[string]string)
response, err := c.newRequest(http.MethodGet, "/containers/showmounted", nil, nil)
if err != nil {
return mounts, err
}
return mounts, response.Process(&mounts)
}

View File

@ -1,37 +0,0 @@
package bindings
import (
"fmt"
"net/http"
"github.com/containernetworking/cni/libcni"
)
func (c Connection) CreateNetwork() {}
func (c Connection) InspectNetwork(nameOrID string) (map[string]interface{}, error) {
n := make(map[string]interface{})
response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/networks/%s/json", nameOrID), nil, nil)
if err != nil {
return n, err
}
return n, response.Process(&n)
}
func (c Connection) RemoveNetwork(nameOrID string) error {
response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/networks/%s", nameOrID), nil, nil)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) ListNetworks() ([]*libcni.NetworkConfigList, error) {
var (
netList []*libcni.NetworkConfigList
)
response, err := c.newRequest(http.MethodGet, "/networks/json", nil, nil)
if err != nil {
return netList, err
}
return netList, response.Process(&netList)
}

View File

@ -0,0 +1,50 @@
package network
import (
"context"
"net/http"
"github.com/containernetworking/cni/libcni"
"github.com/containers/libpod/pkg/bindings"
)
func Create() {}
func Inspect(ctx context.Context, nameOrID string) (map[string]interface{}, error) {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
n := make(map[string]interface{})
response, err := conn.DoRequest(nil, http.MethodGet, "/networks/%s/json", nil, nameOrID)
if err != nil {
return n, err
}
return n, response.Process(&n)
}
func Remove(ctx context.Context, nameOrID string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
response, err := conn.DoRequest(nil, http.MethodDelete, "/networks/%s", nil, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
func List(ctx context.Context) ([]*libcni.NetworkConfigList, error) {
var (
netList []*libcni.NetworkConfigList
)
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
response, err := conn.DoRequest(nil, http.MethodGet, "/networks/json", nil)
if err != nil {
return netList, err
}
return netList, response.Process(&netList)
}

View File

@ -1,3 +0,0 @@
package bindings
func (c Connection) PlayKube() {}

View File

@ -0,0 +1,7 @@
package play
import "github.com/containers/libpod/pkg/bindings"
func PlayKube() error {
return bindings.ErrNotImplemented
}

View File

@ -1,129 +0,0 @@
package bindings
import (
"fmt"
"net/http"
"strconv"
"github.com/containers/libpod/libpod"
)
func (c Connection) CreatePod() error {
// TODO
return ErrNotImplemented
}
func (c Connection) PodExists(nameOrID string) (bool, error) {
response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/pods/%s/exists", nameOrID))) // nolint
defer closeResponseBody(response)
if err != nil {
return false, err
}
return response.StatusCode == http.StatusOK, err
}
func (c Connection) InspectPod(nameOrID string) (*libpod.PodInspect, error) {
inspect := libpod.PodInspect{}
response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/pods/%s/json", nameOrID), nil, nil)
if err != nil {
return &inspect, err
}
return &inspect, response.Process(&inspect)
}
func (c Connection) KillPod(nameOrID string, signal int) error {
params := make(map[string]string)
params["signal"] = strconv.Itoa(signal)
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/pods/%s/kill", nameOrID), nil, params)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) PausePod(nameOrID string) error {
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/pods/%s/pause", nameOrID), nil, nil)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) PrunePods(force bool) error {
params := make(map[string]string)
params["force"] = strconv.FormatBool(force)
response, err := c.newRequest(http.MethodPost, "/pods/prune", nil, params)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) ListPods(filters []string) (*[]libpod.PodInspect, error) {
var (
inspect []libpod.PodInspect
)
params := make(map[string]string)
// TODO I dont remember how to do this for []string{}
// FIXME
//params["filters"] = strconv.FormatBool(force)
response, err := c.newRequest(http.MethodPost, "/pods/json", nil, params)
if err != nil {
return &inspect, err
}
return &inspect, response.Process(&inspect)
}
func (c Connection) RestartPod(nameOrID string) error {
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/pods/%s/restart", nameOrID), nil, nil)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) RemovePod(nameOrID string, force bool) error {
params := make(map[string]string)
params["force"] = strconv.FormatBool(force)
response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/pods/%s", nameOrID), nil, params)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) StartPod(nameOrID string) error {
response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/pods/%s/start", nameOrID), nil, nil)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) PodStats() error {
// TODO
return ErrNotImplemented
}
func (c Connection) StopPod(nameOrID string, timeout int) error {
params := make(map[string]string)
params["t"] = strconv.Itoa(timeout)
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/pods/%s/stop", nameOrID), nil, params)
if err != nil {
return err
}
return response.Process(nil)
}
func (c Connection) PodTop() error {
// TODO
return ErrNotImplemented // nolint:typecheck
}
func (c Connection) UnpausePod(nameOrID string) error {
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/pods/%s/unpause", nameOrID), nil, nil)
if err != nil {
return err
}
return response.Process(nil)
}

196
pkg/bindings/pods/pods.go Normal file
View File

@ -0,0 +1,196 @@
package pods
import (
"context"
"net/http"
"strconv"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/bindings"
)
func CreatePod() error {
// TODO
return bindings.ErrNotImplemented
}
// Exists is a lightweight method to determine if a pod exists in local storage
func Exists(ctx context.Context, nameOrID string) (bool, error) {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return false, err
}
response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/exists", nil, nameOrID)
if err != nil {
return false, err
}
return response.IsSuccess(), nil
}
// Inspect returns low-level information about the given pod.
func Inspect(ctx context.Context, nameOrID string) (*libpod.PodInspect, error) {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
inspect := libpod.PodInspect{}
response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/json", nil, nameOrID)
if err != nil {
return &inspect, err
}
return &inspect, response.Process(&inspect)
}
// Kill sends a SIGTERM to all the containers in a pod. The optional signal parameter
// can be used to override SIGTERM.
func Kill(ctx context.Context, nameOrID string, signal *string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
params := make(map[string]string)
if signal != nil {
params["signal"] = *signal
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
// Pause pauses all running containers in a given pod.
func Pause(ctx context.Context, nameOrID string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/pause", nil, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
// Prune removes all non-running pods in local storage.
func Prune(ctx context.Context) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/prune", nil)
if err != nil {
return err
}
return response.Process(nil)
}
// List returns all pods in local storage. The optional filters parameter can
// be used to refine which pods should be listed.
func List(ctx context.Context, filters map[string][]string) (*[]libpod.PodInspect, error) {
var (
inspect []libpod.PodInspect
)
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
params := make(map[string]string)
if filters != nil {
stringFilter, err := bindings.FiltersToHTML(filters)
if err != nil {
return nil, err
}
params["filters"] = stringFilter
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/json", params)
if err != nil {
return &inspect, err
}
return &inspect, response.Process(&inspect)
}
// Restart restarts all containers in a pod.
func Restart(ctx context.Context, nameOrID string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/restart", nil, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
// Remove deletes a Pod from from local storage. The optional force parameter denotes
// that the Pod can be removed even if in a running state.
func Remove(ctx context.Context, nameOrID string, force *bool) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
params := make(map[string]string)
if force != nil {
params["force"] = strconv.FormatBool(*force)
}
response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
// Start starts all containers in a pod.
func Start(ctx context.Context, nameOrID string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s/start", nil, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
func Stats() error {
// TODO
return bindings.ErrNotImplemented
}
// Stop stops all containers in a Pod. The optional timeout parameter can be
// used to override the timeout before the container is killed.
func Stop(ctx context.Context, nameOrID string, timeout *int) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
params := make(map[string]string)
if timeout != nil {
params["t"] = strconv.Itoa(*timeout)
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}
func Top() error {
// TODO
return bindings.ErrNotImplemented // nolint:typecheck
}
// Unpause unpauses all paused containers in a Pod.
func Unpause(ctx context.Context, nameOrID string) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/unpause", nil, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}

View File

@ -1,39 +0,0 @@
package bindings
import (
"net/http"
"strconv"
"github.com/containers/libpod/libpod/image"
)
type ImageSearchFilters struct {
Automated bool `json:"automated"`
Official bool `json:"official"`
Stars int `json:"stars"`
}
// TODO This method can be concluded when we determine how we want the filters to work on the
// API end
func (i *ImageSearchFilters) ToMapJSON() string {
return ""
}
func (c Connection) SearchImages(term string, limit int, filters *ImageSearchFilters) ([]image.SearchResult, error) {
var (
searchResults []image.SearchResult
)
params := make(map[string]string)
params["term"] = term
if limit > 0 {
params["limit"] = strconv.Itoa(limit)
}
if filters != nil {
params["filters"] = filters.ToMapJSON()
}
response, err := c.newRequest(http.MethodGet, "/images/search", nil, params)
if err != nil {
return searchResults, nil
}
return searchResults, response.Process(&searchResults)
}

View File

@ -0,0 +1,112 @@
package test_bindings
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/onsi/ginkgo"
"github.com/onsi/gomega/gexec"
"github.com/pkg/errors"
)
const (
defaultPodmanBinaryLocation string = "/usr/bin/podman"
)
type bindingTest struct {
artifactDirPath string
imageCacheDir string
sock string
tempDirPath string
runRoot string
crioRoot string
}
func (b *bindingTest) runPodman(command []string) *gexec.Session {
var cmd []string
podmanBinary := defaultPodmanBinaryLocation
val, ok := os.LookupEnv("PODMAN_BINARY")
if ok {
podmanBinary = val
}
val, ok = os.LookupEnv("CGROUP_MANAGER")
if ok {
cmd = append(cmd, "--cgroup-manager", val)
}
val, ok = os.LookupEnv("CNI_CONFIG_DIR")
if ok {
cmd = append(cmd, "--cni-config-dir", val)
}
val, ok = os.LookupEnv("CONMON")
if ok {
cmd = append(cmd, "--conmon", val)
}
val, ok = os.LookupEnv("ROOT")
if ok {
cmd = append(cmd, "--root", val)
} else {
cmd = append(cmd, "--root", b.crioRoot)
}
val, ok = os.LookupEnv("OCI_RUNTIME")
if ok {
cmd = append(cmd, "--runtime", val)
}
val, ok = os.LookupEnv("RUNROOT")
if ok {
cmd = append(cmd, "--runroot", val)
} else {
cmd = append(cmd, "--runroot", b.runRoot)
}
val, ok = os.LookupEnv("STORAGE_DRIVER")
if ok {
cmd = append(cmd, "--storage-driver", val)
}
val, ok = os.LookupEnv("STORAGE_OPTIONS")
if ok {
cmd = append(cmd, "--storage", val)
}
cmd = append(cmd, command...)
c := exec.Command(podmanBinary, cmd...)
fmt.Printf("Running: %s %s\n", podmanBinary, strings.Join(cmd, " "))
session, err := gexec.Start(c, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter)
if err != nil {
panic(errors.Errorf("unable to run podman command: %q", cmd))
}
return session
}
func newBindingTest() *bindingTest {
tmpPath, _ := createTempDirInTempDir()
b := bindingTest{
crioRoot: filepath.Join(tmpPath, "crio"),
runRoot: filepath.Join(tmpPath, "run"),
artifactDirPath: "",
imageCacheDir: "",
sock: fmt.Sprintf("unix:%s", filepath.Join(tmpPath, "api.sock")),
tempDirPath: tmpPath,
}
return &b
}
// createTempDirinTempDir create a temp dir with prefix podman_test
func createTempDirInTempDir() (string, error) {
return ioutil.TempDir("", "libpod_api")
}
func (b *bindingTest) startAPIService() *gexec.Session {
var (
cmd []string
)
cmd = append(cmd, "--log-level=debug", "service", "--timeout=999999", b.sock)
return b.runPodman(cmd)
}
func (b *bindingTest) cleanup() {
if err := os.RemoveAll(b.tempDirPath); err != nil {
fmt.Println(err)
}
}

View File

@ -0,0 +1,92 @@
package test_bindings
import (
"context"
"fmt"
"time"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/bindings/images"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
)
var _ = Describe("Podman images", func() {
var (
//tempdir string
//err error
//podmanTest *PodmanTestIntegration
bt *bindingTest
s *gexec.Session
connText context.Context
err error
false bool
//true bool = true
)
BeforeEach(func() {
//tempdir, err = CreateTempDirInTempDir()
//if err != nil {
// os.Exit(1)
//}
//podmanTest = PodmanTestCreate(tempdir)
//podmanTest.Setup()
//podmanTest.SeedImages()
bt = newBindingTest()
p := bt.runPodman([]string{"pull", "docker.io/library/alpine:latest"})
p.Wait(45)
s = bt.startAPIService()
time.Sleep(1 * time.Second)
connText, err = bindings.NewConnection(bt.sock)
Expect(err).To(BeNil())
})
AfterEach(func() {
//podmanTest.Cleanup()
//f := CurrentGinkgoTestDescription()
//processTestResult(f)
s.Kill()
bt.cleanup()
})
It("inspect image", func() {
// Inspect invalid image be 404
_, err = images.GetImage(connText, "foobar5000", nil)
Expect(err).ToNot(BeNil())
code, _ := bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", 404))
// Inspect by short name
data, err := images.GetImage(connText, "alpine", nil)
Expect(err).To(BeNil())
// Inspect with full ID
_, err = images.GetImage(connText, data.ID, nil)
Expect(err).To(BeNil())
// Inspect with partial ID
_, err = images.GetImage(connText, data.ID[0:12], nil)
Expect(err).To(BeNil())
// Inspect by ID
// Inspect by long name should work, it doesnt (yet) i think it needs to be html escaped
//_, err = images.GetImage(connText, )
//Expect(err).To(BeNil())
})
It("remove image", func() {
// Remove invalid image should be a 404
_, err = images.RemoveImage(connText, "foobar5000", &false)
Expect(err).ToNot(BeNil())
code, _ := bindings.CheckResponseCode(err)
Expect(code).To(BeNumerically("==", 404))
_, err := images.GetImage(connText, "alpine", nil)
Expect(err).To(BeNil())
response, err := images.RemoveImage(connText, "alpine", &false)
Expect(err).To(BeNil())
fmt.Println(response)
// to be continued
})
})

View File

@ -1,60 +0,0 @@
package bindings
import (
"fmt"
"net/http"
"strconv"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/api/handlers"
)
func (c Connection) CreateVolume(config handlers.VolumeCreateConfig) (string, error) {
var (
volumeID string
)
response, err := c.newRequest(http.MethodPost, "/volumes/create", nil, nil)
if err != nil {
return volumeID, err
}
return volumeID, response.Process(&volumeID)
}
func (c Connection) InspectVolume(nameOrID string) (*libpod.InspectVolumeData, error) {
var (
inspect libpod.InspectVolumeData
)
response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/volumes/%s/json", nameOrID), nil, nil)
if err != nil {
return &inspect, err
}
return &inspect, response.Process(&inspect)
}
func (c Connection) ListVolumes() error {
// TODO
// The API side of things for this one does a lot in main and therefore
// is not implemented yet.
return ErrNotImplemented // nolint:typecheck
}
func (c Connection) PruneVolumes() ([]string, error) {
var (
pruned []string
)
response, err := c.newRequest(http.MethodPost, "/volumes/prune", nil, nil)
if err != nil {
return pruned, err
}
return pruned, response.Process(&pruned)
}
func (c Connection) RemoveVolume(nameOrID string, force bool) error {
params := make(map[string]string)
params["force"] = strconv.FormatBool(force)
response, err := c.newRequest(http.MethodPost, "/volumes/prune", nil, params)
if err != nil {
return err
}
return response.Process(nil)
}

View File

@ -0,0 +1,85 @@
package volumes
import (
"context"
"net/http"
"strconv"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings"
)
// Create creates a volume given its configuration.
func Create(ctx context.Context, config handlers.VolumeCreateConfig) (string, error) {
// TODO This is incomplete. The config needs to be sent via the body
var (
volumeID string
)
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return "", err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/create", nil)
if err != nil {
return volumeID, err
}
return volumeID, response.Process(&volumeID)
}
// Inspect returns low-level information about a volume.
func Inspect(ctx context.Context, nameOrID string) (*libpod.InspectVolumeData, error) {
var (
inspect libpod.InspectVolumeData
)
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/%s/json", nil, nameOrID)
if err != nil {
return &inspect, err
}
return &inspect, response.Process(&inspect)
}
func List() error {
// TODO
// The API side of things for this one does a lot in main and therefore
// is not implemented yet.
return bindings.ErrNotImplemented // nolint:typecheck
}
// Prune removes unused volumes from the local filesystem.
func Prune(ctx context.Context) ([]string, error) {
var (
pruned []string
)
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return nil, err
}
response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/prune", nil)
if err != nil {
return pruned, err
}
return pruned, response.Process(&pruned)
}
// Remove deletes the given volume from storage. The optional force parameter
// is used to remove a volume even if it is being used by a container.
func Remove(ctx context.Context, nameOrID string, force *bool) error {
conn, err := bindings.GetConnectionFromContext(ctx)
if err != nil {
return err
}
params := make(map[string]string)
if force != nil {
params["force"] = strconv.FormatBool(*force)
}
response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/%s/prune", params, nameOrID)
if err != nil {
return err
}
return response.Process(nil)
}