summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/compat/containers_attach.go35
-rw-r--r--pkg/api/handlers/compat/images_search.go1
-rw-r--r--pkg/api/handlers/libpod/images.go53
-rw-r--r--pkg/api/server/register_containers.go8
-rw-r--r--pkg/api/server/register_images.go2
-rw-r--r--pkg/bindings/images/images.go33
-rw-r--r--pkg/bindings/images/search.go41
-rw-r--r--pkg/bindings/test/images_test.go21
-rw-r--r--pkg/cgroups/pids.go2
-rw-r--r--pkg/domain/entities/engine_image.go1
-rw-r--r--pkg/domain/entities/images.go31
-rw-r--r--pkg/domain/infra/abi/images.go35
-rw-r--r--pkg/domain/infra/tunnel/images.go4
13 files changed, 192 insertions, 75 deletions
diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go
index da7b5bb0c..80ad52aee 100644
--- a/pkg/api/handlers/compat/containers_attach.go
+++ b/pkg/api/handlers/compat/containers_attach.go
@@ -1,6 +1,7 @@
package compat
import (
+ "fmt"
"net/http"
"github.com/containers/libpod/libpod"
@@ -23,7 +24,9 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
Stdin bool `schema:"stdin"`
Stdout bool `schema:"stdout"`
Stderr bool `schema:"stderr"`
- }{}
+ }{
+ Stream: true,
+ }
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, "Error parsing parameters", http.StatusBadRequest, err)
return
@@ -61,16 +64,9 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
return
}
- // TODO: Investigate supporting these.
- // Logs replays container logs over the attach socket.
- // Stream seems to break things up somehow? Not 100% clear.
- if query.Logs {
- utils.Error(w, "Unsupported parameter", http.StatusBadRequest, errors.Errorf("the logs parameter to attach is not presently supported"))
- return
- }
- // We only support stream=true or unset
- if _, found := r.URL.Query()["stream"]; found && query.Stream {
- utils.Error(w, "Unsupported parameter", http.StatusBadRequest, errors.Errorf("the stream parameter to attach is not presently supported"))
+ // At least one of these must be set
+ if !query.Stream && !query.Logs {
+ utils.Error(w, "Unsupported parameter", http.StatusBadRequest, errors.Errorf("at least one of Logs or Stream must be set"))
return
}
@@ -86,7 +82,13 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- if !(state == define.ContainerStateCreated || state == define.ContainerStateRunning) {
+ // For Docker compatibility, we need to re-initialize containers in these states.
+ if state == define.ContainerStateConfigured || state == define.ContainerStateExited {
+ if err := ctr.Init(r.Context()); err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "error preparing container %s for attach", ctr.ID()))
+ return
+ }
+ } else if !(state == define.ContainerStateCreated || state == define.ContainerStateRunning) {
utils.InternalServerError(w, errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers"))
return
}
@@ -98,20 +100,23 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
return
}
- w.WriteHeader(http.StatusSwitchingProtocols)
-
connection, buffer, err := hijacker.Hijack()
if err != nil {
utils.InternalServerError(w, errors.Wrapf(err, "error hijacking connection"))
return
}
+ // This header string sourced from Docker:
+ // https://raw.githubusercontent.com/moby/moby/b95fad8e51bd064be4f4e58a996924f343846c85/api/server/router/container/container_routes.go
+ // Using literally to ensure compatability with existing clients.
+ fmt.Fprintf(connection, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
+
logrus.Debugf("Hijack for attach of container %s successful", ctr.ID())
// Perform HTTP attach.
// HTTPAttach will handle everything about the connection from here on
// (including closing it and writing errors to it).
- if err := ctr.HTTPAttach(connection, buffer, streams, detachKeys, nil); err != nil {
+ if err := ctr.HTTPAttach(connection, buffer, streams, detachKeys, nil, query.Stream, query.Logs); err != nil {
// We can't really do anything about errors anymore. HTTPAttach
// should be writing them to the connection.
logrus.Errorf("Error attaching to container %s: %v", ctr.ID(), err)
diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go
index 7283b22c4..8da685527 100644
--- a/pkg/api/handlers/compat/images_search.go
+++ b/pkg/api/handlers/compat/images_search.go
@@ -57,6 +57,7 @@ func SearchImages(w http.ResponseWriter, r *http.Request) {
Filter: filter,
Limit: query.Limit,
}
+
results, err := image.SearchImages(query.Term, options)
if err != nil {
utils.BadRequest(w, "term", query.Term, err)
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 850de4598..a42d06205 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -645,3 +645,56 @@ func UntagImage(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusCreated, "")
}
+
+func SearchImages(w http.ResponseWriter, r *http.Request) {
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Term string `json:"term"`
+ Limit int `json:"limit"`
+ Filters []string `json:"filters"`
+ TLSVerify bool `json:"tlsVerify"`
+ }{
+ // This is where you can override the golang default value for one of fields
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ options := image.SearchOptions{
+ Limit: query.Limit,
+ }
+ if _, found := r.URL.Query()["tlsVerify"]; found {
+ options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
+ }
+
+ if _, found := r.URL.Query()["filters"]; found {
+ filter, err := image.ParseSearchFilter(query.Filters)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse filters parameter for %s", r.URL.String()))
+ return
+ }
+ options.Filter = *filter
+ }
+
+ searchResults, err := image.SearchImages(query.Term, options)
+ if err != nil {
+ utils.BadRequest(w, "term", query.Term, err)
+ return
+ }
+ // Convert from image.SearchResults to entities.ImageSearchReport. We don't
+ // want to leak any low-level packages into the remote client, which
+ // requires converting.
+ reports := make([]entities.ImageSearchReport, len(searchResults))
+ for i := range searchResults {
+ reports[i].Index = searchResults[i].Index
+ reports[i].Name = searchResults[i].Name
+ reports[i].Description = searchResults[i].Index
+ reports[i].Stars = searchResults[i].Stars
+ reports[i].Official = searchResults[i].Official
+ reports[i].Automated = searchResults[i].Automated
+ }
+
+ utils.WriteResponse(w, http.StatusOK, reports)
+}
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index e39e43bfb..8b9a9e312 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -517,13 +517,13 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// name: logs
// required: false
// type: boolean
- // description: Not yet supported
+ // description: Stream all logs from the container across the connection. Happens before streaming attach (if requested). At least one of logs or stream must be set
// - in: query
// name: stream
// required: false
// type: boolean
// default: true
- // description: If passed, must be set to true; stream=false is not yet supported
+ // description: Attach to the container. If unset, and logs is set, only the container's logs will be sent. At least one of stream or logs must be set
// - in: query
// name: stdout
// required: false
@@ -1194,13 +1194,13 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// name: logs
// required: false
// type: boolean
- // description: Not yet supported
+ // description: Stream all logs from the container across the connection. Happens before streaming attach (if requested). At least one of logs or stream must be set
// - in: query
// name: stream
// required: false
// type: boolean
// default: true
- // description: If passed, must be set to true; stream=false is not yet supported
+ // description: Attach to the container. If unset, and logs is set, only the container's logs will be sent. At least one of stream or logs must be set
// - in: query
// name: stdout
// required: false
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 77560e789..7dd887037 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -919,7 +919,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: "#/responses/DocsSearchResponse"
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(compat.SearchImages)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(libpod.SearchImages)).Methods(http.MethodGet)
// swagger:operation DELETE /libpod/images/{name:.*} libpod libpodRemoveImage
// ---
// tags:
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index 1b3df609b..3550c3968 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -2,7 +2,6 @@ package images
import (
"context"
- "errors"
"fmt"
"io"
"net/http"
@@ -13,6 +12,7 @@ import (
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
)
// Exists a lightweight way to determine if an image exists in local storage. It returns a
@@ -308,3 +308,34 @@ func Push(ctx context.Context, source string, destination string, options entiti
_, err = conn.DoRequest(nil, http.MethodPost, path, params)
return err
}
+
+// Search is the binding for libpod's v2 endpoints for Search images.
+func Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+ params.Set("term", term)
+ params.Set("limit", strconv.Itoa(opts.Limit))
+ for _, f := range opts.Filters {
+ params.Set("filters", f)
+ }
+
+ if opts.TLSVerify != types.OptionalBoolUndefined {
+ val := bool(opts.TLSVerify == types.OptionalBoolTrue)
+ params.Set("tlsVerify", strconv.FormatBool(val))
+ }
+
+ response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params)
+ if err != nil {
+ return nil, err
+ }
+
+ results := []entities.ImageSearchReport{}
+ if err := response.Process(&results); err != nil {
+ return nil, err
+ }
+
+ return results, nil
+}
diff --git a/pkg/bindings/images/search.go b/pkg/bindings/images/search.go
deleted file mode 100644
index 183ff3d77..000000000
--- a/pkg/bindings/images/search.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package images
-
-import (
- "context"
- "net/http"
- "net/url"
- "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.GetClient(ctx)
- if err != nil {
- return nil, err
- }
- params := url.Values{}
- params.Set("term", term)
- if limit != nil {
- params.Set("limit", strconv.Itoa(*limit))
- }
- if filters != nil {
- stringFilter, err := bindings.FiltersToString(filters)
- if err != nil {
- return nil, err
- }
- params.Set("filters", stringFilter)
- }
- response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params)
- if err != nil {
- return searchResults, nil
- }
- return searchResults, response.Process(&searchResults)
-}
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index 992720196..58210efd0 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -314,11 +314,11 @@ var _ = Describe("Podman images", func() {
})
It("Search for an image", func() {
- imgs, err := images.Search(bt.conn, "alpine", nil, nil)
+ reports, err := images.Search(bt.conn, "alpine", entities.ImageSearchOptions{})
Expect(err).To(BeNil())
- Expect(len(imgs)).To(BeNumerically(">", 1))
+ Expect(len(reports)).To(BeNumerically(">", 1))
var foundAlpine bool
- for _, i := range imgs {
+ for _, i := range reports {
if i.Name == "docker.io/library/alpine" {
foundAlpine = true
break
@@ -327,23 +327,20 @@ var _ = Describe("Podman images", func() {
Expect(foundAlpine).To(BeTrue())
// Search for alpine with a limit of 10
- ten := 10
- imgs, err = images.Search(bt.conn, "docker.io/alpine", &ten, nil)
+ reports, err = images.Search(bt.conn, "docker.io/alpine", entities.ImageSearchOptions{Limit: 10})
Expect(err).To(BeNil())
- Expect(len(imgs)).To(BeNumerically("<=", 10))
+ Expect(len(reports)).To(BeNumerically("<=", 10))
// Search for alpine with stars greater than 100
- filters := make(map[string][]string)
- filters["stars"] = []string{"100"}
- imgs, err = images.Search(bt.conn, "docker.io/alpine", nil, filters)
+ reports, err = images.Search(bt.conn, "docker.io/alpine", entities.ImageSearchOptions{Filters: []string{"stars=100"}})
Expect(err).To(BeNil())
- for _, i := range imgs {
+ for _, i := range reports {
Expect(i.Stars).To(BeNumerically(">=", 100))
}
// Search with a fqdn
- imgs, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", nil, nil)
- Expect(len(imgs)).To(BeNumerically(">=", 1))
+ reports, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", entities.ImageSearchOptions{})
+ Expect(len(reports)).To(BeNumerically(">=", 1))
})
It("Prune images", func() {
diff --git a/pkg/cgroups/pids.go b/pkg/cgroups/pids.go
index 92f82553b..b2bfebe4d 100644
--- a/pkg/cgroups/pids.go
+++ b/pkg/cgroups/pids.go
@@ -44,7 +44,7 @@ func (c *pidHandler) Destroy(ctr *CgroupControl) error {
// Stat fills a metrics structure with usage stats for the controller
func (c *pidHandler) Stat(ctr *CgroupControl, m *Metrics) error {
- if ctr.path != "" {
+ if ctr.path == "" {
// nothing we can do to retrieve the pids.current path
return nil
}
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index 16b96e9ef..3110898a8 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -19,4 +19,5 @@ type ImageEngine interface {
Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error
Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error
Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error
+ Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error)
}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index bc8a34c13..53a5f4951 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -181,6 +181,37 @@ type ImagePushOptions struct {
TLSVerify types.OptionalBool
}
+// ImageSearchOptions are the arguments for searching images.
+type ImageSearchOptions struct {
+ // Authfile is the path to the authentication file. Ignored for remote
+ // calls.
+ Authfile string
+ // Filters for the search results.
+ Filters []string
+ // Limit the number of results.
+ Limit int
+ // NoTrunc will not truncate the output.
+ NoTrunc bool
+ // TLSVerify to enable/disable HTTPS and certificate verification.
+ TLSVerify types.OptionalBool
+}
+
+// ImageSearchReport is the response from searching images.
+type ImageSearchReport struct {
+ // Index is the image index (e.g., "docker.io" or "quay.io")
+ Index string
+ // Name is the canoncical name of the image (e.g., "docker.io/library/alpine").
+ Name string
+ // Description of the image.
+ Description string
+ // Stars is the number of stars of the image.
+ Stars int
+ // Official indicates if it's an official image.
+ Official string
+ // Automated indicates if the image was created by an automated build.
+ Automated string
+}
+
type ImageListOptions struct {
All bool `json:"all" schema:"all"`
Filter []string `json:"Filter,omitempty"`
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 6fc5eb9e3..402bbb45e 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -425,3 +425,38 @@ func (ir *ImageEngine) Diff(_ context.Context, nameOrId string, _ entities.DiffO
}
return &entities.DiffReport{Changes: changes}, nil
}
+
+func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) {
+ filter, err := image.ParseSearchFilter(opts.Filters)
+ if err != nil {
+ return nil, err
+ }
+
+ searchOpts := image.SearchOptions{
+ Authfile: opts.Authfile,
+ Filter: *filter,
+ Limit: opts.Limit,
+ NoTrunc: opts.NoTrunc,
+ InsecureSkipTLSVerify: opts.TLSVerify,
+ }
+
+ searchResults, err := image.SearchImages(term, searchOpts)
+ if err != nil {
+ return nil, err
+ }
+
+ // Convert from image.SearchResults to entities.ImageSearchReport. We don't
+ // want to leak any low-level packages into the remote client, which
+ // requires converting.
+ reports := make([]entities.ImageSearchReport, len(searchResults))
+ for i := range searchResults {
+ reports[i].Index = searchResults[i].Index
+ reports[i].Name = searchResults[i].Name
+ reports[i].Description = searchResults[i].Index
+ reports[i].Stars = searchResults[i].Stars
+ reports[i].Official = searchResults[i].Official
+ reports[i].Automated = searchResults[i].Automated
+ }
+
+ return reports, nil
+}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 66abd7f47..54f2e8334 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -250,3 +250,7 @@ func (ir *ImageEngine) Diff(ctx context.Context, nameOrId string, _ entities.Dif
}
return &entities.DiffReport{Changes: changes}, nil
}
+
+func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) {
+ return images.Search(ir.ClientCxt, term, opts)
+}