summaryrefslogtreecommitdiff
path: root/pkg/api/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api/handlers')
-rw-r--r--pkg/api/handlers/compat/containers_attach.go35
-rw-r--r--pkg/api/handlers/compat/events.go19
-rw-r--r--pkg/api/handlers/compat/images_search.go1
-rw-r--r--pkg/api/handlers/libpod/containers.go20
-rw-r--r--pkg/api/handlers/libpod/images.go53
-rw-r--r--pkg/api/handlers/libpod/pods.go3
-rw-r--r--pkg/api/handlers/types.go25
7 files changed, 137 insertions, 19 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/events.go b/pkg/api/handlers/compat/events.go
index 0f72ef328..8ef32716d 100644
--- a/pkg/api/handlers/compat/events.go
+++ b/pkg/api/handlers/compat/events.go
@@ -1,7 +1,6 @@
package compat
import (
- "encoding/json"
"fmt"
"net/http"
@@ -10,6 +9,7 @@ import (
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/gorilla/schema"
+ jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -48,14 +48,27 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
}()
if eventsError != nil {
utils.InternalServerError(w, eventsError)
+ close(eventChannel)
return
}
- coder := json.NewEncoder(w)
- coder.SetEscapeHTML(true)
+ // If client disappears we need to stop listening for events
+ go func(done <-chan struct{}) {
+ <-done
+ close(eventChannel)
+ }(r.Context().Done())
+ // Headers need to be written out before turning Writer() over to json encoder
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
+ if flusher, ok := w.(http.Flusher); ok {
+ flusher.Flush()
+ }
+
+ json := jsoniter.ConfigCompatibleWithStandardLibrary
+ coder := json.NewEncoder(w)
+ coder.SetEscapeHTML(true)
+
for event := range eventChannel {
e := handlers.EventToApiEvent(event)
if err := coder.Encode(e); err != nil {
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/containers.go b/pkg/api/handlers/libpod/containers.go
index 5cbfb11eb..086bef847 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -285,3 +285,23 @@ func Restore(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusOK, entities.RestoreReport{Id: ctr.ID()})
}
+
+func InitContainer(w http.ResponseWriter, r *http.Request) {
+ name := utils.GetName(r)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ ctr, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ err = ctr.Init(r.Context())
+ if errors.Cause(err) == define.ErrCtrStateInvalid {
+ utils.Error(w, "container already initialized", http.StatusNotModified, err)
+ return
+ }
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+}
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/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index a890169a1..81cab1ede 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -12,6 +12,7 @@ import (
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/specgen"
+ "github.com/containers/libpod/pkg/specgen/generate"
"github.com/containers/libpod/pkg/util"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -27,7 +28,7 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen"))
return
}
- pod, err := psg.MakePod(runtime)
+ pod, err := generate.MakePod(&psg, runtime)
if err != nil {
http_code := http.StatusInternalServerError
if errors.Cause(err) == define.ErrPodExists {
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index f1c932ebc..0fe6ae6a7 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -180,6 +180,31 @@ type ExecCreateResponse struct {
docker.IDResponse
}
+func (e *Event) ToLibpodEvent() *events.Event {
+ exitCode, err := strconv.Atoi(e.Actor.Attributes["containerExitCode"])
+ if err != nil {
+ return nil
+ }
+ status, err := events.StringToStatus(e.Action)
+ if err != nil {
+ return nil
+ }
+ t, err := events.StringToType(e.Type)
+ if err != nil {
+ return nil
+ }
+ lp := events.Event{
+ ContainerExitCode: exitCode,
+ ID: e.Actor.ID,
+ Image: e.Actor.Attributes["image"],
+ Name: e.Actor.Attributes["name"],
+ Status: status,
+ Time: time.Unix(e.Time, e.TimeNano),
+ Type: t,
+ }
+ return &lp
+}
+
func EventToApiEvent(e *events.Event) *Event {
return &Event{dockerEvents.Message{
Type: e.Type.String(),