mirror of
https://github.com/containers/podman.git
synced 2025-06-21 01:19:15 +08:00
Merge pull request #4980 from baude/bindingdocs
[CI:DOCS]Binding overhauls
This commit is contained in:
2
Makefile
2
Makefile
@ -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)" \
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}{
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
255
pkg/bindings/containers/containers.go
Normal file
255
pkg/bindings/containers/containers.go
Normal 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)
|
||||
}
|
26
pkg/bindings/containers/healthcheck.go
Normal file
26
pkg/bindings/containers/healthcheck.go
Normal 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)
|
||||
}
|
53
pkg/bindings/containers/mount.go
Normal file
53
pkg/bindings/containers/mount.go
Normal 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)
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
package bindings
|
||||
|
||||
func (c Connection) GenerateKube() {}
|
||||
func (c Connection) GenerateSystemd() {}
|
4
pkg/bindings/generate/generate.go
Normal file
4
pkg/bindings/generate/generate.go
Normal file
@ -0,0 +1,4 @@
|
||||
package generate
|
||||
|
||||
func GenerateKube() {}
|
||||
func GenerateSystemd() {}
|
@ -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)
|
||||
}
|
@ -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) {}
|
187
pkg/bindings/images/images.go
Normal file
187
pkg/bindings/images/images.go
Normal 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) {}
|
40
pkg/bindings/images/search.go
Normal file
40
pkg/bindings/images/search.go
Normal 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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
50
pkg/bindings/network/network.go
Normal file
50
pkg/bindings/network/network.go
Normal 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)
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package bindings
|
||||
|
||||
func (c Connection) PlayKube() {}
|
7
pkg/bindings/play/play.go
Normal file
7
pkg/bindings/play/play.go
Normal file
@ -0,0 +1,7 @@
|
||||
package play
|
||||
|
||||
import "github.com/containers/libpod/pkg/bindings"
|
||||
|
||||
func PlayKube() error {
|
||||
return bindings.ErrNotImplemented
|
||||
}
|
@ -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
196
pkg/bindings/pods/pods.go
Normal 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)
|
||||
}
|
@ -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)
|
||||
}
|
112
pkg/bindings/test/common_test.go
Normal file
112
pkg/bindings/test/common_test.go
Normal 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)
|
||||
}
|
||||
}
|
92
pkg/bindings/test/images_test.go
Normal file
92
pkg/bindings/test/images_test.go
Normal 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
|
||||
|
||||
})
|
||||
|
||||
})
|
@ -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)
|
||||
}
|
85
pkg/bindings/volumes/volumes.go
Normal file
85
pkg/bindings/volumes/volumes.go
Normal 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)
|
||||
}
|
Reference in New Issue
Block a user