summaryrefslogtreecommitdiff
path: root/pkg/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/bindings')
-rw-r--r--pkg/bindings/connection.go234
-rw-r--r--pkg/bindings/containers/containers.go24
-rw-r--r--pkg/bindings/containers/create.go2
-rw-r--r--pkg/bindings/containers/healthcheck.go2
-rw-r--r--pkg/bindings/containers/mount.go6
-rw-r--r--pkg/bindings/images/images.go18
-rw-r--r--pkg/bindings/images/search.go2
-rw-r--r--pkg/bindings/network/network.go6
-rw-r--r--pkg/bindings/pods/pods.go34
-rw-r--r--pkg/bindings/test/common_test.go30
-rw-r--r--pkg/bindings/test/images_test.go4
-rw-r--r--pkg/bindings/test/pods_test.go202
-rw-r--r--pkg/bindings/volumes/volumes.go8
13 files changed, 459 insertions, 113 deletions
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index f270060a6..75f1fc6a5 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -1,22 +1,34 @@
package bindings
import (
+ "bufio"
"context"
"fmt"
"io"
+ "io/ioutil"
"net"
"net/http"
"net/url"
+ "os"
"path/filepath"
+ "strconv"
"strings"
+ "time"
"github.com/containers/libpod/pkg/api/handlers"
jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/crypto/ssh"
+ "k8s.io/client-go/util/homedir"
)
var (
- defaultConnectionPath string = filepath.Join(fmt.Sprintf("v%s", handlers.MinimalApiVersion), "libpod")
+ basePath = &url.URL{
+ Scheme: "http",
+ Host: "d",
+ Path: "/v" + handlers.MinimalApiVersion + "/libpod",
+ }
)
type APIResponse struct {
@@ -25,9 +37,28 @@ type APIResponse struct {
}
type Connection struct {
- scheme string
- address string
- client *http.Client
+ _url *url.URL
+ client *http.Client
+}
+
+type valueKey string
+
+const (
+ clientKey = valueKey("client")
+)
+
+// GetClient from context build by NewConnection()
+func GetClient(ctx context.Context) (*Connection, error) {
+ c, ok := ctx.Value(clientKey).(*Connection)
+ if !ok {
+ return nil, errors.Errorf("ClientKey not set in context")
+ }
+ return c, nil
+}
+
+// JoinURL elements with '/'
+func JoinURL(elements ...string) string {
+ return strings.Join(elements, "/")
}
// NewConnection takes a URI as a string and returns a context with the
@@ -36,46 +67,81 @@ type Connection struct {
//
// 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
+// or unix:///run/podman/podman.sock
+// or ssh://<user>@<host>[:port]/run/podman/podman.sock?secure=True
+func NewConnection(ctx context.Context, uri string, identity ...string) (context.Context, error) {
+ var (
+ err error
+ secure bool
+ )
+ if v, found := os.LookupEnv("PODMAN_HOST"); found {
+ uri = v
}
- if u.Scheme != "tcp" && u.Scheme != "unix" {
- return nil, errors.Errorf("%s is not a support schema", u.Scheme)
+
+ if v, found := os.LookupEnv("PODMAN_SSHKEY"); found {
+ identity = []string{v}
}
- if u.Scheme == "tcp" && !strings.HasPrefix(uri, "tcp://") {
- return nil, errors.New("tcp URIs should begin with tcp://")
+ _url, err := url.Parse(uri)
+ if err != nil {
+ return nil, errors.Wrapf(err, "Value of PODMAN_HOST is not a valid url: %s", uri)
}
- address := u.Path
- if u.Scheme == "tcp" {
- address = u.Host
+ // Now we setup the http client to use the connection above
+ var client *http.Client
+ switch _url.Scheme {
+ case "ssh":
+ secure, err = strconv.ParseBool(_url.Query().Get("secure"))
+ if err != nil {
+ secure = false
+ }
+ client, err = sshClient(_url, identity[0], secure)
+ case "unix":
+ if !strings.HasPrefix(uri, "unix:///") {
+ // autofix unix://path_element vs unix:///path_element
+ _url.Path = JoinURL(_url.Host, _url.Path)
+ _url.Host = ""
+ }
+ client, err = unixClient(_url)
+ case "tcp":
+ if !strings.HasPrefix(uri, "tcp://") {
+ return nil, errors.New("tcp URIs should begin with tcp://")
+ }
+ client, err = tcpClient(_url)
+ default:
+ return nil, errors.Errorf("%s is not a support schema", _url.Scheme)
+ }
+ if err != nil {
+ return nil, errors.Wrapf(err, "Failed to create %sClient", _url.Scheme)
}
- newConn := newConnection(u.Scheme, address)
- ctx := context.WithValue(context.Background(), "conn", &newConn)
+
+ ctx = context.WithValue(ctx, clientKey, &Connection{_url, client})
if err := pingNewConnection(ctx); err != nil {
return nil, err
}
return ctx, nil
}
+func tcpClient(_url *url.URL) (*http.Client, error) {
+ return &http.Client{
+ Transport: &http.Transport{
+ DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
+ return net.Dial("tcp", _url.Path)
+ },
+ DisableCompression: true,
+ },
+ }, 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)
+ client, err := GetClient(ctx)
if err != nil {
return err
}
// the ping endpoint sits at / in this case
- response, err := conn.DoRequest(nil, http.MethodGet, "../../../_ping", nil)
+ response, err := client.DoRequest(nil, http.MethodGet, "../../../_ping", nil)
if err != nil {
return err
}
@@ -85,26 +151,58 @@ func pingNewConnection(ctx context.Context) error {
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)
+func sshClient(_url *url.URL, identity string, secure bool) (*http.Client, error) {
+ auth, err := publicKey(identity)
+ if err != nil {
+ return nil, errors.Wrapf(err, "Failed to parse identity %s: %v\n", _url.String(), identity)
+ }
+
+ callback := ssh.InsecureIgnoreHostKey()
+ if secure {
+ key := hostKey(_url.Hostname())
+ if key != nil {
+ callback = ssh.FixedHostKey(key)
+ }
+ }
+
+ bastion, err := ssh.Dial("tcp",
+ net.JoinHostPort(_url.Hostname(), _url.Port()),
+ &ssh.ClientConfig{
+ User: _url.User.Username(),
+ Auth: []ssh.AuthMethod{auth},
+ HostKeyCallback: callback,
+ HostKeyAlgorithms: []string{
+ ssh.KeyAlgoRSA,
+ ssh.KeyAlgoDSA,
+ ssh.KeyAlgoECDSA256,
+ ssh.KeyAlgoECDSA384,
+ ssh.KeyAlgoECDSA521,
+ ssh.KeyAlgoED25519,
},
+ Timeout: 5 * time.Second,
},
+ )
+ if err != nil {
+ return nil, errors.Wrapf(err, "Connection to bastion host (%s) failed.", _url.String())
}
- newConn := Connection{
- client: &client,
- address: address,
- scheme: scheme,
- }
- return newConn
+ return &http.Client{
+ Transport: &http.Transport{
+ DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
+ return bastion.Dial("unix", _url.Path)
+ },
+ }}, nil
}
-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 unixClient(_url *url.URL) (*http.Client, error) {
+ return &http.Client{
+ Transport: &http.Transport{
+ DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
+ d := net.Dialer{}
+ return d.DialContext(ctx, "unix", _url.Path)
+ },
+ DisableCompression: true,
+ },
+ }, nil
}
// DoRequest assembles the http request and returns the response
@@ -121,7 +219,7 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string,
// Lets eventually use URL for this which might lead to safer
// usage
safeEndpoint := fmt.Sprintf(endpoint, safePathValues...)
- e := c.makeEndpoint(safeEndpoint)
+ e := basePath.String() + safeEndpoint
req, err := http.NewRequest(httpMethod, e, httpBody)
if err != nil {
return nil, err
@@ -140,21 +238,11 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string,
if err == nil {
break
}
+ time.Sleep(time.Duration(i*100) * time.Millisecond)
}
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
-}
-
// FiltersToString converts our typical filter format of a
// map[string][]string to a query/html safe string.
func FiltersToString(filters map[string][]string) (string, error) {
@@ -189,3 +277,45 @@ func (h *APIResponse) IsClientError() bool {
func (h *APIResponse) IsServerError() bool {
return h.Response.StatusCode/100 == 5
}
+
+func publicKey(path string) (ssh.AuthMethod, error) {
+ key, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+
+ signer, err := ssh.ParsePrivateKey(key)
+ if err != nil {
+ return nil, err
+ }
+
+ return ssh.PublicKeys(signer), nil
+}
+
+func hostKey(host string) ssh.PublicKey {
+ // parse OpenSSH known_hosts file
+ // ssh or use ssh-keyscan to get initial key
+ known_hosts := filepath.Join(homedir.HomeDir(), ".ssh", "known_hosts")
+ fd, err := os.Open(known_hosts)
+ if err != nil {
+ logrus.Error(err)
+ return nil
+ }
+
+ scanner := bufio.NewScanner(fd)
+ for scanner.Scan() {
+ _, hosts, key, _, _, err := ssh.ParseKnownHosts(scanner.Bytes())
+ if err != nil {
+ logrus.Errorf("Failed to parse known_hosts: %s", scanner.Text())
+ continue
+ }
+
+ for _, h := range hosts {
+ if h == host {
+ return key
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index 04f7f8802..a437e9a9b 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -16,7 +16,7 @@ import (
// 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, all *bool, last *int, pod, size, sync *bool) ([]lpapiv2.ListContainer, error) { // nolint:typecheck
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -59,7 +59,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
var (
pruneResponse []string
)
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -82,7 +82,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -105,7 +105,7 @@ func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -125,7 +125,7 @@ func Inspect(ctx context.Context, nameOrID string, size *bool) (*libpod.InspectC
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -143,7 +143,7 @@ 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -158,7 +158,7 @@ func Pause(ctx context.Context, nameOrID string) error {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -177,7 +177,7 @@ func Restart(ctx context.Context, nameOrID string, timeout *int) error {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -198,7 +198,7 @@ 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -213,7 +213,7 @@ func Unpause(ctx context.Context, nameOrID string) error {
// or a partial/full ID.
func Wait(ctx context.Context, nameOrID string) (int32, error) {
var exitCode int32
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return exitCode, err
}
@@ -228,7 +228,7 @@ func Wait(ctx context.Context, nameOrID string) (int32, error) {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return false, err
}
@@ -243,7 +243,7 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
diff --git a/pkg/bindings/containers/create.go b/pkg/bindings/containers/create.go
index 18b32335b..2943cb522 100644
--- a/pkg/bindings/containers/create.go
+++ b/pkg/bindings/containers/create.go
@@ -13,7 +13,7 @@ import (
func CreateWithSpec(ctx context.Context, s specgen.SpecGenerator) (utils.ContainerCreateResponse, error) {
var ccr utils.ContainerCreateResponse
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return ccr, err
}
diff --git a/pkg/bindings/containers/healthcheck.go b/pkg/bindings/containers/healthcheck.go
index 9ed7f858d..dc607c1b3 100644
--- a/pkg/bindings/containers/healthcheck.go
+++ b/pkg/bindings/containers/healthcheck.go
@@ -11,7 +11,7 @@ import (
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/containers/mount.go b/pkg/bindings/containers/mount.go
index d68dee981..e0627d9a3 100644
--- a/pkg/bindings/containers/mount.go
+++ b/pkg/bindings/containers/mount.go
@@ -10,7 +10,7 @@ import (
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return "", err
}
@@ -27,7 +27,7 @@ func Mount(ctx context.Context, nameOrID string) (string, error) {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -40,7 +40,7 @@ func Unmount(ctx context.Context, nameOrID string) error {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index b19482943..271d58952 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -14,7 +14,7 @@ import (
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return false, err
}
@@ -29,7 +29,7 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -54,7 +54,7 @@ func List(ctx context.Context, all *bool, filters map[string][]string) ([]*handl
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -77,7 +77,7 @@ func ImageTree(ctx context.Context, nameOrId string) error {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -89,7 +89,7 @@ func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse,
}
func Load(ctx context.Context, r io.Reader) error {
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -103,7 +103,7 @@ func Load(ctx context.Context, r io.Reader) error {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -121,7 +121,7 @@ func Remove(ctx context.Context, nameOrID string, force *bool) ([]map[string]str
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -149,7 +149,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
var (
deleted []string
)
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -170,7 +170,7 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
diff --git a/pkg/bindings/images/search.go b/pkg/bindings/images/search.go
index 58b25425b..dca1b0e63 100644
--- a/pkg/bindings/images/search.go
+++ b/pkg/bindings/images/search.go
@@ -16,7 +16,7 @@ func Search(ctx context.Context, term string, limit *int, filters map[string][]s
var (
searchResults []image.SearchResult
)
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go
index 97bbb8c42..c95b22953 100644
--- a/pkg/bindings/network/network.go
+++ b/pkg/bindings/network/network.go
@@ -10,7 +10,7 @@ import (
func Create() {}
func Inspect(ctx context.Context, nameOrID string) (map[string]interface{}, error) {
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -23,7 +23,7 @@ func Inspect(ctx context.Context, nameOrID string) (map[string]interface{}, erro
}
func Remove(ctx context.Context, nameOrID string) error {
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -38,7 +38,7 @@ func List(ctx context.Context) ([]*libcni.NetworkConfigList, error) {
var (
netList []*libcni.NetworkConfigList
)
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go
index d079f01c2..838b22e43 100644
--- a/pkg/bindings/pods/pods.go
+++ b/pkg/bindings/pods/pods.go
@@ -16,7 +16,7 @@ func CreatePod() error {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return false, err
}
@@ -29,7 +29,7 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) {
// Inspect returns low-level information about the given pod.
func Inspect(ctx context.Context, nameOrID string) (*libpod.PodInspect, error) {
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -44,7 +44,7 @@ func Inspect(ctx context.Context, nameOrID string) (*libpod.PodInspect, error) {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -61,7 +61,7 @@ func Kill(ctx context.Context, nameOrID string, signal *string) error {
// Pause pauses all running containers in a given pod.
func Pause(ctx context.Context, nameOrID string) error {
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -74,7 +74,7 @@ func Pause(ctx context.Context, nameOrID string) error {
// Prune removes all non-running pods in local storage.
func Prune(ctx context.Context) error {
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -87,11 +87,11 @@ func Prune(ctx context.Context) error {
// 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) {
+func List(ctx context.Context, filters map[string][]string) ([]*libpod.PodInspect, error) {
var (
- inspect []libpod.PodInspect
+ inspect []*libpod.PodInspect
)
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -103,16 +103,16 @@ func List(ctx context.Context, filters map[string][]string) (*[]libpod.PodInspec
}
params["filters"] = stringFilter
}
- response, err := conn.DoRequest(nil, http.MethodPost, "/pods/json", params)
+ response, err := conn.DoRequest(nil, http.MethodGet, "/pods/json", params)
if err != nil {
- return &inspect, err
+ return inspect, err
}
- return &inspect, response.Process(&inspect)
+ 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -126,7 +126,7 @@ func Restart(ctx context.Context, nameOrID string) error {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -143,11 +143,11 @@ func Remove(ctx context.Context, nameOrID string, force *bool) error {
// Start starts all containers in a pod.
func Start(ctx context.Context, nameOrID string) error {
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
- response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s/start", nil, nameOrID)
+ response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/start", nil, nameOrID)
if err != nil {
return err
}
@@ -162,7 +162,7 @@ func Stats() error {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
@@ -184,7 +184,7 @@ func Top() error {
// Unpause unpauses all paused containers in a Pod.
func Unpause(ctx context.Context, nameOrID string) error {
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go
index dba94cb35..98d64bbaa 100644
--- a/pkg/bindings/test/common_test.go
+++ b/pkg/bindings/test/common_test.go
@@ -114,7 +114,7 @@ func newBindingTest() *bindingTest {
runRoot: filepath.Join(tmpPath, "run"),
artifactDirPath: "",
imageCacheDir: "",
- sock: fmt.Sprintf("unix:%s", filepath.Join(tmpPath, "api.sock")),
+ sock: fmt.Sprintf("unix://%s", filepath.Join(tmpPath, "api.sock")),
tempDirPath: tmpPath,
}
return &b
@@ -162,16 +162,30 @@ func (b *bindingTest) restoreImageFromCache(i testImage) {
p.Wait(45)
}
-// Run a container and add append the alpine image to it
-func (b *bindingTest) RunTopContainer(name *string) {
+// Run a container within or without a pod
+// and add or append the alpine image to it
+func (b *bindingTest) RunTopContainer(containerName *string, insidePod *bool, podName *string) {
cmd := []string{"run", "-dt"}
- if name != nil {
- containerName := *name
- cmd = append(cmd, "--name", containerName)
+ if *insidePod && podName != nil {
+ pName := *podName
+ cmd = append(cmd, "--pod", pName)
+ } else if containerName != nil {
+ cName := *containerName
+ cmd = append(cmd, "--name", cName)
}
cmd = append(cmd, alpine.name, "top")
- p := b.runPodman(cmd)
- p.Wait(45)
+ b.runPodman(cmd).Wait(45)
+}
+
+// This method creates a pod with the given pod name.
+// Podname is an optional parameter
+func (b *bindingTest) Podcreate(name *string) {
+ if name != nil {
+ podname := *name
+ b.runPodman([]string{"pod", "create", "--name", podname}).Wait(45)
+ } else {
+ b.runPodman([]string{"pod", "create"}).Wait(45)
+ }
}
// StringInSlice returns a boolean based on whether a given
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index 74e0cc67a..0b51c8c9e 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -38,7 +38,7 @@ var _ = Describe("Podman images", func() {
bt.RestoreImagesFromCache()
s = bt.startAPIService()
time.Sleep(1 * time.Second)
- connText, err = bindings.NewConnection(bt.sock)
+ connText, err = bindings.NewConnection(context.Background(), bt.sock)
Expect(err).To(BeNil())
})
@@ -93,7 +93,7 @@ var _ = Describe("Podman images", func() {
// Start a container with alpine image
var top string = "top"
- bt.RunTopContainer(&top)
+ bt.RunTopContainer(&top, &falseFlag, nil)
// we should now have a container called "top" running
containerResponse, err := containers.Inspect(connText, "top", &falseFlag)
Expect(err).To(BeNil())
diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go
new file mode 100644
index 000000000..76ccd10f2
--- /dev/null
+++ b/pkg/bindings/test/pods_test.go
@@ -0,0 +1,202 @@
+package test_bindings
+
+import (
+ "context"
+ "net/http"
+ "time"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/bindings/pods"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe("Podman images", func() {
+ var (
+ bt *bindingTest
+ s *gexec.Session
+ connText context.Context
+ newpod string
+ err error
+ trueFlag bool = true
+ )
+
+ BeforeEach(func() {
+ bt = newBindingTest()
+ newpod = "newpod"
+ bt.RestoreImagesFromCache()
+ bt.Podcreate(&newpod)
+ s = bt.startAPIService()
+ time.Sleep(1 * time.Second)
+ connText, err = bindings.NewConnection(bt.sock)
+ Expect(err).To(BeNil())
+ })
+
+ AfterEach(func() {
+ s.Kill()
+ bt.cleanup()
+ })
+
+ It("inspect pod", func() {
+ //Inspect an invalid pod name
+ _, err := pods.Inspect(connText, "dummyname")
+ Expect(err).ToNot(BeNil())
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusNotFound))
+
+ //Inspect an valid pod name
+ response, err := pods.Inspect(connText, newpod)
+ Expect(err).To(BeNil())
+ Expect(response.Config.Name).To(Equal(newpod))
+ })
+
+ // Test validates the list all api returns
+ It("list pod", func() {
+ //List all the pods in the current instance
+ podSummary, err := pods.List(connText, nil)
+ Expect(err).To(BeNil())
+ Expect(len(podSummary)).To(Equal(1))
+ // Adding an alpine container to the existing pod
+ bt.RunTopContainer(nil, &trueFlag, &newpod)
+ podSummary, err = pods.List(connText, nil)
+ // Verify no errors.
+ Expect(err).To(BeNil())
+ // Verify number of containers in the pod.
+ Expect(len(podSummary[0].Containers)).To(Equal(2))
+
+ // Add multiple pods and verify them by name and size.
+ var newpod2 string = "newpod2"
+ bt.Podcreate(&newpod2)
+ podSummary, err = pods.List(connText, nil)
+ Expect(len(podSummary)).To(Equal(2))
+ var names []string
+ for _, i := range podSummary {
+ names = append(names, i.Config.Name)
+ }
+ Expect(StringInSlice(newpod, names)).To(BeTrue())
+ Expect(StringInSlice("newpod2", names)).To(BeTrue())
+
+ // Not working Because: code to list based on filter
+ // "not yet implemented",
+ // Validate list pod with filters
+ filters := make(map[string][]string)
+ filters["name"] = []string{newpod}
+ filteredPods, err := pods.List(connText, filters)
+ Expect(err).To(BeNil())
+ Expect(len(filteredPods)).To(BeNumerically("==", 1))
+ })
+
+ // The test validates if the exists responds
+ It("exists pod", func() {
+ response, err := pods.Exists(connText, "dummyName")
+ Expect(err).To(BeNil())
+ Expect(response).To(BeFalse())
+
+ // Should exit with no error and response should be true
+ response, err = pods.Exists(connText, "newpod")
+ Expect(err).To(BeNil())
+ Expect(response).To(BeTrue())
+ })
+
+ // This test validates if All running containers within
+ // each specified pod are paused and unpaused
+ It("pause upause pod", func() {
+ // Pause invalid container
+ err := pods.Pause(connText, "dummyName")
+ Expect(err).ToNot(BeNil())
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusNotFound))
+
+ // Adding an alpine container to the existing pod
+ bt.RunTopContainer(nil, &trueFlag, &newpod)
+ response, err := pods.Inspect(connText, newpod)
+ Expect(err).To(BeNil())
+
+ // Binding needs to be modified to inspect the pod state.
+ // Since we dont have a pod state we inspect the states of the containers within the pod.
+ // Pause a valid container
+ err = pods.Pause(connText, newpod)
+ Expect(err).To(BeNil())
+ response, err = pods.Inspect(connText, newpod)
+ for _, i := range response.Containers {
+ Expect(define.StringToContainerStatus(i.State)).
+ To(Equal(define.ContainerStatePaused))
+ }
+
+ // Unpause a valid container
+ err = pods.Unpause(connText, newpod)
+ Expect(err).To(BeNil())
+ response, err = pods.Inspect(connText, newpod)
+ for _, i := range response.Containers {
+ Expect(define.StringToContainerStatus(i.State)).
+ To(Equal(define.ContainerStateRunning))
+ }
+ })
+
+ It("start stop restart pod", func() {
+ // Start an invalid pod
+ err = pods.Start(connText, "dummyName")
+ Expect(err).ToNot(BeNil())
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusNotFound))
+
+ // Stop an invalid pod
+ err = pods.Stop(connText, "dummyName", nil)
+ Expect(err).ToNot(BeNil())
+ code, _ = bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusNotFound))
+
+ // Restart an invalid pod
+ err = pods.Restart(connText, "dummyName")
+ Expect(err).ToNot(BeNil())
+ code, _ = bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusNotFound))
+
+ // Start a valid pod and inspect status of each container
+ err = pods.Start(connText, newpod)
+ Expect(err).To(BeNil())
+
+ response, err := pods.Inspect(connText, newpod)
+ for _, i := range response.Containers {
+ Expect(define.StringToContainerStatus(i.State)).
+ To(Equal(define.ContainerStateRunning))
+ }
+
+ // Start a already running container
+ // (Test fails for now needs to be fixed)
+ err = pods.Start(connText, newpod)
+ Expect(err).ToNot(BeNil())
+ code, _ = bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusNotModified))
+
+ // Stop the running pods
+ err = pods.Stop(connText, newpod, nil)
+ Expect(err).To(BeNil())
+ response, _ = pods.Inspect(connText, newpod)
+ for _, i := range response.Containers {
+ Expect(define.StringToContainerStatus(i.State)).
+ To(Equal(define.ContainerStateStopped))
+ }
+
+ // Stop a already running pod
+ // (Test fails for now needs to be fixed)
+ err = pods.Stop(connText, newpod, nil)
+ Expect(err).ToNot(BeNil())
+ code, _ = bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusNotModified))
+
+ err = pods.Restart(connText, newpod)
+ Expect(err).To(BeNil())
+ response, _ = pods.Inspect(connText, newpod)
+ for _, i := range response.Containers {
+ Expect(define.StringToContainerStatus(i.State)).
+ To(Equal(define.ContainerStateRunning))
+ }
+ })
+
+ // Remove all stopped pods and their container to be implemented.
+ It("prune pod", func() {
+ })
+})
diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go
index 05a4f73fd..8313a7460 100644
--- a/pkg/bindings/volumes/volumes.go
+++ b/pkg/bindings/volumes/volumes.go
@@ -16,7 +16,7 @@ func Create(ctx context.Context, config handlers.VolumeCreateConfig) (string, er
var (
volumeID string
)
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return "", err
}
@@ -32,7 +32,7 @@ func Inspect(ctx context.Context, nameOrID string) (*libpod.InspectVolumeData, e
var (
inspect libpod.InspectVolumeData
)
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -55,7 +55,7 @@ func Prune(ctx context.Context) ([]string, error) {
var (
pruned []string
)
- conn, err := bindings.GetConnectionFromContext(ctx)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
@@ -69,7 +69,7 @@ func Prune(ctx context.Context) ([]string, error) {
// 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)
+ conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}