summaryrefslogtreecommitdiff
path: root/pkg/api
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api')
-rw-r--r--pkg/api/handlers/compat/containers.go27
-rw-r--r--pkg/api/handlers/compat/containers_archive.go12
-rw-r--r--pkg/api/handlers/compat/containers_attach.go51
-rw-r--r--pkg/api/handlers/compat/containers_start.go9
-rw-r--r--pkg/api/handlers/compat/containers_stats.go6
-rw-r--r--pkg/api/handlers/compat/containers_stop.go (renamed from pkg/api/handlers/compat/container_start.go)0
-rw-r--r--pkg/api/handlers/compat/events.go12
-rw-r--r--pkg/api/handlers/compat/exec.go72
-rw-r--r--pkg/api/handlers/compat/images.go30
-rw-r--r--pkg/api/handlers/compat/images_build.go1
-rw-r--r--pkg/api/handlers/compat/images_push.go15
-rw-r--r--pkg/api/handlers/compat/networks.go301
-rw-r--r--pkg/api/handlers/compat/ping.go8
-rw-r--r--pkg/api/handlers/compat/resize.go86
-rw-r--r--pkg/api/handlers/compat/swagger.go28
-rw-r--r--pkg/api/handlers/compat/types.go2
-rw-r--r--pkg/api/handlers/compat/version.go43
-rw-r--r--pkg/api/handlers/handler.go6
-rw-r--r--pkg/api/handlers/libpod/containers.go4
-rw-r--r--pkg/api/handlers/libpod/containers_create.go3
-rw-r--r--pkg/api/handlers/libpod/copy.go12
-rw-r--r--pkg/api/handlers/libpod/generate.go38
-rw-r--r--pkg/api/handlers/libpod/healthcheck.go24
-rw-r--r--pkg/api/handlers/libpod/images.go128
-rw-r--r--pkg/api/handlers/libpod/manifests.go5
-rw-r--r--pkg/api/handlers/libpod/networks.go68
-rw-r--r--pkg/api/handlers/libpod/play.go82
-rw-r--r--pkg/api/handlers/libpod/swagger.go28
-rw-r--r--pkg/api/handlers/libpod/system.go13
-rw-r--r--pkg/api/handlers/swagger/swagger.go7
-rw-r--r--pkg/api/handlers/types.go13
-rw-r--r--pkg/api/handlers/utils/errors.go8
-rw-r--r--pkg/api/handlers/utils/handler.go86
-rw-r--r--pkg/api/handlers/utils/handler_test.go139
-rw-r--r--pkg/api/handlers/utils/images.go33
-rw-r--r--pkg/api/server/register_archive.go171
-rw-r--r--pkg/api/server/register_containers.go6
-rw-r--r--pkg/api/server/register_events.go5
-rw-r--r--pkg/api/server/register_exec.go24
-rw-r--r--pkg/api/server/register_generate.go41
-rw-r--r--pkg/api/server/register_images.go290
-rw-r--r--pkg/api/server/register_networks.go190
-rw-r--r--pkg/api/server/register_play.go42
-rw-r--r--pkg/api/server/register_system.go17
-rw-r--r--pkg/api/server/register_version.go21
-rw-r--r--pkg/api/server/server.go12
-rw-r--r--pkg/api/server/swagger.go43
-rw-r--r--pkg/api/tags.yaml8
48 files changed, 2008 insertions, 262 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 239e41af4..cea4bd0f6 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -4,6 +4,7 @@ import (
"encoding/binary"
"encoding/json"
"fmt"
+ "io"
"net/http"
"strconv"
"strings"
@@ -295,7 +296,9 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
}()
w.WriteHeader(http.StatusOK)
- var builder strings.Builder
+
+ var frame strings.Builder
+ header := make([]byte, 8)
for ok := true; ok; ok = query.Follow {
for line := range logChannel {
if _, found := r.URL.Query()["until"]; found {
@@ -304,10 +307,8 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
}
}
- // Reset variables we're ready to loop again
- builder.Reset()
- header := [8]byte{}
-
+ // Reset buffer we're ready to loop again
+ frame.Reset()
switch line.Device {
case "stdout":
if !query.Stdout {
@@ -327,17 +328,17 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
}
if query.Timestamps {
- builder.WriteString(line.Time.Format(time.RFC3339))
- builder.WriteRune(' ')
+ frame.WriteString(line.Time.Format(time.RFC3339))
+ frame.WriteString(" ")
}
- builder.WriteString(line.Msg)
- // Build header and output entry
- binary.BigEndian.PutUint32(header[4:], uint32(len(header)+builder.Len()))
- if _, err := w.Write(header[:]); err != nil {
+ frame.WriteString(line.Msg)
+
+ binary.BigEndian.PutUint32(header[4:], uint32(frame.Len()))
+ if _, err := w.Write(header[0:8]); err != nil {
log.Errorf("unable to write log output header: %q", err)
}
- if _, err := fmt.Fprint(w, builder.String()); err != nil {
- log.Errorf("unable to write builder string: %q", err)
+ if _, err := io.WriteString(w, frame.String()); err != nil {
+ log.Errorf("unable to write frame string: %q", err)
}
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go
new file mode 100644
index 000000000..c3a26873e
--- /dev/null
+++ b/pkg/api/handlers/compat/containers_archive.go
@@ -0,0 +1,12 @@
+package compat
+
+import (
+ "errors"
+ "net/http"
+
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+)
+
+func Archive(w http.ResponseWriter, r *http.Request) {
+ utils.Error(w, "not implemented", http.StatusNotImplemented, errors.New("not implemented"))
+}
diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go
index 80ad52aee..990140ee1 100644
--- a/pkg/api/handlers/compat/containers_attach.go
+++ b/pkg/api/handlers/compat/containers_attach.go
@@ -10,9 +10,14 @@ import (
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
- "k8s.io/client-go/tools/remotecommand"
)
+// AttachHeader is the literal header sent for upgraded/hijacked connections for
+// attach, sourced from Docker at:
+// https://raw.githubusercontent.com/moby/moby/b95fad8e51bd064be4f4e58a996924f343846c85/api/server/router/container/container_routes.go
+// Using literally to ensure compatibility with existing clients.
+const AttachHeader = "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n"
+
func AttachContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
@@ -85,11 +90,11 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
// 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()))
+ utils.Error(w, "Container in wrong state", http.StatusConflict, 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"))
+ utils.InternalServerError(w, errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers - currently in state %s", state.String()))
return
}
@@ -106,10 +111,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
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")
+ fmt.Fprintf(connection, AttachHeader)
logrus.Debugf("Hijack for attach of container %s successful", ctr.ID())
@@ -124,38 +126,3 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
logrus.Debugf("Attach for container %s completed successfully", ctr.ID())
}
-
-func ResizeContainer(w http.ResponseWriter, r *http.Request) {
- runtime := r.Context().Value("runtime").(*libpod.Runtime)
- decoder := r.Context().Value("decoder").(*schema.Decoder)
-
- query := struct {
- Height uint16 `schema:"h"`
- Width uint16 `schema:"w"`
- }{}
- if err := decoder.Decode(&query, r.URL.Query()); err != nil {
- // This is not a 400, despite the fact that is should be, for
- // compatibility reasons.
- utils.InternalServerError(w, errors.Wrapf(err, "error parsing query options"))
- return
- }
-
- name := utils.GetName(r)
- ctr, err := runtime.LookupContainer(name)
- if err != nil {
- utils.ContainerNotFound(w, name, err)
- return
- }
-
- newSize := remotecommand.TerminalSize{
- Width: query.Width,
- Height: query.Height,
- }
- if err := ctr.AttachResize(newSize); err != nil {
- utils.InternalServerError(w, err)
- return
- }
- // This is not a 204, even though we write nothing, for compatibility
- // reasons.
- utils.WriteResponse(w, http.StatusOK, "")
-}
diff --git a/pkg/api/handlers/compat/containers_start.go b/pkg/api/handlers/compat/containers_start.go
index 67bd287ab..cdbc8ff76 100644
--- a/pkg/api/handlers/compat/containers_start.go
+++ b/pkg/api/handlers/compat/containers_start.go
@@ -3,11 +3,12 @@ package compat
import (
"net/http"
+ "github.com/sirupsen/logrus"
+
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/gorilla/schema"
- "github.com/pkg/errors"
)
func StartContainer(w http.ResponseWriter, r *http.Request) {
@@ -23,8 +24,7 @@ func StartContainer(w http.ResponseWriter, r *http.Request) {
}
if len(query.DetachKeys) > 0 {
// TODO - start does not support adding detach keys
- utils.BadRequest(w, "detachKeys", query.DetachKeys, errors.New("the detachKeys parameter is not supported yet"))
- return
+ logrus.Info("the detach keys parameter is not supported on start container")
}
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
@@ -33,7 +33,6 @@ func StartContainer(w http.ResponseWriter, r *http.Request) {
utils.ContainerNotFound(w, name, err)
return
}
-
state, err := con.State()
if err != nil {
utils.InternalServerError(w, err)
@@ -43,7 +42,7 @@ func StartContainer(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusNotModified, "")
return
}
- if err := con.Start(r.Context(), false); err != nil {
+ if err := con.Start(r.Context(), len(con.PodID()) > 0); err != nil {
utils.InternalServerError(w, err)
return
}
diff --git a/pkg/api/handlers/compat/containers_stats.go b/pkg/api/handlers/compat/containers_stats.go
index 53ad0a632..048321add 100644
--- a/pkg/api/handlers/compat/containers_stats.go
+++ b/pkg/api/handlers/compat/containers_stats.go
@@ -45,12 +45,12 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- if state != define.ContainerStateRunning && !query.Stream {
- utils.InternalServerError(w, define.ErrCtrStateInvalid)
+ if state != define.ContainerStateRunning {
+ utils.Error(w, "Container not running and streaming requested", http.StatusConflict, define.ErrCtrStateInvalid)
return
}
- stats, err := ctnr.GetContainerStats(&libpod.ContainerStats{})
+ stats, err := ctnr.GetContainerStats(&define.ContainerStats{})
if err != nil {
utils.InternalServerError(w, errors.Wrapf(err, "Failed to obtain Container %s stats", name))
return
diff --git a/pkg/api/handlers/compat/container_start.go b/pkg/api/handlers/compat/containers_stop.go
index d26ef2c82..d26ef2c82 100644
--- a/pkg/api/handlers/compat/container_start.go
+++ b/pkg/api/handlers/compat/containers_stop.go
diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go
index 7ebfb0d1e..577ddd0a1 100644
--- a/pkg/api/handlers/compat/events.go
+++ b/pkg/api/handlers/compat/events.go
@@ -26,7 +26,10 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
Since string `schema:"since"`
Until string `schema:"until"`
Filters map[string][]string `schema:"filters"`
- }{}
+ Stream bool `schema:"stream"`
+ }{
+ Stream: true,
+ }
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, "Failed to parse parameters", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
}
@@ -41,9 +44,10 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
if len(query.Since) > 0 || len(query.Until) > 0 {
fromStart = true
}
+
eventChannel := make(chan *events.Event)
go func() {
- readOpts := events.ReadOptions{FromStart: fromStart, Stream: true, Filters: libpodFilters, EventChannel: eventChannel, Since: query.Since, Until: query.Until}
+ readOpts := events.ReadOptions{FromStart: fromStart, Stream: query.Stream, Filters: libpodFilters, EventChannel: eventChannel, Since: query.Since, Until: query.Until}
eventsError = runtime.Events(readOpts)
}()
if eventsError != nil {
@@ -55,7 +59,9 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
// If client disappears we need to stop listening for events
go func(done <-chan struct{}) {
<-done
- close(eventChannel)
+ if _, ok := <-eventChannel; ok {
+ close(eventChannel)
+ }
}(r.Context().Done())
// Headers need to be written out before turning Writer() over to json encoder
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
index ec1a8ac96..6865a3319 100644
--- a/pkg/api/handlers/compat/exec.go
+++ b/pkg/api/handlers/compat/exec.go
@@ -104,4 +104,76 @@ func ExecInspectHandler(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusOK, inspectOut)
+
+ // Only for the Compat API: we want to remove sessions that were
+ // stopped. This is very hacky, but should suffice for now.
+ if !utils.IsLibpodRequest(r) && inspectOut.CanRemove {
+ logrus.Infof("Pruning stale exec session %s from container %s", sessionID, sessionCtr.ID())
+ if err := sessionCtr.ExecRemove(sessionID, false); err != nil && errors.Cause(err) != define.ErrNoSuchExecSession {
+ logrus.Errorf("Error removing stale exec session %s from container %s: %v", sessionID, sessionCtr.ID(), err)
+ }
+ }
+}
+
+// ExecStartHandler runs a given exec session.
+func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ sessionID := mux.Vars(r)["id"]
+
+ // TODO: We should read/support Tty and Detach from here.
+ bodyParams := new(handlers.ExecStartConfig)
+
+ if err := json.NewDecoder(r.Body).Decode(&bodyParams); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to decode parameters for %s", r.URL.String()))
+ return
+ }
+ if bodyParams.Detach {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Errorf("Detached exec is not yet supported"))
+ return
+ }
+ // TODO: Verify TTY setting against what inspect session was made with
+
+ sessionCtr, err := runtime.GetExecSessionContainer(sessionID)
+ if err != nil {
+ utils.Error(w, fmt.Sprintf("No such exec session: %s", sessionID), http.StatusNotFound, err)
+ return
+ }
+
+ logrus.Debugf("Starting exec session %s of container %s", sessionID, sessionCtr.ID())
+
+ state, err := sessionCtr.State()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ if state != define.ContainerStateRunning {
+ utils.Error(w, http.StatusText(http.StatusConflict), http.StatusConflict, errors.Errorf("cannot exec in a container that is not running; container %s is %s", sessionCtr.ID(), state.String()))
+ return
+ }
+
+ // Hijack the connection
+ hijacker, ok := w.(http.Hijacker)
+ if !ok {
+ utils.InternalServerError(w, errors.Errorf("unable to hijack connection"))
+ return
+ }
+
+ connection, buffer, err := hijacker.Hijack()
+ if err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "error hijacking connection"))
+ return
+ }
+
+ fmt.Fprintf(connection, AttachHeader)
+
+ logrus.Debugf("Hijack for attach of container %s exec session %s successful", sessionCtr.ID(), sessionID)
+
+ if err := sessionCtr.ExecHTTPStartAndAttach(sessionID, connection, buffer, nil, nil, nil); err != nil {
+ logrus.Errorf("Error attaching to container %s exec session %s: %v", sessionCtr.ID(), sessionID, err)
+ }
+
+ logrus.Debugf("Attach for container %s exec session %s completed successfully", sessionCtr.ID(), sessionID)
}
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index ea9cbd691..b64ed0036 100644
--- a/pkg/api/handlers/compat/images.go
+++ b/pkg/api/handlers/compat/images.go
@@ -15,6 +15,7 @@ import (
image2 "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/auth"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/util"
"github.com/docker/docker/api/types"
@@ -251,19 +252,32 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) {
return
}
- /*
- fromImage – Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed.
- repo – Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image.
- tag – Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled.
- */
fromImage := query.FromImage
if len(query.Tag) >= 1 {
fromImage = fmt.Sprintf("%s:%s", fromImage, query.Tag)
}
- // TODO
- // We are eating the output right now because we haven't talked about how to deal with multiple responses yet
- img, err := runtime.ImageRuntime().New(r.Context(), fromImage, "", "", nil, &image2.DockerRegistryOptions{}, image2.SigningOptions{}, nil, util.PullImageMissing)
+ authConf, authfile, err := auth.GetCredentials(r)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String()))
+ return
+ }
+ defer auth.RemoveAuthfile(authfile)
+
+ registryOpts := image2.DockerRegistryOptions{DockerRegistryCreds: authConf}
+ if sys := runtime.SystemContext(); sys != nil {
+ registryOpts.DockerCertPath = sys.DockerCertPath
+ }
+ img, err := runtime.ImageRuntime().New(r.Context(),
+ fromImage,
+ "", // signature policy
+ authfile,
+ nil, // writer
+ &registryOpts,
+ image2.SigningOptions{},
+ nil, // label
+ util.PullImageMissing,
+ )
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index e208e6ddc..e9d8fd719 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -226,6 +226,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
id, _, err := runtime.Build(r.Context(), buildOptions, query.Dockerfile)
if err != nil {
utils.InternalServerError(w, err)
+ return
}
// Find image ID that was built...
diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go
index 2260d5557..47976b7c9 100644
--- a/pkg/api/handlers/compat/images_push.go
+++ b/pkg/api/handlers/compat/images_push.go
@@ -9,6 +9,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/auth"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -48,13 +49,17 @@ func PushImage(w http.ResponseWriter, r *http.Request) {
return
}
- // TODO: the X-Registry-Auth header is not checked yet here nor in any other
- // endpoint. Pushing does NOT work with authentication at the moment.
- dockerRegistryOptions := &image.DockerRegistryOptions{}
- authfile := ""
+ authConf, authfile, err := auth.GetCredentials(r)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String()))
+ return
+ }
+ defer auth.RemoveAuthfile(authfile)
+
+ dockerRegistryOptions := &image.DockerRegistryOptions{DockerRegistryCreds: authConf}
if sys := runtime.SystemContext(); sys != nil {
dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
- authfile = sys.AuthFilePath
+ dockerRegistryOptions.RegistriesConfPath = sys.SystemRegistriesConfPath
}
err = newImage.PushImageToHeuristicDestination(
diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go
new file mode 100644
index 000000000..c52ca093f
--- /dev/null
+++ b/pkg/api/handlers/compat/networks.go
@@ -0,0 +1,301 @@
+package compat
+
+import (
+ "encoding/json"
+ "net"
+ "net/http"
+ "os"
+ "syscall"
+ "time"
+
+ "github.com/containernetworking/cni/libcni"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi"
+ "github.com/containers/libpod/pkg/network"
+ "github.com/docker/docker/api/types"
+ dockerNetwork "github.com/docker/docker/api/types/network"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+)
+
+type CompatInspectNetwork struct {
+ types.NetworkResource
+}
+
+func InspectNetwork(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ // FYI scope and version are currently unused but are described by the API
+ // Leaving this for if/when we have to enable these
+ //query := struct {
+ // scope string
+ // verbose bool
+ //}{
+ // // override any golang type defaults
+ //}
+ //decoder := r.Context().Value("decoder").(*schema.Decoder)
+ //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
+ //}
+ config, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ name := utils.GetName(r)
+ _, err = network.InspectNetwork(config, name)
+ if err != nil {
+ // TODO our network package does not distinguish between not finding a
+ // specific network vs not being able to read it
+ utils.InternalServerError(w, err)
+ return
+ }
+ report, err := getNetworkResourceByName(name, runtime)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
+}
+
+func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*types.NetworkResource, error) {
+ var (
+ ipamConfigs []dockerNetwork.IPAMConfig
+ )
+ config, err := runtime.GetConfig()
+ if err != nil {
+ return nil, err
+ }
+ containerEndpoints := map[string]types.EndpointResource{}
+ // Get the network path so we can get created time
+ networkConfigPath, err := network.GetCNIConfigPathByName(config, name)
+ if err != nil {
+ return nil, err
+ }
+ f, err := os.Stat(networkConfigPath)
+ if err != nil {
+ return nil, err
+ }
+ stat := f.Sys().(*syscall.Stat_t)
+ cons, err := runtime.GetAllContainers()
+ if err != nil {
+ return nil, err
+ }
+ conf, err := libcni.ConfListFromFile(networkConfigPath)
+ if err != nil {
+ return nil, err
+ }
+
+ // No Bridge plugin means we bail
+ bridge, err := genericPluginsToBridge(conf.Plugins, network.DefaultNetworkDriver)
+ if err != nil {
+ return nil, err
+ }
+ for _, outer := range bridge.IPAM.Ranges {
+ for _, n := range outer {
+ ipamConfig := dockerNetwork.IPAMConfig{
+ Subnet: n.Subnet,
+ Gateway: n.Gateway,
+ }
+ ipamConfigs = append(ipamConfigs, ipamConfig)
+ }
+ }
+
+ for _, con := range cons {
+ data, err := con.Inspect(false)
+ if err != nil {
+ return nil, err
+ }
+ if netData, ok := data.NetworkSettings.Networks[name]; ok {
+ containerEndpoint := types.EndpointResource{
+ Name: netData.NetworkID,
+ EndpointID: netData.EndpointID,
+ MacAddress: netData.MacAddress,
+ IPv4Address: netData.IPAddress,
+ IPv6Address: netData.GlobalIPv6Address,
+ }
+ containerEndpoints[con.ID()] = containerEndpoint
+ }
+ }
+ report := types.NetworkResource{
+ Name: name,
+ ID: "",
+ Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert
+ Scope: "",
+ Driver: network.DefaultNetworkDriver,
+ EnableIPv6: false,
+ IPAM: dockerNetwork.IPAM{
+ Driver: "default",
+ Options: nil,
+ Config: ipamConfigs,
+ },
+ Internal: false,
+ Attachable: false,
+ Ingress: false,
+ ConfigFrom: dockerNetwork.ConfigReference{},
+ ConfigOnly: false,
+ Containers: containerEndpoints,
+ Options: nil,
+ Labels: nil,
+ Peers: nil,
+ Services: nil,
+ }
+ return &report, nil
+}
+
+func genericPluginsToBridge(plugins []*libcni.NetworkConfig, pluginType string) (network.HostLocalBridge, error) {
+ var bridge network.HostLocalBridge
+ generic, err := findPluginByName(plugins, pluginType)
+ if err != nil {
+ return bridge, err
+ }
+ err = json.Unmarshal(generic, &bridge)
+ return bridge, err
+}
+
+func findPluginByName(plugins []*libcni.NetworkConfig, pluginType string) ([]byte, error) {
+ for _, p := range plugins {
+ if pluginType == p.Network.Type {
+ return p.Bytes, nil
+ }
+ }
+ return nil, errors.New("unable to find bridge plugin")
+}
+
+func ListNetworks(w http.ResponseWriter, r *http.Request) {
+ var (
+ reports []*types.NetworkResource
+ )
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Filters map[string][]string `schema:"filters"`
+ }{
+ // override any golang type defaults
+ }
+ 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
+ }
+ config, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ // TODO remove when filters are implemented
+ if len(query.Filters) > 0 {
+ utils.InternalServerError(w, errors.New("filters for listing networks is not implemented"))
+ return
+ }
+ netNames, err := network.GetNetworkNamesFromFileSystem(config)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ for _, name := range netNames {
+ report, err := getNetworkResourceByName(name, runtime)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ }
+ reports = append(reports, report)
+ }
+ utils.WriteResponse(w, http.StatusOK, reports)
+}
+
+func CreateNetwork(w http.ResponseWriter, r *http.Request) {
+ var (
+ name string
+ networkCreate types.NetworkCreateRequest
+ )
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ if err := json.NewDecoder(r.Body).Decode(&networkCreate); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ return
+ }
+
+ if len(networkCreate.Name) > 0 {
+ name = networkCreate.Name
+ }
+ // At present I think we should just suport the bridge driver
+ // and allow demand to make us consider more
+ if networkCreate.Driver != network.DefaultNetworkDriver {
+ utils.InternalServerError(w, errors.New("network create only supports the bridge driver"))
+ return
+ }
+ ncOptions := entities.NetworkCreateOptions{
+ Driver: network.DefaultNetworkDriver,
+ Internal: networkCreate.Internal,
+ }
+ if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil {
+ if len(networkCreate.IPAM.Config) > 1 {
+ utils.InternalServerError(w, errors.New("compat network create can only support one IPAM config"))
+ return
+ }
+
+ if len(networkCreate.IPAM.Config[0].Subnet) > 0 {
+ _, subnet, err := net.ParseCIDR(networkCreate.IPAM.Config[0].Subnet)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ ncOptions.Subnet = *subnet
+ }
+ if len(networkCreate.IPAM.Config[0].Gateway) > 0 {
+ ncOptions.Gateway = net.ParseIP(networkCreate.IPAM.Config[0].Gateway)
+ }
+ if len(networkCreate.IPAM.Config[0].IPRange) > 0 {
+ _, IPRange, err := net.ParseCIDR(networkCreate.IPAM.Config[0].IPRange)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ ncOptions.Range = *IPRange
+ }
+ }
+ ce := abi.ContainerEngine{Libpod: runtime}
+ _, err := ce.NetworkCreate(r.Context(), name, ncOptions)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ }
+ report := types.NetworkCreate{
+ CheckDuplicate: networkCreate.CheckDuplicate,
+ Driver: networkCreate.Driver,
+ Scope: networkCreate.Scope,
+ EnableIPv6: networkCreate.EnableIPv6,
+ IPAM: networkCreate.IPAM,
+ Internal: networkCreate.Internal,
+ Attachable: networkCreate.Attachable,
+ Ingress: networkCreate.Ingress,
+ ConfigOnly: networkCreate.ConfigOnly,
+ ConfigFrom: networkCreate.ConfigFrom,
+ Options: networkCreate.Options,
+ Labels: networkCreate.Labels,
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
+}
+
+func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ config, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ name := utils.GetName(r)
+ exists, err := network.Exists(config, name)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ if !exists {
+ utils.Error(w, "network not found", http.StatusNotFound, err)
+ return
+ }
+ if err := network.RemoveNetwork(config, name); err != nil {
+ utils.InternalServerError(w, err)
+ }
+ utils.WriteResponse(w, http.StatusNoContent, "")
+}
diff --git a/pkg/api/handlers/compat/ping.go b/pkg/api/handlers/compat/ping.go
index 6e77e270f..abee3d8e8 100644
--- a/pkg/api/handlers/compat/ping.go
+++ b/pkg/api/handlers/compat/ping.go
@@ -5,22 +5,22 @@ import (
"net/http"
"github.com/containers/buildah"
- "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
)
// Ping returns headers to client about the service
//
// This handler must always be the same for the compatibility and libpod URL trees!
// Clients will use the Header availability to test which backend engine is in use.
+// Note: Additionally handler supports GET and HEAD methods
func Ping(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("API-Version", handlers.DefaultApiVersion)
+ w.Header().Set("API-Version", utils.ApiVersion[utils.CompatTree][utils.CurrentApiVersion].String())
w.Header().Set("BuildKit-Version", "")
w.Header().Set("Docker-Experimental", "true")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Pragma", "no-cache")
- // API-Version and Libpod-API-Version may not always be equal
- w.Header().Set("Libpod-API-Version", handlers.DefaultApiVersion)
+ w.Header().Set("Libpod-API-Version", utils.ApiVersion[utils.LibpodTree][utils.CurrentApiVersion].String())
w.Header().Set("Libpod-Buildha-Version", buildah.Version)
w.WriteHeader(http.StatusOK)
diff --git a/pkg/api/handlers/compat/resize.go b/pkg/api/handlers/compat/resize.go
new file mode 100644
index 000000000..231b53175
--- /dev/null
+++ b/pkg/api/handlers/compat/resize.go
@@ -0,0 +1,86 @@
+package compat
+
+import (
+ "fmt"
+ "net/http"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+func ResizeTTY(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+
+ // /containers/{id}/resize
+ query := struct {
+ height uint16 `schema:"h"`
+ width uint16 `schema:"w"`
+ }{
+ // override any golang type defaults
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ sz := remotecommand.TerminalSize{
+ Width: query.width,
+ Height: query.height,
+ }
+
+ var status int
+ name := utils.GetName(r)
+ switch {
+ case strings.Contains(r.URL.Path, "/containers/"):
+ ctnr, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ if state, err := ctnr.State(); err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "cannot obtain container state"))
+ return
+ } else if state != define.ContainerStateRunning {
+ utils.Error(w, "Container not running", http.StatusConflict,
+ fmt.Errorf("container %q in wrong state %q", name, state.String()))
+ return
+ }
+ if err := ctnr.AttachResize(sz); err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "cannot resize container"))
+ return
+ }
+ // This is not a 204, even though we write nothing, for compatibility
+ // reasons.
+ status = http.StatusOK
+ case strings.Contains(r.URL.Path, "/exec/"):
+ ctnr, err := runtime.GetExecSessionContainer(name)
+ if err != nil {
+ utils.SessionNotFound(w, name, err)
+ return
+ }
+ if state, err := ctnr.State(); err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "cannot obtain session container state"))
+ return
+ } else if state != define.ContainerStateRunning {
+ utils.Error(w, "Container not running", http.StatusConflict,
+ fmt.Errorf("container %q in wrong state %q", name, state.String()))
+ return
+ }
+ if err := ctnr.ExecResize(name, sz); err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "cannot resize session"))
+ return
+ }
+ // This is not a 204, even though we write nothing, for compatibility
+ // reasons.
+ status = http.StatusCreated
+ }
+ utils.WriteResponse(w, status, "")
+}
diff --git a/pkg/api/handlers/compat/swagger.go b/pkg/api/handlers/compat/swagger.go
index ce83aa32f..dc94a7ebd 100644
--- a/pkg/api/handlers/compat/swagger.go
+++ b/pkg/api/handlers/compat/swagger.go
@@ -3,6 +3,7 @@ package compat
import (
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/storage/pkg/archive"
+ "github.com/docker/docker/api/types"
)
// Create container
@@ -35,3 +36,30 @@ type swagChangesResponse struct {
Changes []archive.Change
}
}
+
+// Network inspect
+// swagger:response CompatNetworkInspect
+type swagCompatNetworkInspect struct {
+ // in:body
+ Body types.NetworkResource
+}
+
+// Network list
+// swagger:response CompatNetworkList
+type swagCompatNetworkList struct {
+ // in:body
+ Body []types.NetworkResource
+}
+
+// Network create
+// swagger:model NetworkCreateRequest
+type NetworkCreateRequest struct {
+ types.NetworkCreateRequest
+}
+
+// Network create
+// swagger:response CompatNetworkCreate
+type swagCompatNetworkCreateResponse struct {
+ // in:body
+ Body struct{ types.NetworkCreate }
+}
diff --git a/pkg/api/handlers/compat/types.go b/pkg/api/handlers/compat/types.go
index b8d06760f..6d47ede64 100644
--- a/pkg/api/handlers/compat/types.go
+++ b/pkg/api/handlers/compat/types.go
@@ -48,7 +48,7 @@ type StatsJSON struct {
Stats
Name string `json:"name,omitempty"`
- ID string `json:"id,omitempty"`
+ ID string `json:"Id,omitempty"`
// Networks request version >=1.21
Networks map[string]docker.NetworkStats `json:"networks,omitempty"`
diff --git a/pkg/api/handlers/compat/version.go b/pkg/api/handlers/compat/version.go
index 35a95b562..bfc226bb8 100644
--- a/pkg/api/handlers/compat/version.go
+++ b/pkg/api/handlers/compat/version.go
@@ -8,8 +8,8 @@ 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"
+ "github.com/containers/libpod/pkg/domain/entities"
docker "github.com/docker/docker/api/types"
"github.com/pkg/errors"
)
@@ -34,34 +34,35 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
Name: "Podman Engine",
Version: versionInfo.Version,
Details: map[string]string{
- "APIVersion": handlers.DefaultApiVersion,
+ "APIVersion": utils.ApiVersion[utils.LibpodTree][utils.CurrentApiVersion].String(),
"Arch": goRuntime.GOARCH,
"BuildTime": time.Unix(versionInfo.Built, 0).Format(time.RFC3339),
"Experimental": "true",
"GitCommit": versionInfo.GitCommit,
"GoVersion": versionInfo.GoVersion,
"KernelVersion": infoData.Host.Kernel,
- "MinAPIVersion": handlers.MinimalApiVersion,
+ "MinAPIVersion": utils.ApiVersion[utils.LibpodTree][utils.MinimalApiVersion].String(),
"Os": goRuntime.GOOS,
},
}}
- utils.WriteResponse(w, http.StatusOK, handlers.Version{Version: docker.Version{
- Platform: struct {
- Name string
- }{
- Name: fmt.Sprintf("%s/%s/%s-%s", goRuntime.GOOS, goRuntime.GOARCH, infoData.Host.Distribution.Distribution, infoData.Host.Distribution.Version),
- },
- APIVersion: components[0].Details["APIVersion"],
- Arch: components[0].Details["Arch"],
- BuildTime: components[0].Details["BuildTime"],
- Components: components,
- Experimental: true,
- GitCommit: components[0].Details["GitCommit"],
- GoVersion: components[0].Details["GoVersion"],
- KernelVersion: components[0].Details["KernelVersion"],
- MinAPIVersion: components[0].Details["MinAPIVersion"],
- Os: components[0].Details["Os"],
- Version: components[0].Version,
- }})
+ utils.WriteResponse(w, http.StatusOK, entities.ComponentVersion{
+ Version: docker.Version{
+ Platform: struct {
+ Name string
+ }{
+ Name: fmt.Sprintf("%s/%s/%s-%s", goRuntime.GOOS, goRuntime.GOARCH, infoData.Host.Distribution.Distribution, infoData.Host.Distribution.Version),
+ },
+ APIVersion: components[0].Details["APIVersion"],
+ Arch: components[0].Details["Arch"],
+ BuildTime: components[0].Details["BuildTime"],
+ Components: components,
+ Experimental: true,
+ GitCommit: components[0].Details["GitCommit"],
+ GoVersion: components[0].Details["GoVersion"],
+ KernelVersion: components[0].Details["KernelVersion"],
+ MinAPIVersion: components[0].Details["MinAPIVersion"],
+ Os: components[0].Details["Os"],
+ Version: components[0].Version,
+ }})
}
diff --git a/pkg/api/handlers/handler.go b/pkg/api/handlers/handler.go
deleted file mode 100644
index 2dd2c886b..000000000
--- a/pkg/api/handlers/handler.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package handlers
-
-const (
- DefaultApiVersion = "1.40" // See https://docs.docker.com/engine/api/v1.40/
- MinimalApiVersion = "1.24"
-)
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index 3902bdc9b..50f6b1a38 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -66,6 +66,10 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
+ if len(pss) == 0 {
+ utils.WriteResponse(w, http.StatusOK, "[]")
+ return
+ }
utils.WriteResponse(w, http.StatusOK, pss)
}
diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go
index 40b6cacdb..71f440bce 100644
--- a/pkg/api/handlers/libpod/containers_create.go
+++ b/pkg/api/handlers/libpod/containers_create.go
@@ -5,10 +5,9 @@ import (
"encoding/json"
"net/http"
- "github.com/containers/libpod/pkg/domain/entities"
-
"github.com/containers/libpod/libpod"
"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/pkg/errors"
diff --git a/pkg/api/handlers/libpod/copy.go b/pkg/api/handlers/libpod/copy.go
new file mode 100644
index 000000000..a3b404bce
--- /dev/null
+++ b/pkg/api/handlers/libpod/copy.go
@@ -0,0 +1,12 @@
+package libpod
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/pkg/errors"
+)
+
+func Archive(w http.ResponseWriter, r *http.Request) {
+ utils.Error(w, "not implemented", http.StatusNotImplemented, errors.New("not implemented"))
+}
diff --git a/pkg/api/handlers/libpod/generate.go b/pkg/api/handlers/libpod/generate.go
new file mode 100644
index 000000000..23320d346
--- /dev/null
+++ b/pkg/api/handlers/libpod/generate.go
@@ -0,0 +1,38 @@
+package libpod
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+)
+
+func GenerateKube(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Service bool `schema:"service"`
+ }{
+ // Defaults would go here.
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
+ options := entities.GenerateKubeOptions{Service: query.Service}
+ report, err := containerEngine.GenerateKube(r.Context(), utils.GetName(r), options)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error generating YAML"))
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, report.Reader)
+}
diff --git a/pkg/api/handlers/libpod/healthcheck.go b/pkg/api/handlers/libpod/healthcheck.go
index 6eb2ab0e3..0ca3574b7 100644
--- a/pkg/api/handlers/libpod/healthcheck.go
+++ b/pkg/api/handlers/libpod/healthcheck.go
@@ -4,6 +4,7 @@ import (
"net/http"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/utils"
)
@@ -12,32 +13,27 @@ func RunHealthCheck(w http.ResponseWriter, r *http.Request) {
name := utils.GetName(r)
status, err := runtime.HealthCheck(name)
if err != nil {
- if status == libpod.HealthCheckContainerNotFound {
+ if status == define.HealthCheckContainerNotFound {
utils.ContainerNotFound(w, name, err)
return
}
- if status == libpod.HealthCheckNotDefined {
+ if status == define.HealthCheckNotDefined {
utils.Error(w, "no healthcheck defined", http.StatusConflict, err)
return
}
- if status == libpod.HealthCheckContainerStopped {
+ if status == define.HealthCheckContainerStopped {
utils.Error(w, "container not running", http.StatusConflict, err)
return
}
utils.InternalServerError(w, err)
return
}
- ctr, err := runtime.LookupContainer(name)
- if err != nil {
- utils.InternalServerError(w, err)
- return
+ hcStatus := define.HealthCheckUnhealthy
+ if status == define.HealthCheckSuccess {
+ hcStatus = define.HealthCheckHealthy
}
-
- hcLog, err := ctr.GetHealthCheckLog()
- if err != nil {
- utils.InternalServerError(w, err)
- return
+ report := define.HealthCheckResults{
+ Status: hcStatus,
}
-
- utils.WriteResponse(w, http.StatusOK, hcLog)
+ utils.WriteResponse(w, http.StatusOK, report)
}
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index f7be5ce9a..4b277d39c 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -21,12 +21,15 @@ import (
image2 "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/auth"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/infra/abi"
+ "github.com/containers/libpod/pkg/errorhandling"
"github.com/containers/libpod/pkg/util"
utils2 "github.com/containers/libpod/utils"
"github.com/gorilla/schema"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// Commit
@@ -55,13 +58,6 @@ func ImageExists(w http.ResponseWriter, r *http.Request) {
func ImageTree(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
-
- img, err := runtime.ImageRuntime().NewFromLocal(name)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "Failed to find image %s", name))
- return
- }
-
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
WhatRequires bool `schema:"whatrequires"`
@@ -73,14 +69,18 @@ func ImageTree(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
return
}
-
- tree, err := img.GenerateTree(query.WhatRequires)
+ ir := abi.ImageEngine{Libpod: runtime}
+ options := entities.ImageTreeOptions{WhatRequires: query.WhatRequires}
+ report, err := ir.Tree(r.Context(), name, options)
if err != nil {
+ if errors.Cause(err) == define.ErrNoSuchImage {
+ utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "Failed to find image %s", name))
+ return
+ }
utils.Error(w, "Server error", http.StatusInternalServerError, errors.Wrapf(err, "failed to generate image tree for %s", name))
return
}
-
- utils.WriteResponse(w, http.StatusOK, tree)
+ utils.WriteResponse(w, http.StatusOK, report)
}
func GetImage(w http.ResponseWriter, r *http.Request) {
@@ -341,7 +341,6 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
Reference string `schema:"reference"`
- Credentials string `schema:"credentials"`
OverrideOS string `schema:"overrideOS"`
OverrideArch string `schema:"overrideArch"`
TLSVerify bool `schema:"tlsVerify"`
@@ -384,20 +383,16 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
return
}
- var registryCreds *types.DockerAuthConfig
- if len(query.Credentials) != 0 {
- creds, err := util.ParseRegistryCreds(query.Credentials)
- if err != nil {
- utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Wrapf(err, "error parsing credentials %q", query.Credentials))
- return
- }
- registryCreds = creds
+ authConf, authfile, err := auth.GetCredentials(r)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String()))
+ return
}
+ defer auth.RemoveAuthfile(authfile)
// Setup the registry options
dockerRegistryOptions := image.DockerRegistryOptions{
- DockerRegistryCreds: registryCreds,
+ DockerRegistryCreds: authConf,
OSChoice: query.OverrideOS,
ArchitectureChoice: query.OverrideArch,
}
@@ -405,6 +400,13 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
}
+ sys := runtime.SystemContext()
+ if sys == nil {
+ sys = image.GetSystemContext("", authfile, false)
+ }
+ dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
+ sys.DockerAuthConfig = authConf
+
// Prepare the images we want to pull
imagesToPull := []string{}
res := []handlers.LibpodImagesPullReport{}
@@ -413,8 +415,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
if !query.AllTags {
imagesToPull = append(imagesToPull, imageName)
} else {
- systemContext := image.GetSystemContext("", "", false)
- tags, err := docker.GetRepositoryTags(context.Background(), systemContext, imageRef)
+ tags, err := docker.GetRepositoryTags(context.Background(), sys, imageRef)
if err != nil {
utils.InternalServerError(w, errors.Wrap(err, "error getting repository tags"))
return
@@ -424,12 +425,6 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
}
}
- authfile := ""
- if sys := runtime.SystemContext(); sys != nil {
- dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
- authfile = sys.AuthFilePath
- }
-
// Finally pull the images
for _, img := range imagesToPull {
newImage, err := runtime.ImageRuntime().New(
@@ -458,7 +453,6 @@ func PushImage(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
query := struct {
- Credentials string `schema:"credentials"`
Destination string `schema:"destination"`
TLSVerify bool `schema:"tlsVerify"`
}{
@@ -494,26 +488,20 @@ func PushImage(w http.ResponseWriter, r *http.Request) {
return
}
- var registryCreds *types.DockerAuthConfig
- if len(query.Credentials) != 0 {
- creds, err := util.ParseRegistryCreds(query.Credentials)
- if err != nil {
- utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Wrapf(err, "error parsing credentials %q", query.Credentials))
- return
- }
- registryCreds = creds
+ authConf, authfile, err := auth.GetCredentials(r)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String()))
+ return
}
+ defer auth.RemoveAuthfile(authfile)
+ logrus.Errorf("AuthConf: %v", authConf)
- // TODO: the X-Registry-Auth header is not checked yet here nor in any other
- // endpoint. Pushing does NOT work with authentication at the moment.
dockerRegistryOptions := &image.DockerRegistryOptions{
- DockerRegistryCreds: registryCreds,
+ DockerRegistryCreds: authConf,
}
- authfile := ""
if sys := runtime.SystemContext(); sys != nil {
dockerRegistryOptions.DockerCertPath = sys.DockerCertPath
- authfile = sys.AuthFilePath
+ dockerRegistryOptions.RegistriesConfPath = sys.SystemRegistriesConfPath
}
if _, found := r.URL.Query()["tlsVerify"]; found {
dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
@@ -700,8 +688,8 @@ func SearchImages(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, reports)
}
-// ImagesRemove is the endpoint for image removal.
-func ImagesRemove(w http.ResponseWriter, r *http.Request) {
+// ImagesBatchRemove is the endpoint for batch image removal.
+func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
@@ -722,7 +710,49 @@ func ImagesRemove(w http.ResponseWriter, r *http.Request) {
opts := entities.ImageRemoveOptions{All: query.All, Force: query.Force}
imageEngine := abi.ImageEngine{Libpod: runtime}
- rmReport, rmError := imageEngine.Remove(r.Context(), query.Images, opts)
- report := handlers.LibpodImagesRemoveReport{ImageRemoveReport: *rmReport, Error: rmError.Error()}
+ rmReport, rmErrors := imageEngine.Remove(r.Context(), query.Images, opts)
+
+ strErrs := errorhandling.ErrorsToStrings(rmErrors)
+ report := handlers.LibpodImagesRemoveReport{ImageRemoveReport: *rmReport, Errors: strErrs}
utils.WriteResponse(w, http.StatusOK, report)
}
+
+// ImagesRemove is the endpoint for removing one image.
+func ImagesRemove(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Force bool `schema:"force"`
+ }{
+ Force: false,
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ opts := entities.ImageRemoveOptions{Force: query.Force}
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+ rmReport, rmErrors := imageEngine.Remove(r.Context(), []string{utils.GetName(r)}, opts)
+
+ // In contrast to batch-removal, where we're only setting the exit
+ // code, we need to have another closer look at the errors here and set
+ // the appropriate http status code.
+
+ switch rmReport.ExitCode {
+ case 0:
+ report := handlers.LibpodImagesRemoveReport{ImageRemoveReport: *rmReport, Errors: []string{}}
+ utils.WriteResponse(w, http.StatusOK, report)
+ case 1:
+ // 404 - no such image
+ utils.Error(w, "error removing image", http.StatusNotFound, errorhandling.JoinErrors(rmErrors))
+ case 2:
+ // 409 - conflict error (in use by containers)
+ utils.Error(w, "error removing image", http.StatusConflict, errorhandling.JoinErrors(rmErrors))
+ default:
+ // 500 - internal error
+ utils.Error(w, "failed to remove image", http.StatusInternalServerError, errorhandling.JoinErrors(rmErrors))
+ }
+}
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
index d87ed7eba..aef92368b 100644
--- a/pkg/api/handlers/libpod/manifests.go
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -120,6 +120,10 @@ func ManifestRemove(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
}
func ManifestPush(w http.ResponseWriter, r *http.Request) {
+ // FIXME: parameters are missing (tlsVerify, format).
+ // Also, we should use the ABI function to avoid duplicate code.
+ // Also, support for XRegistryAuth headers are missing.
+
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
@@ -151,6 +155,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) {
}
sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
opts := manifests.PushOptions{
+ Store: runtime.GetStore(),
ImageListSelection: copy2.CopySpecificImages,
SystemContext: sc,
}
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index e8a92e93e..7de285e5e 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -1,39 +1,59 @@
package libpod
import (
+ "encoding/json"
"net/http"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi"
"github.com/containers/libpod/pkg/network"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
-func CreateNetwork(w http.ResponseWriter, r *http.Request) {}
-func ListNetworks(w http.ResponseWriter, r *http.Request) {
+func CreateNetwork(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- config, err := runtime.GetConfig()
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ options := entities.NetworkCreateOptions{}
+ if err := json.NewDecoder(r.Body).Decode(&options); err != nil {
+ utils.Error(w, "unable to marshall input", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ return
+ }
+ query := struct {
+ Name string `schema:"name"`
+ }{
+ // override any golang type defaults
+ }
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+ ic := abi.ContainerEngine{Libpod: runtime}
+ report, err := ic.NetworkCreate(r.Context(), query.Name, options)
if err != nil {
utils.InternalServerError(w, err)
return
}
- configDir := config.Network.NetworkConfigDir
- if len(configDir) < 1 {
- configDir = network.CNIConfigDir
- }
- networks, err := network.LoadCNIConfsFromDir(configDir)
+ utils.WriteResponse(w, http.StatusOK, report)
+
+}
+func ListNetworks(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ options := entities.NetworkListOptions{}
+ ic := abi.ContainerEngine{Libpod: runtime}
+ reports, err := ic.NetworkList(r.Context(), options)
if err != nil {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(w, http.StatusOK, networks)
+ utils.WriteResponse(w, http.StatusOK, reports)
}
func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
- // 200 ok
- // 404 no such
- // 500 internal
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
Force bool `schema:"force"`
@@ -46,22 +66,30 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
return
}
name := utils.GetName(r)
- if err := network.RemoveNetwork(name); err != nil {
+
+ options := entities.NetworkRmOptions{
+ Force: query.Force,
+ }
+ ic := abi.ContainerEngine{Libpod: runtime}
+ reports, err := ic.NetworkRm(r.Context(), []string{name}, options)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ if reports[0].Err != nil {
// If the network cannot be found, we return a 404.
if errors.Cause(err) == network.ErrNetworkNotFound {
utils.Error(w, "Something went wrong", http.StatusNotFound, err)
return
}
- utils.InternalServerError(w, err)
- return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ utils.WriteResponse(w, http.StatusOK, reports)
}
func InspectNetwork(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- Force bool `schema:"force"`
}{
// override any golang type defaults
}
@@ -71,7 +99,9 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) {
return
}
name := utils.GetName(r)
- n, err := network.InspectNetwork(name)
+ options := entities.NetworkInspectOptions{}
+ ic := abi.ContainerEngine{Libpod: runtime}
+ reports, err := ic.NetworkInspect(r.Context(), []string{name}, options)
if err != nil {
// If the network cannot be found, we return a 404.
if errors.Cause(err) == network.ErrNetworkNotFound {
@@ -81,5 +111,5 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(w, http.StatusOK, n)
+ utils.WriteResponse(w, http.StatusOK, reports)
}
diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go
new file mode 100644
index 000000000..1cb5cdb6c
--- /dev/null
+++ b/pkg/api/handlers/libpod/play.go
@@ -0,0 +1,82 @@
+package libpod
+
+import (
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/auth"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi"
+ "github.com/gorilla/schema"
+ "github.com/pkg/errors"
+)
+
+func PlayKube(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Network string `schema:"reference"`
+ TLSVerify bool `schema:"tlsVerify"`
+ }{
+ TLSVerify: true,
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ // Fetch the K8s YAML file from the body, and copy it to a temp file.
+ tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml")
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
+ return
+ }
+ defer os.Remove(tmpfile.Name())
+ if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF {
+ tmpfile.Close()
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file"))
+ return
+ }
+ if err := tmpfile.Close(); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error closing temporary file"))
+ return
+ }
+ authConf, authfile, err := auth.GetCredentials(r)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String()))
+ return
+ }
+ defer auth.RemoveAuthfile(authfile)
+ var username, password string
+ if authConf != nil {
+ username = authConf.Username
+ password = authConf.Password
+ }
+
+ containerEngine := abi.ContainerEngine{Libpod: runtime}
+ options := entities.PlayKubeOptions{
+ Authfile: authfile,
+ Username: username,
+ Password: password,
+ Network: query.Network,
+ Quiet: true,
+ }
+ if _, found := r.URL.Query()["tlsVerify"]; found {
+ options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
+ }
+
+ report, err := containerEngine.PlayKube(r.Context(), tmpfile.Name(), options)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error playing YAML file"))
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, report)
+}
diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go
index 46426eb6b..057fbfb41 100644
--- a/pkg/api/handlers/libpod/swagger.go
+++ b/pkg/api/handlers/libpod/swagger.go
@@ -91,6 +91,34 @@ type swagInfoResponse struct {
Body define.Info
}
+// Network rm
+// swagger:response NetworkRmReport
+type swagNetworkRmReport struct {
+ // in:body
+ Body entities.NetworkRmReport
+}
+
+// Network inspect
+// swagger:response NetworkInspectReport
+type swagNetworkInspectReport struct {
+ // in:body
+ Body []entities.NetworkInspectReport
+}
+
+// Network list
+// swagger:response NetworkListReport
+type swagNetworkListReport struct {
+ // in:body
+ Body []entities.NetworkListReport
+}
+
+// Network create
+// swagger:response NetworkCreateReport
+type swagNetworkCreateReport struct {
+ // in:body
+ Body entities.NetworkCreateReport
+}
+
func ServeSwagger(w http.ResponseWriter, r *http.Request) {
path := DefaultPodmanSwaggerSpec
if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found {
diff --git a/pkg/api/handlers/libpod/system.go b/pkg/api/handlers/libpod/system.go
index 98e33bf10..f575546c9 100644
--- a/pkg/api/handlers/libpod/system.go
+++ b/pkg/api/handlers/libpod/system.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/libpod/pkg/api/handlers/compat"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -69,3 +70,15 @@ func SystemPrune(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusOK, systemPruneReport)
}
+
+func DiskUsage(w http.ResponseWriter, r *http.Request) {
+ // Options are only used by the CLI
+ options := entities.SystemDfOptions{}
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ ic := abi.ContainerEngine{Libpod: runtime}
+ response, err := ic.SystemDf(r.Context(), options)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ }
+ utils.WriteResponse(w, http.StatusOK, response)
+}
diff --git a/pkg/api/handlers/swagger/swagger.go b/pkg/api/handlers/swagger/swagger.go
index 0aceaf5f6..5d125417b 100644
--- a/pkg/api/handlers/swagger/swagger.go
+++ b/pkg/api/handlers/swagger/swagger.go
@@ -56,6 +56,13 @@ type swagLibpodImagesRemoveResponse struct {
Body handlers.LibpodImagesRemoveReport
}
+// PlayKube response
+// swagger:response DocsLibpodPlayKubeResponse
+type swagLibpodPlayKubeResponse struct {
+ // in:body
+ Body entities.PlayKubeReport
+}
+
// Delete response
// swagger:response DocsImageDeleteResponse
type swagImageDeleteResponse struct {
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 58a12ea6a..aa3d0fe91 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -41,7 +41,7 @@ type LibpodImagesPullReport struct {
type LibpodImagesRemoveReport struct {
entities.ImageRemoveReport
// Image removal requires is to return data and an error.
- Error string
+ Errors []string
}
type ContainersPruneReport struct {
@@ -71,10 +71,6 @@ type Container struct {
docker.ContainerCreateConfig
}
-type Version struct {
- docker.Version
-}
-
type DiskUsage struct {
docker.DiskUsage
}
@@ -124,7 +120,7 @@ type CreateContainerConfig struct {
// swagger:model IDResponse
type IDResponse struct {
// ID
- ID string `json:"id"`
+ ID string `json:"Id"`
}
type ContainerTopOKBody struct {
@@ -174,6 +170,11 @@ type ExecCreateResponse struct {
docker.IDResponse
}
+type ExecStartConfig struct {
+ Detach bool `json:"Detach"`
+ Tty bool `json:"Tty"`
+}
+
func ImageToImageSummary(l *libpodImage.Image) (*entities.ImageSummary, error) {
containers, err := l.Containers()
if err != nil {
diff --git a/pkg/api/handlers/utils/errors.go b/pkg/api/handlers/utils/errors.go
index 3253a9be3..c17720694 100644
--- a/pkg/api/handlers/utils/errors.go
+++ b/pkg/api/handlers/utils/errors.go
@@ -63,6 +63,14 @@ func PodNotFound(w http.ResponseWriter, name string, err error) {
Error(w, msg, http.StatusNotFound, err)
}
+func SessionNotFound(w http.ResponseWriter, name string, err error) {
+ if errors.Cause(err) != define.ErrNoSuchExecSession {
+ InternalServerError(w, err)
+ }
+ msg := fmt.Sprintf("No such exec session: %s", name)
+ Error(w, msg, http.StatusNotFound, err)
+}
+
func ContainerNotRunning(w http.ResponseWriter, containerID string, err error) {
msg := fmt.Sprintf("Container %s is not running", containerID)
Error(w, msg, http.StatusConflict, err)
diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go
index b5bd488fb..2f4a54b98 100644
--- a/pkg/api/handlers/utils/handler.go
+++ b/pkg/api/handlers/utils/handler.go
@@ -9,11 +9,55 @@ import (
"os"
"strings"
+ "github.com/blang/semver"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
+type (
+ // VersionTree determines which API endpoint tree for version
+ VersionTree int
+ // VersionLevel determines which API level, current or something from the past
+ VersionLevel int
+)
+
+const (
+ // LibpodTree supports Libpod endpoints
+ LibpodTree = VersionTree(iota)
+ // CompatTree supports Libpod endpoints
+ CompatTree
+
+ // CurrentApiVersion announces what is the current API level
+ CurrentApiVersion = VersionLevel(iota)
+ // MinimalApiVersion announces what is the oldest API level supported
+ MinimalApiVersion
+)
+
+var (
+ // See https://docs.docker.com/engine/api/v1.40/
+ // libpod compat handlers are expected to honor docker API versions
+
+ // ApiVersion provides the current and minimal API versions for compat and libpod endpoint trees
+ // Note: GET|HEAD /_ping is never versioned and provides the API-Version and Libpod-API-Version headers to allow
+ // clients to shop for the Version they wish to support
+ ApiVersion = map[VersionTree]map[VersionLevel]semver.Version{
+ LibpodTree: {
+ CurrentApiVersion: semver.MustParse("1.0.0"),
+ MinimalApiVersion: semver.MustParse("1.0.0"),
+ },
+ CompatTree: {
+ CurrentApiVersion: semver.MustParse("1.40.0"),
+ MinimalApiVersion: semver.MustParse("1.24.0"),
+ },
+ }
+
+ // ErrVersionNotGiven returned when version not given by client
+ ErrVersionNotGiven = errors.New("version not given in URL path")
+ // ErrVersionNotSupported returned when given version is too old
+ ErrVersionNotSupported = errors.New("given version is not supported")
+)
+
// IsLibpodRequest returns true if the request related to a libpod endpoint
// (e.g., /v2/libpod/...).
func IsLibpodRequest(r *http.Request) bool {
@@ -21,6 +65,48 @@ func IsLibpodRequest(r *http.Request) bool {
return len(split) >= 3 && split[2] == "libpod"
}
+// SupportedVersion validates that the version provided by client is included in the given condition
+// https://github.com/blang/semver#ranges provides the details for writing conditions
+// If a version is not given in URL path, ErrVersionNotGiven is returned
+func SupportedVersion(r *http.Request, condition string) (semver.Version, error) {
+ version := semver.Version{}
+ val, ok := mux.Vars(r)["version"]
+ if !ok {
+ return version, ErrVersionNotGiven
+ }
+ safeVal, err := url.PathUnescape(val)
+ if err != nil {
+ return version, errors.Wrapf(err, "unable to unescape given API version: %q", val)
+ }
+ version, err = semver.ParseTolerant(safeVal)
+ if err != nil {
+ return version, errors.Wrapf(err, "unable to parse given API version: %q from %q", safeVal, val)
+ }
+
+ inRange, err := semver.ParseRange(condition)
+ if err != nil {
+ return version, err
+ }
+
+ if inRange(version) {
+ return version, nil
+ }
+ return version, ErrVersionNotSupported
+}
+
+// SupportedVersionWithDefaults validates that the version provided by client valid is supported by server
+// minimal API version <= client path version <= maximum API version focused on the endpoint tree from URL
+func SupportedVersionWithDefaults(r *http.Request) (semver.Version, error) {
+ tree := CompatTree
+ if IsLibpodRequest(r) {
+ tree = LibpodTree
+ }
+
+ return SupportedVersion(r,
+ fmt.Sprintf(">=%s <=%s", ApiVersion[tree][MinimalApiVersion].String(),
+ ApiVersion[tree][CurrentApiVersion].String()))
+}
+
// WriteResponse encodes the given value as JSON or string and renders it for http client
func WriteResponse(w http.ResponseWriter, code int, value interface{}) {
// RFC2616 explicitly states that the following status codes "MUST NOT
diff --git a/pkg/api/handlers/utils/handler_test.go b/pkg/api/handlers/utils/handler_test.go
new file mode 100644
index 000000000..6009432b5
--- /dev/null
+++ b/pkg/api/handlers/utils/handler_test.go
@@ -0,0 +1,139 @@
+package utils
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/gorilla/mux"
+)
+
+func TestSupportedVersion(t *testing.T) {
+ req, err := http.NewRequest("GET",
+ fmt.Sprintf("/v%s/libpod/testing/versions", ApiVersion[LibpodTree][CurrentApiVersion]),
+ nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ req = mux.SetURLVars(req, map[string]string{"version": ApiVersion[LibpodTree][CurrentApiVersion].String()})
+
+ rr := httptest.NewRecorder()
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ _, err := SupportedVersionWithDefaults(r)
+ switch {
+ case errors.Is(err, ErrVersionNotGiven): // for compat endpoints version optional
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprint(w, err.Error())
+ case errors.Is(err, ErrVersionNotSupported): // version given but not supported
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprint(w, err.Error())
+ case err != nil:
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprint(w, err.Error())
+ default: // all good
+ w.WriteHeader(http.StatusOK)
+ fmt.Fprint(w, "OK")
+ }
+ })
+ handler.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ t.Errorf("handler returned wrong status code: got %v want %v",
+ status, http.StatusOK)
+ }
+
+ // Check the response body is what we expect.
+ expected := `OK`
+ if rr.Body.String() != expected {
+ t.Errorf("handler returned unexpected body: got %q want %q",
+ rr.Body.String(), expected)
+ }
+}
+
+func TestUnsupportedVersion(t *testing.T) {
+ version := "999.999.999"
+ req, err := http.NewRequest("GET",
+ fmt.Sprintf("/v%s/libpod/testing/versions", version),
+ nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ req = mux.SetURLVars(req, map[string]string{"version": version})
+
+ rr := httptest.NewRecorder()
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ _, err := SupportedVersionWithDefaults(r)
+ switch {
+ case errors.Is(err, ErrVersionNotGiven): // for compat endpoints version optional
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprint(w, err.Error())
+ case errors.Is(err, ErrVersionNotSupported): // version given but not supported
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprint(w, err.Error())
+ case err != nil:
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprint(w, err.Error())
+ default: // all good
+ w.WriteHeader(http.StatusOK)
+ fmt.Fprint(w, "OK")
+ }
+ })
+ handler.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusBadRequest {
+ t.Errorf("handler returned wrong status code: got %v want %v",
+ status, http.StatusBadRequest)
+ }
+
+ // Check the response body is what we expect.
+ expected := ErrVersionNotSupported.Error()
+ if rr.Body.String() != expected {
+ t.Errorf("handler returned unexpected body: got %q want %q",
+ rr.Body.String(), expected)
+ }
+}
+
+func TestEqualVersion(t *testing.T) {
+ version := "1.30.0"
+ req, err := http.NewRequest("GET",
+ fmt.Sprintf("/v%s/libpod/testing/versions", version),
+ nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ req = mux.SetURLVars(req, map[string]string{"version": version})
+
+ rr := httptest.NewRecorder()
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ _, err := SupportedVersion(r, "=="+version)
+ switch {
+ case errors.Is(err, ErrVersionNotGiven): // for compat endpoints version optional
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprint(w, err.Error())
+ case errors.Is(err, ErrVersionNotSupported): // version given but not supported
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprint(w, err.Error())
+ case err != nil:
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprint(w, err.Error())
+ default: // all good
+ w.WriteHeader(http.StatusOK)
+ fmt.Fprint(w, "OK")
+ }
+ })
+ handler.ServeHTTP(rr, req)
+
+ if status := rr.Code; status != http.StatusOK {
+ t.Errorf("handler returned wrong status code: got %v want %v",
+ status, http.StatusOK)
+ }
+
+ // Check the response body is what we expect.
+ expected := http.StatusText(http.StatusOK)
+ if rr.Body.String() != expected {
+ t.Errorf("handler returned unexpected body: got %q want %q",
+ rr.Body.String(), expected)
+ }
+}
diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go
index 1c67de9db..7fb31a177 100644
--- a/pkg/api/handlers/utils/images.go
+++ b/pkg/api/handlers/utils/images.go
@@ -62,7 +62,6 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) {
}{
// This is where you can override the golang default value for one of fields
}
- // TODO I think all is implemented with a filter?
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
return nil, err
@@ -71,6 +70,10 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) {
if _, found := r.URL.Query()["digests"]; found && query.Digests {
UnSupportedParameter("digests")
}
+ var (
+ images []*image.Image
+ err error
+ )
if len(query.Filters) > 0 {
for k, v := range query.Filters {
@@ -78,11 +81,33 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) {
filters = append(filters, fmt.Sprintf("%s=%s", k, val))
}
}
- return runtime.ImageRuntime().GetImagesWithFilters(filters)
+ images, err = runtime.ImageRuntime().GetImagesWithFilters(filters)
+ if err != nil {
+ return images, err
+ }
} else {
- return runtime.ImageRuntime().GetImages()
+ images, err = runtime.ImageRuntime().GetImages()
+ if err != nil {
+ return images, err
+ }
}
-
+ if query.All {
+ return images, nil
+ }
+ var returnImages []*image.Image
+ for _, img := range images {
+ if len(img.Names()) == 0 {
+ parent, err := img.IsParent(r.Context())
+ if err != nil {
+ return nil, err
+ }
+ if parent {
+ continue
+ }
+ }
+ returnImages = append(returnImages, img)
+ }
+ return returnImages, nil
}
func GetImage(r *http.Request, name string) (*image.Image, error) {
diff --git a/pkg/api/server/register_archive.go b/pkg/api/server/register_archive.go
new file mode 100644
index 000000000..a1d5941bc
--- /dev/null
+++ b/pkg/api/server/register_archive.go
@@ -0,0 +1,171 @@
+package server
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/pkg/api/handlers/compat"
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
+ "github.com/gorilla/mux"
+)
+
+func (s *APIServer) registerAchiveHandlers(r *mux.Router) error {
+ // swagger:operation POST /containers/{name}/archive compat putArchive
+ // ---
+ // summary: Put files into a container
+ // description: Put a tar archive of files into a container
+ // tags:
+ // - containers (compat)
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // description: container name or id
+ // required: true
+ // - in: query
+ // name: path
+ // type: string
+ // description: Path to a directory in the container to extract
+ // required: true
+ // - in: query
+ // name: noOverwriteDirNonDir
+ // type: string
+ // description: if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa (1 or true)
+ // - in: query
+ // name: copyUIDGID
+ // type: string
+ // description: copy UID/GID maps to the dest file or di (1 or true)
+ // - in: body
+ // name: request
+ // description: tarfile of files to copy into the container
+ // schema:
+ // type: string
+ // responses:
+ // 200:
+ // description: no error
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 403:
+ // description: the container rootfs is read-only
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+
+ // swagger:operation GET /containers/{name}/archive compat getArchive
+ // ---
+ // summary: Get files from a container
+ // description: Get a tar archive of files from a container
+ // tags:
+ // - containers (compat)
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // description: container name or id
+ // required: true
+ // - in: query
+ // name: path
+ // type: string
+ // description: Path to a directory in the container to extract
+ // required: true
+ // responses:
+ // 200:
+ // description: no error
+ // schema:
+ // type: string
+ // format: binary
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/containers/{name}/archive"), s.APIHandler(compat.Archive)).Methods(http.MethodGet, http.MethodPost)
+ // Added non version path to URI to support docker non versioned paths
+ r.HandleFunc("/containers/{name}/archive", s.APIHandler(compat.Archive)).Methods(http.MethodGet, http.MethodPost)
+
+ /*
+ Libpod
+ */
+
+ // swagger:operation POST /libpod/containers/{name}/copy libpod libpodPutArchive
+ // ---
+ // summary: Copy files into a container
+ // description: Copy a tar archive of files into a container
+ // tags:
+ // - containers
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // description: container name or id
+ // required: true
+ // - in: query
+ // name: path
+ // type: string
+ // description: Path to a directory in the container to extract
+ // required: true
+ // - in: query
+ // name: pause
+ // type: boolean
+ // description: pause the container while copying (defaults to true)
+ // default: true
+ // - in: body
+ // name: request
+ // description: tarfile of files to copy into the container
+ // schema:
+ // type: string
+ // responses:
+ // 200:
+ // description: no error
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 403:
+ // description: the container rootfs is read-only
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+
+ // swagger:operation GET /libpod/containers/{name}/copy libpod libpodGetArchive
+ // ---
+ // summary: Copy files from a container
+ // description: Copy a tar archive of files from a container
+ // tags:
+ // - containers (compat)
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // description: container name or id
+ // required: true
+ // - in: query
+ // name: path
+ // type: string
+ // description: Path to a directory in the container to extract
+ // required: true
+ // responses:
+ // 200:
+ // description: no error
+ // schema:
+ // type: string
+ // format: binary
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name}/copy"), s.APIHandler(libpod.Archive)).Methods(http.MethodGet, http.MethodPost)
+ r.HandleFunc(VersionedPath("/libpod/containers/{name}/archive"), s.APIHandler(libpod.Archive)).Methods(http.MethodGet, http.MethodPost)
+
+ return nil
+}
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index 378d1e06c..0d78e4cdb 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -584,9 +584,9 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchContainer"
// 500:
// $ref: "#/responses/InternalError"
- r.HandleFunc(VersionedPath("/containers/{name}/resize"), s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
+ r.HandleFunc(VersionedPath("/containers/{name}/resize"), s.APIHandler(compat.ResizeTTY)).Methods(http.MethodPost)
// Added non version path to URI to support docker non versioned paths
- r.HandleFunc("/containers/{name}/resize", s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
+ r.HandleFunc("/containers/{name}/resize", s.APIHandler(compat.ResizeTTY)).Methods(http.MethodPost)
// swagger:operation GET /containers/{name}/export compat exportContainer
// ---
// tags:
@@ -1259,7 +1259,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchContainer"
// 500:
// $ref: "#/responses/InternalError"
- r.HandleFunc(VersionedPath("/libpod/containers/{name}/resize"), s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
+ r.HandleFunc(VersionedPath("/libpod/containers/{name}/resize"), s.APIHandler(compat.ResizeTTY)).Methods(http.MethodPost)
// swagger:operation GET /libpod/containers/{name}/export libpod libpodExportContainer
// ---
// tags:
diff --git a/pkg/api/server/register_events.go b/pkg/api/server/register_events.go
index e909303da..2b85eb169 100644
--- a/pkg/api/server/register_events.go
+++ b/pkg/api/server/register_events.go
@@ -58,6 +58,11 @@ func (s *APIServer) registerEventsHandlers(r *mux.Router) error {
// type: string
// in: query
// description: JSON encoded map[string][]string of constraints
+ // - name: stream
+ // type: boolean
+ // in: query
+ // default: true
+ // description: when false, do not follow events
// responses:
// 200:
// description: returns a string of json data describing an event
diff --git a/pkg/api/server/register_exec.go b/pkg/api/server/register_exec.go
index 71fb50307..1533edba9 100644
--- a/pkg/api/server/register_exec.go
+++ b/pkg/api/server/register_exec.go
@@ -97,10 +97,10 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// properties:
// Detach:
// type: boolean
- // description: Detach from the command
+ // description: Detach from the command. Not presently supported.
// Tty:
// type: boolean
- // description: Allocate a pseudo-TTY
+ // description: Allocate a pseudo-TTY. Presently ignored.
// produces:
// - application/json
// responses:
@@ -109,12 +109,12 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// 404:
// $ref: "#/responses/NoSuchExecInstance"
// 409:
- // description: container is stopped or paused
+ // description: container is not running
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/exec/{id}/start"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/exec/{id}/start"), s.APIHandler(compat.ExecStartHandler)).Methods(http.MethodPost)
// Added non version path to URI to support docker non versioned paths
- r.Handle("/exec/{id}/start", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle("/exec/{id}/start", s.APIHandler(compat.ExecStartHandler)).Methods(http.MethodPost)
// swagger:operation POST /exec/{id}/resize compat resizeExec
// ---
// tags:
@@ -145,15 +145,15 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchExecInstance"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/exec/{id}/resize"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/exec/{id}/resize"), s.APIHandler(compat.ResizeTTY)).Methods(http.MethodPost)
// Added non version path to URI to support docker non versioned paths
- r.Handle("/exec/{id}/resize", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle("/exec/{id}/resize", s.APIHandler(compat.ResizeTTY)).Methods(http.MethodPost)
// swagger:operation GET /exec/{id}/json compat inspectExec
// ---
// tags:
// - exec (compat)
// summary: Inspect an exec instance
- // description: Return low-level information about an exec instance.
+ // description: Return low-level information about an exec instance. Stale (stopped) exec sessions will be auto-removed after inspect runs.
// parameters:
// - in: path
// name: id
@@ -264,10 +264,10 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// properties:
// Detach:
// type: boolean
- // description: Detach from the command
+ // description: Detach from the command. Not presently supported.
// Tty:
// type: boolean
- // description: Allocate a pseudo-TTY
+ // description: Allocate a pseudo-TTY. Presently ignored.
// produces:
// - application/json
// responses:
@@ -276,10 +276,10 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// 404:
// $ref: "#/responses/NoSuchExecInstance"
// 409:
- // description: container is stopped or paused
+ // description: container is not running.
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/exec/{id}/start"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/libpod/exec/{id}/start"), s.APIHandler(compat.ExecStartHandler)).Methods(http.MethodPost)
// swagger:operation POST /libpod/exec/{id}/resize libpod libpodResizeExec
// ---
// tags:
diff --git a/pkg/api/server/register_generate.go b/pkg/api/server/register_generate.go
new file mode 100644
index 000000000..391e60111
--- /dev/null
+++ b/pkg/api/server/register_generate.go
@@ -0,0 +1,41 @@
+package server
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
+ "github.com/gorilla/mux"
+)
+
+func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
+ // swagger:operation GET /libpod/generate/{name:.*}/kube libpod libpodGenerateKube
+ // ---
+ // tags:
+ // - containers
+ // - pods
+ // summary: Play a Kubernetes YAML file.
+ // description: Create and run pods based on a Kubernetes YAML file (pod or service kind).
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: Name or ID of the container or pod.
+ // - in: query
+ // name: service
+ // type: boolean
+ // default: false
+ // description: Generate YAML for a Kubernetes service object.
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: no error
+ // schema:
+ // type: string
+ // format: binary
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/generate/{name:.*}/kube"), s.APIHandler(libpod.GenerateKube)).Methods(http.MethodGet)
+ return nil
+}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index f59dca6f5..83584d0f3 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -8,6 +8,10 @@ import (
"github.com/gorilla/mux"
)
+// TODO
+//
+// * /images/create is missing the "message" and "platform" parameters
+
func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// swagger:operation POST /images/create compat createImage
// ---
@@ -631,13 +635,14 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// required: true
// description: Name of image to push.
// - in: query
- // name: tag
+ // name: destination
// type: string
- // description: The tag to associate with the image on the registry.
+ // description: Allows for pushing the image to a different destintation than the image refers to.
// - in: query
- // name: credentials
- // description: username:password for the registry.
- // type: string
+ // name: tlsVerify
+ // description: Require TLS verification.
+ // type: boolean
+ // default: true
// - in: header
// name: X-Registry-Auth
// type: string
@@ -698,7 +703,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// responses:
// 200:
// $ref: '#/responses/LibpodImageTreeResponse'
- // 401:
+ // 404:
// $ref: '#/responses/NoSuchImage'
// 500:
// $ref: '#/responses/InternalError'
@@ -822,7 +827,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/import"), s.APIHandler(libpod.ImagesImport)).Methods(http.MethodPost)
- // swagger:operation GET /libpod/images/remove libpod libpodImagesRemove
+ // swagger:operation DELETE /libpod/images/remove libpod libpodImagesRemove
// ---
// tags:
// - images
@@ -853,7 +858,37 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: "#/responses/BadParamError"
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/libpod/images/remove"), s.APIHandler(libpod.ImagesRemove)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/libpod/images/remove"), s.APIHandler(libpod.ImagesBatchRemove)).Methods(http.MethodDelete)
+ // swagger:operation DELETE /libpod/images/{name:.*} libpod libpodRemoveImage
+ // ---
+ // tags:
+ // - images
+ // summary: Remove an image from the local storage.
+ // description: Remove an image from the local storage.
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: name or ID of image to remove
+ // - in: query
+ // name: force
+ // type: boolean
+ // description: remove the image even if used by containers or has other tags
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/DocsImageDeleteResponse"
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 404:
+ // $ref: '#/responses/NoSuchImage'
+ // 409:
+ // $ref: '#/responses/ConflictError'
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/libpod/images/{name:.*}"), s.APIHandler(libpod.ImagesRemove)).Methods(http.MethodDelete)
// swagger:operation POST /libpod/images/pull libpod libpodImagesPull
// ---
// tags:
@@ -952,36 +987,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(libpod.SearchImages)).Methods(http.MethodGet)
- // swagger:operation DELETE /libpod/images/{name:.*} libpod libpodRemoveImage
- // ---
- // tags:
- // - images
- // summary: Remove Image
- // description: Delete an image from local store
- // parameters:
- // - in: path
- // name: name:.*
- // type: string
- // required: true
- // description: name or ID of image to delete
- // - in: query
- // name: force
- // type: boolean
- // description: remove the image even if used by containers or has other tags
- // produces:
- // - application/json
- // responses:
- // 200:
- // $ref: "#/responses/DocsImageDeleteResponse"
- // 400:
- // $ref: "#/responses/BadParamError"
- // 404:
- // $ref: '#/responses/NoSuchImage'
- // 409:
- // $ref: '#/responses/ConflictError'
- // 500:
- // $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/libpod/images/{name:.*}"), s.APIHandler(compat.RemoveImage)).Methods(http.MethodDelete)
// swagger:operation GET /libpod/images/{name:.*}/get libpod libpodExportImage
// ---
// tags:
@@ -1188,5 +1193,214 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/images/{name}/changes"), s.APIHandler(compat.Changes)).Methods(http.MethodGet)
+ // swagger:operation POST /libpod/build libpod libpodBuildImage
+ // ---
+ // tags:
+ // - images
+ // summary: Create image
+ // description: Build an image from the given Dockerfile(s)
+ // parameters:
+ // - in: query
+ // name: dockerfile
+ // type: string
+ // default: Dockerfile
+ // description: |
+ // Path within the build context to the `Dockerfile`.
+ // This is ignored if remote is specified and points to an external `Dockerfile`.
+ // - in: query
+ // name: t
+ // type: string
+ // default: latest
+ // description: A name and optional tag to apply to the image in the `name:tag` format.
+ // - in: query
+ // name: extrahosts
+ // type: string
+ // default:
+ // description: |
+ // TBD Extra hosts to add to /etc/hosts
+ // (As of version 1.xx)
+ // - in: query
+ // name: remote
+ // type: string
+ // default:
+ // description: |
+ // A Git repository URI or HTTP/HTTPS context URI.
+ // If the URI points to a single text file, the file’s contents are placed
+ // into a file called Dockerfile and the image is built from that file. If
+ // the URI points to a tarball, the file is downloaded by the daemon and the
+ // contents therein used as the context for the build. If the URI points to a
+ // tarball and the dockerfile parameter is also specified, there must be a file
+ // with the corresponding path inside the tarball.
+ // (As of version 1.xx)
+ // - in: query
+ // name: q
+ // type: boolean
+ // default: false
+ // description: |
+ // Suppress verbose build output
+ // - in: query
+ // name: nocache
+ // type: boolean
+ // default: false
+ // description: |
+ // Do not use the cache when building the image
+ // (As of version 1.xx)
+ // - in: query
+ // name: cachefrom
+ // type: string
+ // default:
+ // description: |
+ // JSON array of images used to build cache resolution
+ // (As of version 1.xx)
+ // - in: query
+ // name: pull
+ // type: boolean
+ // default: false
+ // description: |
+ // Attempt to pull the image even if an older image exists locally
+ // (As of version 1.xx)
+ // - in: query
+ // name: rm
+ // type: boolean
+ // default: true
+ // description: |
+ // Remove intermediate containers after a successful build
+ // (As of version 1.xx)
+ // - in: query
+ // name: forcerm
+ // type: boolean
+ // default: false
+ // description: |
+ // Always remove intermediate containers, even upon failure
+ // (As of version 1.xx)
+ // - in: query
+ // name: memory
+ // type: integer
+ // description: |
+ // Memory is the upper limit (in bytes) on how much memory running containers can use
+ // (As of version 1.xx)
+ // - in: query
+ // name: memswap
+ // type: integer
+ // description: |
+ // MemorySwap limits the amount of memory and swap together
+ // (As of version 1.xx)
+ // - in: query
+ // name: cpushares
+ // type: integer
+ // description: |
+ // CPUShares (relative weight
+ // (As of version 1.xx)
+ // - in: query
+ // name: cpusetcpus
+ // type: string
+ // description: |
+ // CPUSetCPUs in which to allow execution (0-3, 0,1)
+ // (As of version 1.xx)
+ // - in: query
+ // name: cpuperiod
+ // type: integer
+ // description: |
+ // CPUPeriod limits the CPU CFS (Completely Fair Scheduler) period
+ // (As of version 1.xx)
+ // - in: query
+ // name: cpuquota
+ // type: integer
+ // description: |
+ // CPUQuota limits the CPU CFS (Completely Fair Scheduler) quota
+ // (As of version 1.xx)
+ // - in: query
+ // name: buildargs
+ // type: string
+ // default:
+ // description: |
+ // JSON map of string pairs denoting build-time variables.
+ // For example, the build argument `Foo` with the value of `bar` would be encoded in JSON as `["Foo":"bar"]`.
+ //
+ // For example, buildargs={"Foo":"bar"}.
+ //
+ // Note(s):
+ // * This should not be used to pass secrets.
+ // * The value of buildargs should be URI component encoded before being passed to the API.
+ //
+ // (As of version 1.xx)
+ // - in: query
+ // name: shmsize
+ // type: integer
+ // default: 67108864
+ // description: |
+ // ShmSize is the "size" value to use when mounting an shmfs on the container's /dev/shm directory.
+ // Default is 64MB
+ // (As of version 1.xx)
+ // - in: query
+ // name: squash
+ // type: boolean
+ // default: false
+ // description: |
+ // Silently ignored.
+ // Squash the resulting images layers into a single layer
+ // (As of version 1.xx)
+ // - in: query
+ // name: labels
+ // type: string
+ // default:
+ // description: |
+ // JSON map of key, value pairs to set as labels on the new image
+ // (As of version 1.xx)
+ // - in: query
+ // name: networkmode
+ // type: string
+ // default: bridge
+ // description: |
+ // Sets the networking mode for the run commands during build.
+ // Supported standard values are:
+ // * `bridge` limited to containers within a single host, port mapping required for external access
+ // * `host` no isolation between host and containers on this network
+ // * `none` disable all networking for this container
+ // * container:<nameOrID> share networking with given container
+ // ---All other values are assumed to be a custom network's name
+ // (As of version 1.xx)
+ // - in: query
+ // name: platform
+ // type: string
+ // default:
+ // description: |
+ // Platform format os[/arch[/variant]]
+ // (As of version 1.xx)
+ // - in: query
+ // name: target
+ // type: string
+ // default:
+ // description: |
+ // Target build stage
+ // (As of version 1.xx)
+ // - in: query
+ // name: outputs
+ // type: string
+ // default:
+ // description: |
+ // output configuration TBD
+ // (As of version 1.xx)
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: OK (As of version 1.xx)
+ // schema:
+ // type: object
+ // required:
+ // - stream
+ // properties:
+ // stream:
+ // type: string
+ // description: output from build process
+ // example: |
+ // (build details...)
+ // Successfully built 8ba084515c724cbf90d447a63600c0a6
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/build"), s.APIHandler(compat.BuildImage)).Methods(http.MethodPost)
return nil
}
diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go
new file mode 100644
index 000000000..2c60b2b27
--- /dev/null
+++ b/pkg/api/server/register_networks.go
@@ -0,0 +1,190 @@
+package server
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/pkg/api/handlers/compat"
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
+ "github.com/gorilla/mux"
+)
+
+func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
+ // swagger:operation DELETE /networks/{name} compat compatRemoveNetwork
+ // ---
+ // tags:
+ // - networks (compat)
+ // summary: Remove a network
+ // description: Remove a network
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name of the network
+ // produces:
+ // - application/json
+ // responses:
+ // 204:
+ // description: no error
+ // 404:
+ // $ref: "#/responses/NoSuchNetwork"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/networks/{name}"), s.APIHandler(compat.RemoveNetwork)).Methods(http.MethodDelete)
+ r.HandleFunc("/networks/{name}", s.APIHandler(compat.RemoveNetwork)).Methods(http.MethodDelete)
+ // swagger:operation GET /networks/{name}/json compat compatInspectNetwork
+ // ---
+ // tags:
+ // - networks (compat)
+ // summary: Inspect a network
+ // description: Display low level configuration network
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name of the network
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/CompatNetworkInspect"
+ // 404:
+ // $ref: "#/responses/NoSuchNetwork"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/networks/{name}/json"), s.APIHandler(compat.InspectNetwork)).Methods(http.MethodGet)
+ r.HandleFunc("/networks/{name}/json", s.APIHandler(compat.InspectNetwork)).Methods(http.MethodGet)
+ // swagger:operation GET /networks/json compat compatListNetwork
+ // ---
+ // tags:
+ // - networks (compat)
+ // summary: List networks
+ // description: Display summary of network configurations
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/CompatNetworkList"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/networks/json"), s.APIHandler(compat.ListNetworks)).Methods(http.MethodGet)
+ r.HandleFunc("/networks", s.APIHandler(compat.ListNetworks)).Methods(http.MethodGet)
+ // swagger:operation POST /networks/create compat compatCreateNetwork
+ // ---
+ // tags:
+ // - networks (compat)
+ // summary: Create network
+ // description: Create a network configuration
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: body
+ // name: create
+ // description: attributes for creating a container
+ // schema:
+ // $ref: "#/definitions/NetworkCreateRequest"
+ // responses:
+ // 200:
+ // $ref: "#/responses/CompatNetworkCreate"
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/networks/create"), s.APIHandler(compat.CreateNetwork)).Methods(http.MethodPost)
+ r.HandleFunc("/networks/create", s.APIHandler(compat.CreateNetwork)).Methods(http.MethodPost)
+ // swagger:operation DELETE /libpod/networks/{name} libpod libpodRemoveNetwork
+ // ---
+ // tags:
+ // - networks
+ // summary: Remove a network
+ // description: Remove a CNI configured network
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name of the network
+ // - in: query
+ // name: Force
+ // type: boolean
+ // description: remove containers associated with network
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/NetworkRmReport"
+ // 404:
+ // $ref: "#/responses/NoSuchNetwork"
+ // 500:
+ // $ref: "#/responses/InternalError"
+
+ /*
+ Libpod
+ */
+
+ r.HandleFunc(VersionedPath("/libpod/networks/{name}"), s.APIHandler(libpod.RemoveNetwork)).Methods(http.MethodDelete)
+ // swagger:operation GET /libpod/networks/{name}/json libpod libpodInspectNetwork
+ // ---
+ // tags:
+ // - networks
+ // summary: Inspect a network
+ // description: Display low level configuration for a CNI network
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name of the network
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/NetworkInspectReport"
+ // 404:
+ // $ref: "#/responses/NoSuchNetwork"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/networks/{name}/json"), s.APIHandler(libpod.InspectNetwork)).Methods(http.MethodGet)
+ // swagger:operation GET /libpod/networks/json libpod libpodListNetwork
+ // ---
+ // tags:
+ // - networks
+ // summary: List networks
+ // description: Display summary of network configurations
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/NetworkListReport"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/networks/json"), s.APIHandler(libpod.ListNetworks)).Methods(http.MethodGet)
+ // swagger:operation POST /libpod/networks/create libpod libpodCreateNetwork
+ // ---
+ // tags:
+ // - networks
+ // summary: Create network
+ // description: Create a new CNI network configuration
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: query
+ // name: name
+ // type: string
+ // description: optional name for new network
+ // - in: body
+ // name: create
+ // description: attributes for creating a container
+ // schema:
+ // $ref: "#/definitions/NetworkCreateOptions"
+ // responses:
+ // 200:
+ // $ref: "#/responses/NetworkCreateReport"
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/networks/create"), s.APIHandler(libpod.CreateNetwork)).Methods(http.MethodPost)
+ return nil
+}
diff --git a/pkg/api/server/register_play.go b/pkg/api/server/register_play.go
new file mode 100644
index 000000000..d04879c19
--- /dev/null
+++ b/pkg/api/server/register_play.go
@@ -0,0 +1,42 @@
+package server
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
+ "github.com/gorilla/mux"
+)
+
+func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
+ // swagger:operation POST /libpod/play/kube libpod libpodPlayKube
+ // ---
+ // tags:
+ // - containers
+ // - pods
+ // summary: Play a Kubernetes YAML file.
+ // description: Create and run pods based on a Kubernetes YAML file (pod or service kind).
+ // parameters:
+ // - in: query
+ // name: network
+ // type: string
+ // description: Connect the pod to this network.
+ // - in: query
+ // name: tlsVerify
+ // type: boolean
+ // default: true
+ // description: Require HTTPS and verify signatures when contating registries.
+ // - in: body
+ // name: request
+ // description: Kubernetes YAML file.
+ // schema:
+ // type: string
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/DocsLibpodPlayKubeResponse"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/play/kube"), s.APIHandler(libpod.PlayKube)).Methods(http.MethodPost)
+ return nil
+}
diff --git a/pkg/api/server/register_system.go b/pkg/api/server/register_system.go
index 7375a75c1..118ad2d08 100644
--- a/pkg/api/server/register_system.go
+++ b/pkg/api/server/register_system.go
@@ -12,7 +12,7 @@ func (s *APIServer) registerSystemHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/system/df"), s.APIHandler(compat.GetDiskUsage)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths
r.Handle("/system/df", s.APIHandler(compat.GetDiskUsage)).Methods(http.MethodGet)
- // Swagger:operation POST /libpod/system/prune libpod pruneSystem
+ // swagger:operation POST /libpod/system/prune libpod pruneSystem
// ---
// tags:
// - system
@@ -27,6 +27,19 @@ func (s *APIServer) registerSystemHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/system/prune"), s.APIHandler(libpod.SystemPrune)).Methods(http.MethodPost)
-
+ // swagger:operation GET /libpod/system/df libpod df
+ // ---
+ // tags:
+ // - system
+ // summary: Show disk usage
+ // description: Return information about disk usage for containers, images, and volumes
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: '#/responses/SystemDiskUse'
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/system/df"), s.APIHandler(libpod.DiskUsage)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/api/server/register_version.go b/pkg/api/server/register_version.go
index 25cacbc61..30289ffe3 100644
--- a/pkg/api/server/register_version.go
+++ b/pkg/api/server/register_version.go
@@ -8,7 +8,28 @@ import (
)
func (s *APIServer) registerVersionHandlers(r *mux.Router) error {
+ // swagger:operation GET /version compat CompatSystemVersion
+ // ---
+ // summary: Component Version information
+ // tags:
+ // - system (compat)
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/Version"
r.Handle("/version", s.APIHandler(compat.VersionHandler)).Methods(http.MethodGet)
r.Handle(VersionedPath("/version"), s.APIHandler(compat.VersionHandler)).Methods(http.MethodGet)
+ // swagger:operation GET /libpod/version libpod SystemVersion
+ // ---
+ // summary: Component Version information
+ // tags:
+ // - system
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/Version"
+ r.Handle(VersionedPath("/libpod/version"), s.APIHandler(compat.VersionHandler)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index ce2d152e0..499a4c58a 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -92,18 +92,30 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
},
)
+ router.MethodNotAllowedHandler = http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ // We can track user errors...
+ logrus.Infof("Failed Request: (%d:%s) for %s:'%s'", http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed), r.Method, r.URL.String())
+ http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
+ },
+ )
+
for _, fn := range []func(*mux.Router) error{
server.registerAuthHandlers,
+ server.registerAchiveHandlers,
server.registerContainersHandlers,
server.registerDistributionHandlers,
server.registerEventsHandlers,
server.registerExecHandlers,
+ server.registerGenerateHandlers,
server.registerHealthCheckHandlers,
server.registerImagesHandlers,
server.registerInfoHandlers,
server.registerManifestHandlers,
server.registerMonitorHandlers,
+ server.registerNetworkHandlers,
server.registerPingHandlers,
+ server.registerPlayHandlers,
server.registerPluginsHandlers,
server.registerPodsHandlers,
server.RegisterSwaggerHandlers,
diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go
index 75dcc71a6..c463f809e 100644
--- a/pkg/api/server/swagger.go
+++ b/pkg/api/server/swagger.go
@@ -24,6 +24,15 @@ type swagErrNoSuchContainer struct {
}
}
+// No such network
+// swagger:response NoSuchNetwork
+type swagErrNoSuchNetwork struct {
+ // in:body
+ Body struct {
+ entities.ErrorModel
+ }
+}
+
// No such exec instance
// swagger:response NoSuchExecInstance
type swagErrNoSuchExecInstance struct {
@@ -130,6 +139,13 @@ type swagImageSummary struct {
Body []entities.ImageSummary
}
+// Registries summary
+// swagger:response DocsRegistriesList
+type swagRegistriesList struct {
+ // in:body
+ Body entities.ListRegistriesReport
+}
+
// List Containers
// swagger:response DocsListContainer
type swagListContainers struct {
@@ -181,3 +197,30 @@ type swagHealthCheckRunResponse struct {
define.HealthCheckResults
}
}
+
+// Version
+// swagger:response Version
+type swagVersion struct {
+ // in:body
+ Body struct {
+ entities.SystemVersionReport
+ }
+}
+
+// Disk usage
+// swagger:response SystemDiskUse
+type swagDiskUseResponse struct {
+ // in:body
+ Body struct {
+ entities.SystemDfReport
+ }
+}
+
+// Prune report
+// swagger:response SystemPruneReport
+type swagSystemPruneReport struct {
+ // in:body
+ Body struct {
+ entities.SystemPruneReport
+ }
+}
diff --git a/pkg/api/tags.yaml b/pkg/api/tags.yaml
index 5b5d9f5bb..f86f8dbea 100644
--- a/pkg/api/tags.yaml
+++ b/pkg/api/tags.yaml
@@ -5,9 +5,11 @@ tags:
description: Actions related to exec
- name: images
description: Actions related to images
- - name: pods
- description: Actions related to manifests
- name: manifests
+ description: Actions related to manifests
+ - name: networks
+ description: Actions related to networks
+ - name: pods
description: Actions related to pods
- name: volumes
description: Actions related to volumes
@@ -19,5 +21,7 @@ tags:
description: Actions related to exec for the compatibility endpoints
- name: images (compat)
description: Actions related to images for the compatibility endpoints
+ - name: networks (compat)
+ description: Actions related to compatibility networks
- name: system (compat)
description: Actions related to Podman and compatibility engines