summaryrefslogtreecommitdiff
path: root/pkg/api
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api')
-rw-r--r--pkg/api/handlers/compat/containers.go18
-rw-r--r--pkg/api/handlers/compat/containers_create.go13
-rw-r--r--pkg/api/handlers/compat/containers_restart.go16
-rw-r--r--pkg/api/handlers/compat/exec.go107
-rw-r--r--pkg/api/handlers/compat/images.go10
-rw-r--r--pkg/api/handlers/compat/images_history.go2
-rw-r--r--pkg/api/handlers/compat/images_remove.go22
-rw-r--r--pkg/api/handlers/compat/info.go8
-rw-r--r--pkg/api/handlers/libpod/containers.go6
-rw-r--r--pkg/api/handlers/libpod/images.go96
-rw-r--r--pkg/api/handlers/libpod/manifests.go166
-rw-r--r--pkg/api/handlers/libpod/networks.go2
-rw-r--r--pkg/api/handlers/libpod/pods.go193
-rw-r--r--pkg/api/handlers/libpod/swagger.go86
-rw-r--r--pkg/api/handlers/libpod/volumes.go155
-rw-r--r--pkg/api/handlers/swagger.go6
-rw-r--r--pkg/api/handlers/types.go105
-rw-r--r--pkg/api/handlers/utils/containers.go2
-rw-r--r--pkg/api/handlers/utils/pods.go84
-rw-r--r--pkg/api/server/register_events.go2
-rw-r--r--pkg/api/server/register_exec.go16
-rw-r--r--pkg/api/server/register_images.go13
-rw-r--r--pkg/api/server/register_manifest.go145
-rw-r--r--pkg/api/server/register_pods.go30
-rw-r--r--pkg/api/server/register_swagger.go15
-rw-r--r--pkg/api/server/register_volumes.go10
-rw-r--r--pkg/api/server/server.go94
-rw-r--r--pkg/api/server/swagger.go25
-rw-r--r--pkg/api/tags.yaml2
29 files changed, 1054 insertions, 395 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 1298e7fa4..2ce113d30 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -7,7 +7,6 @@ import (
"strconv"
"strings"
"sync"
- "syscall"
"time"
"github.com/containers/libpod/libpod"
@@ -15,6 +14,7 @@ import (
"github.com/containers/libpod/libpod/logs"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/signal"
"github.com/containers/libpod/pkg/util"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -87,7 +87,7 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- if _, found := r.URL.Query()["limit"]; found {
+ if _, found := r.URL.Query()["limit"]; found && query.Limit != -1 {
last := query.Limit
if len(containers) > last {
containers = containers[len(containers)-last:]
@@ -145,14 +145,20 @@ func KillContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- Signal syscall.Signal `schema:"signal"`
+ Signal string `schema:"signal"`
}{
- Signal: syscall.SIGKILL,
+ Signal: "KILL",
}
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
}
+
+ sig, err := signal.ParseSignalNameOrNumber(query.Signal)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
name := utils.GetName(r)
con, err := runtime.LookupContainer(name)
if err != nil {
@@ -172,7 +178,7 @@ func KillContainer(w http.ResponseWriter, r *http.Request) {
return
}
- err = con.Kill(uint(query.Signal))
+ err = con.Kill(uint(sig))
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "unable to kill Container %s", name))
}
@@ -326,7 +332,6 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
builder.WriteRune(' ')
}
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 {
@@ -335,7 +340,6 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
if _, err := fmt.Fprint(w, builder.String()); err != nil {
log.Errorf("unable to write builder string: %q", err)
}
-
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index 6b8440fc2..12af40876 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -6,8 +6,8 @@ import (
"net/http"
"strings"
+ "github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/libpod/define"
image2 "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
@@ -46,7 +46,12 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "NewFromLocal()"))
return
}
- cc, err := makeCreateConfig(input, newImage)
+ defaultContainerConfig, err := runtime.GetConfig()
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "GetConfig()"))
+ return
+ }
+ cc, err := makeCreateConfig(defaultContainerConfig, input, newImage)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "makeCreatConfig()"))
return
@@ -55,7 +60,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.CreateContainer(r.Context(), w, runtime, &cc)
}
-func makeCreateConfig(input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) {
+func makeCreateConfig(defaultContainerConfig *config.Config, input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) {
var (
err error
init bool
@@ -76,7 +81,7 @@ func makeCreateConfig(input handlers.CreateContainerConfig, newImage *image2.Ima
workDir = input.WorkingDir
}
- stopTimeout := uint(define.CtrRemoveTimeout)
+ stopTimeout := defaultContainerConfig.Engine.StopTimeout
if input.StopTimeout != nil {
stopTimeout = uint(*input.StopTimeout)
}
diff --git a/pkg/api/handlers/compat/containers_restart.go b/pkg/api/handlers/compat/containers_restart.go
index 5b8fafaa4..343bf96d2 100644
--- a/pkg/api/handlers/compat/containers_restart.go
+++ b/pkg/api/handlers/compat/containers_restart.go
@@ -1,11 +1,9 @@
package compat
import (
- "fmt"
"net/http"
"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"
@@ -32,20 +30,6 @@ func RestartContainer(w http.ResponseWriter, r *http.Request) {
return
}
- state, err := con.State()
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
-
- // FIXME: This is not in the swagger.yml...
- // If the Container is stopped already, send a 409
- if state == define.ContainerStateStopped || state == define.ContainerStateExited {
- msg := fmt.Sprintf("Container %s is not running", name)
- utils.Error(w, msg, http.StatusConflict, errors.New(msg))
- return
- }
-
timeout := con.StopTimeout()
if _, found := r.URL.Query()["t"]; found {
timeout = uint(query.Timeout)
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
new file mode 100644
index 000000000..ec1a8ac96
--- /dev/null
+++ b/pkg/api/handlers/compat/exec.go
@@ -0,0 +1,107 @@
+package compat
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+
+ "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/gorilla/mux"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// ExecCreateHandler creates an exec session for a given container.
+func ExecCreateHandler(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ input := new(handlers.ExecCreateConfig)
+ if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "error decoding request body as JSON"))
+ return
+ }
+
+ ctrName := utils.GetName(r)
+ ctr, err := runtime.LookupContainer(ctrName)
+ if err != nil {
+ utils.ContainerNotFound(w, ctrName, err)
+ return
+ }
+
+ libpodConfig := new(libpod.ExecConfig)
+ libpodConfig.Command = input.Cmd
+ libpodConfig.Terminal = input.Tty
+ libpodConfig.AttachStdin = input.AttachStdin
+ libpodConfig.AttachStderr = input.AttachStderr
+ libpodConfig.AttachStdout = input.AttachStdout
+ if input.DetachKeys != "" {
+ libpodConfig.DetachKeys = &input.DetachKeys
+ }
+ libpodConfig.Environment = make(map[string]string)
+ for _, envStr := range input.Env {
+ split := strings.SplitN(envStr, "=", 2)
+ if len(split) != 2 {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, errors.Errorf("environment variable %q badly formed, must be key=value", envStr))
+ return
+ }
+ libpodConfig.Environment[split[0]] = split[1]
+ }
+ libpodConfig.WorkDir = input.WorkingDir
+ libpodConfig.Privileged = input.Privileged
+ libpodConfig.User = input.User
+
+ sessID, err := ctr.ExecCreate(libpodConfig)
+ if err != nil {
+ if errors.Cause(err) == define.ErrCtrStateInvalid {
+ // Check if the container is paused. If so, return a 409
+ state, err := ctr.State()
+ if err == nil {
+ // Ignore the error != nil case. We're already
+ // throwing an InternalServerError below.
+ if state == define.ContainerStatePaused {
+ utils.Error(w, "Container is paused", http.StatusConflict, errors.Errorf("cannot create exec session as container %s is paused", ctr.ID()))
+ return
+ }
+ }
+ }
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ resp := new(handlers.ExecCreateResponse)
+ resp.ID = sessID
+
+ utils.WriteResponse(w, http.StatusCreated, resp)
+}
+
+// ExecInspectHandler inspects a given exec session.
+func ExecInspectHandler(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ sessionID := mux.Vars(r)["id"]
+ 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("Inspecting exec session %s of container %s", sessionID, sessionCtr.ID())
+
+ session, err := sessionCtr.ExecSession(sessionID)
+ if err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "error retrieving exec session %s from container %s", sessionID, sessionCtr.ID()))
+ return
+ }
+
+ inspectOut, err := session.Inspect()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, inspectOut)
+}
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index b18687bf9..ea9cbd691 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/domain/entities"
"github.com/containers/libpod/pkg/util"
"github.com/docker/docker/api/types"
"github.com/gorilla/schema"
@@ -63,6 +64,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
query := struct {
+ All bool
Filters map[string][]string `schema:"filters"`
}{
// This is where you can override the golang default value for one of fields
@@ -79,7 +81,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
filters = append(filters, fmt.Sprintf("%s=%s", k, val))
}
}
- pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), false, filters)
+ pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters)
if err != nil {
utils.InternalServerError(w, err)
return
@@ -127,13 +129,13 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return
}
- sc := image2.GetSystemContext(rtc.SignaturePolicyPath, "", false)
+ sc := image2.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
tag := "latest"
options := libpod.ContainerCommitOptions{
Pause: true,
}
options.CommitOptions = buildah.CommitOptions{
- SignaturePolicyPath: rtc.SignaturePolicyPath,
+ SignaturePolicyPath: rtc.Engine.SignaturePolicyPath,
ReportWriter: os.Stderr,
SystemContext: sc,
PreferredManifestType: manifest.DockerV2Schema2MediaType,
@@ -305,7 +307,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images"))
return
}
- var summaries = make([]*handlers.ImageSummary, len(images))
+ var summaries = make([]*entities.ImageSummary, len(images))
for j, img := range images {
is, err := handlers.ImageToImageSummary(img)
if err != nil {
diff --git a/pkg/api/handlers/compat/images_history.go b/pkg/api/handlers/compat/images_history.go
index 04304caa4..afadf4c48 100644
--- a/pkg/api/handlers/compat/images_history.go
+++ b/pkg/api/handlers/compat/images_history.go
@@ -28,7 +28,7 @@ func HistoryImage(w http.ResponseWriter, r *http.Request) {
for _, h := range history {
l := handlers.HistoryResponse{
ID: h.ID,
- Created: h.Created.UnixNano(),
+ Created: h.Created.Unix(),
CreatedBy: h.CreatedBy,
Tags: h.Tags,
Size: h.Size,
diff --git a/pkg/api/handlers/compat/images_remove.go b/pkg/api/handlers/compat/images_remove.go
index 3d346543e..ed0153529 100644
--- a/pkg/api/handlers/compat/images_remove.go
+++ b/pkg/api/handlers/compat/images_remove.go
@@ -36,17 +36,23 @@ func RemoveImage(w http.ResponseWriter, r *http.Request) {
return
}
- _, err = runtime.RemoveImage(r.Context(), newImage, query.Force)
+ results, err := runtime.RemoveImage(r.Context(), newImage, query.Force)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
}
- // TODO
- // This will need to be fixed for proper response, like Deleted: and Untagged:
- m := make(map[string]string)
- m["Deleted"] = newImage.ID()
- foo := []map[string]string{}
- foo = append(foo, m)
- utils.WriteResponse(w, http.StatusOK, foo)
+
+ response := make([]map[string]string, 0, len(results.Untagged)+1)
+ deleted := make(map[string]string, 1)
+ deleted["Deleted"] = results.Deleted
+ response = append(response, deleted)
+
+ for _, u := range results.Untagged {
+ untagged := make(map[string]string, 1)
+ untagged["Untagged"] = u
+ response = append(response, untagged)
+ }
+
+ utils.WriteResponse(w, http.StatusOK, response)
}
diff --git a/pkg/api/handlers/compat/info.go b/pkg/api/handlers/compat/info.go
index 30b49948d..104d0793b 100644
--- a/pkg/api/handlers/compat/info.go
+++ b/pkg/api/handlers/compat/info.go
@@ -9,8 +9,8 @@ import (
"strings"
"time"
+ "github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/libpod/config"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
@@ -60,7 +60,7 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
CPUCfsQuota: sysInfo.CPUCfsQuota,
CPUSet: sysInfo.Cpuset,
CPUShares: sysInfo.CPUShares,
- CgroupDriver: configInfo.CgroupManager,
+ CgroupDriver: configInfo.Engine.CgroupManager,
ClusterAdvertise: "",
ClusterStore: "",
ContainerdCommit: docker.Commit{},
@@ -69,7 +69,7 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
ContainersRunning: stateInfo[define.ContainerStateRunning],
ContainersStopped: stateInfo[define.ContainerStateStopped] + stateInfo[define.ContainerStateExited],
Debug: log.IsLevelEnabled(log.DebugLevel),
- DefaultRuntime: configInfo.OCIRuntime,
+ DefaultRuntime: configInfo.Engine.OCIRuntime,
DockerRootDir: storeInfo["GraphRoot"].(string),
Driver: storeInfo["GraphDriverName"].(string),
DriverStatus: getGraphStatus(storeInfo),
@@ -152,7 +152,7 @@ func getSecOpts(sysInfo *sysinfo.SysInfo) []string {
func getRuntimes(configInfo *config.Config) map[string]docker.Runtime {
var runtimes = map[string]docker.Runtime{}
- for name, paths := range configInfo.OCIRuntimes {
+ for name, paths := range configInfo.Engine.OCIRuntimes {
runtimes[name] = docker.Runtime{
Path: paths[0],
Args: nil,
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index 8020c391d..cdc34004f 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -21,8 +21,12 @@ func ContainerExists(w http.ResponseWriter, r *http.Request) {
name := utils.GetName(r)
_, err := runtime.LookupContainer(name)
if err != nil {
- utils.ContainerNotFound(w, name, err)
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ utils.ContainerNotFound(w, name, err)
+ }
+ utils.InternalServerError(w, err)
return
+
}
utils.WriteResponse(w, http.StatusNoContent, "")
}
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index cfd3b993e..4b24d7d9f 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -10,14 +10,18 @@ import (
"strconv"
"strings"
+ "github.com/containers/buildah"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference"
+ "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
+ 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/domain/entities"
"github.com/containers/libpod/pkg/util"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -98,7 +102,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images"))
return
}
- var summaries = make([]*handlers.ImageSummary, len(images))
+ var summaries = make([]*entities.ImageSummary, len(images))
for j, img := range images {
is, err := handlers.ImageToImageSummary(img)
if err != nil {
@@ -106,7 +110,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
return
}
// libpod has additional fields that we need to populate.
- is.CreatedTime = img.Created()
+ is.Created = img.Created().Unix()
is.ReadOnly = img.IsReadOnly()
summaries[j] = is
}
@@ -115,12 +119,12 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
func PruneImages(w http.ResponseWriter, r *http.Request) {
var (
- all bool
err error
)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
+ All bool `schema:"all"`
Filters map[string][]string `schema:"filters"`
}{
// override any golang type defaults
@@ -136,7 +140,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
if _, found := r.URL.Query()["filters"]; found {
dangling := query.Filters["all"]
if len(dangling) > 0 {
- all, err = strconv.ParseBool(query.Filters["all"][0])
+ query.All, err = strconv.ParseBool(query.Filters["all"][0])
if err != nil {
utils.InternalServerError(w, err)
return
@@ -148,7 +152,8 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
}
}
- cids, err := runtime.ImageRuntime().PruneImages(r.Context(), all, libpodFilters)
+
+ cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
@@ -416,3 +421,84 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, res)
}
+
+func CommitContainer(w http.ResponseWriter, r *http.Request) {
+ var (
+ destImage string
+ mimeType string
+ )
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ query := struct {
+ Author string `schema:"author"`
+ Changes []string `schema:"changes"`
+ Comment string `schema:"comment"`
+ Container string `schema:"container"`
+ Format string `schema:"format"`
+ Pause bool `schema:"pause"`
+ Repo string `schema:"repo"`
+ Tag string `schema:"tag"`
+ }{
+ Format: "oci",
+ }
+
+ 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
+ }
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ utils.Error(w, "failed to get runtime config", http.StatusInternalServerError, errors.Wrap(err, "failed to get runtime config"))
+ return
+ }
+ sc := image2.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
+ tag := "latest"
+ options := libpod.ContainerCommitOptions{
+ Pause: true,
+ }
+ switch query.Format {
+ case "oci":
+ mimeType = buildah.OCIv1ImageManifest
+ if len(query.Comment) > 0 {
+ utils.InternalServerError(w, errors.New("messages are only compatible with the docker image format (-f docker)"))
+ return
+ }
+ case "docker":
+ mimeType = manifest.DockerV2Schema2MediaType
+ default:
+ utils.InternalServerError(w, errors.Errorf("unrecognized image format %q", query.Format))
+ return
+ }
+ options.CommitOptions = buildah.CommitOptions{
+ SignaturePolicyPath: rtc.Engine.SignaturePolicyPath,
+ ReportWriter: os.Stderr,
+ SystemContext: sc,
+ PreferredManifestType: mimeType,
+ }
+
+ if len(query.Tag) > 0 {
+ tag = query.Tag
+ }
+ options.Message = query.Comment
+ options.Author = query.Author
+ options.Pause = query.Pause
+ options.Changes = query.Changes
+ ctr, err := runtime.LookupContainer(query.Container)
+ if err != nil {
+ utils.Error(w, "failed to lookup container", http.StatusNotFound, err)
+ return
+ }
+
+ // I know mitr hates this ... but doing for now
+ if len(query.Repo) > 1 {
+ destImage = fmt.Sprintf("%s:%s", query.Repo, tag)
+ }
+
+ commitImage, err := ctr.Commit(r.Context(), destImage, options)
+ if err != nil && !strings.Contains(err.Error(), "is not running") {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "CommitFailure"))
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: commitImage.ID()}) // nolint
+}
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
new file mode 100644
index 000000000..d87ed7eba
--- /dev/null
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -0,0 +1,166 @@
+package libpod
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/containers/buildah/manifests"
+ copy2 "github.com/containers/image/v5/copy"
+ "github.com/containers/image/v5/transports/alltransports"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/gorilla/schema"
+ "github.com/opencontainers/go-digest"
+ "github.com/pkg/errors"
+)
+
+func ManifestCreate(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Name []string `schema:"name"`
+ Image []string `schema:"image"`
+ All bool `schema:"all"`
+ }{
+ // Add defaults here once needed.
+ }
+ 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
+ }
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
+ manID, err := image.CreateManifestList(runtime.ImageRuntime(), *sc, query.Name, query.Image, query.All)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: manID})
+}
+
+func ManifestInspect(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := utils.GetName(r)
+ newImage, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.ImageNotFound(w, name, err)
+ return
+ }
+ data, err := newImage.InspectManifest()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, data)
+}
+
+func ManifestAdd(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ var manifestInput image.ManifestAddOpts
+ if err := json.NewDecoder(r.Body).Decode(&manifestInput); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ return
+ }
+ name := utils.GetName(r)
+ newImage, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.ImageNotFound(w, name, err)
+ return
+ }
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
+ newID, err := newImage.AddManifest(*sc, manifestInput)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
+}
+
+func ManifestRemove(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Digest string `schema:"digest"`
+ }{
+ // Add defaults here once needed.
+ }
+ name := utils.GetName(r)
+ 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
+ }
+ newImage, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.ImageNotFound(w, name, err)
+ return
+ }
+ d, err := digest.Parse(query.Digest)
+ if err != nil {
+ utils.Error(w, "invalid digest", http.StatusBadRequest, err)
+ return
+ }
+ newID, err := newImage.RemoveManifest(d)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID})
+}
+func ManifestPush(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ All bool `schema:"all"`
+ Destination string `schema:"destination"`
+ }{
+ // Add defaults here once needed.
+ }
+ 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
+ }
+ name := utils.GetName(r)
+ newImage, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.ImageNotFound(w, name, err)
+ return
+ }
+ dest, err := alltransports.ParseImageName(query.Destination)
+ if err != nil {
+ utils.Error(w, "invalid destination parameter", http.StatusBadRequest, errors.Errorf("invalid destination parameter %q", query.Destination))
+ return
+ }
+ rtc, err := runtime.GetConfig()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false)
+ opts := manifests.PushOptions{
+ ImageListSelection: copy2.CopySpecificImages,
+ SystemContext: sc,
+ }
+ if query.All {
+ opts.ImageListSelection = copy2.CopyAllImages
+ }
+ newD, err := newImage.PushManifest(dest, opts)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, newD.String())
+}
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index e3dbe3b35..e8a92e93e 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -18,7 +18,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- configDir := config.CNIConfigDir
+ configDir := config.Network.NetworkConfigDir
if len(configDir) < 1 {
configDir = network.CNIConfigDir
}
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index f93c8f8d5..7e9c2e2c0 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -4,14 +4,13 @@ import (
"encoding/json"
"fmt"
"net/http"
- "strings"
- "github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/shared/parse"
"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"
+ "github.com/containers/libpod/pkg/specgen"
"github.com/containers/libpod/pkg/util"
"github.com/gorilla/schema"
"github.com/pkg/errors"
@@ -20,76 +19,14 @@ import (
func PodCreate(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
- options []libpod.PodCreateOption
err error
)
- labels := make(map[string]string)
- input := handlers.PodCreateConfig{}
- if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
+ var psg specgen.PodSpecGenerator
+ if err := json.NewDecoder(r.Body).Decode(&psg); err != nil {
+ utils.Error(w, "Failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen"))
return
}
- if len(input.InfraCommand) > 0 || len(input.InfraImage) > 0 {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError,
- errors.New("infra-command and infra-image are not implemented yet"))
- return
- }
- // TODO long term we should break the following out of adapter and into libpod proper
- // so that the cli and api can share the creation of a pod with the same options
- if len(input.CGroupParent) > 0 {
- options = append(options, libpod.WithPodCgroupParent(input.CGroupParent))
- }
-
- if len(input.Labels) > 0 {
- labels, err = parse.GetAllLabels([]string{}, input.Labels)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- }
-
- if len(labels) != 0 {
- options = append(options, libpod.WithPodLabels(labels))
- }
-
- if len(input.Name) > 0 {
- options = append(options, libpod.WithPodName(input.Name))
- }
-
- if len(input.Hostname) > 0 {
- options = append(options, libpod.WithPodHostname(input.Hostname))
- }
-
- if input.Infra {
- // TODO infra-image and infra-command are not supported in the libpod API yet. Will fix
- // when implemented in libpod
- options = append(options, libpod.WithInfraContainer())
- sharedNamespaces := shared.DefaultKernelNamespaces
- if len(input.Share) > 0 {
- sharedNamespaces = input.Share
- }
- nsOptions, err := shared.GetNamespaceOptions(strings.Split(sharedNamespaces, ","))
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- options = append(options, nsOptions...)
- }
-
- if len(input.Publish) > 0 {
- portBindings, err := shared.CreatePortBindings(input.Publish)
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
- return
- }
- options = append(options, libpod.WithInfraContainerPorts(portBindings))
-
- }
- // always have containers use pod cgroups
- // User Opt out is not yet supported
- options = append(options, libpod.WithPodCgroups())
-
- pod, err := runtime.NewPod(r.Context(), options...)
+ pod, err := psg.MakePod(runtime)
if err != nil {
http_code := http.StatusInternalServerError
if errors.Cause(err) == define.ErrPodExists {
@@ -102,10 +39,6 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
}
func Pods(w http.ResponseWriter, r *http.Request) {
- var (
- runtime = r.Context().Value("runtime").(*libpod.Runtime)
- podInspectData []*libpod.PodInspect
- )
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
Filters map[string][]string `schema:"filters"`
@@ -118,25 +51,12 @@ func Pods(w http.ResponseWriter, r *http.Request) {
return
}
- if len(query.Filters) > 0 {
- utils.Error(w, "filters are not implemented yet", http.StatusInternalServerError, define.ErrNotImplemented)
- return
- }
-
- pods, err := runtime.GetAllPods()
+ pods, err := utils.GetPods(w, r)
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- for _, pod := range pods {
- data, err := pod.Inspect()
- if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
- return
- }
- podInspectData = append(podInspectData, data)
- }
- utils.WriteResponse(w, http.StatusOK, podInspectData)
+ utils.WriteResponse(w, http.StatusOK, pods)
}
func PodInspect(w http.ResponseWriter, r *http.Request) {
@@ -160,6 +80,8 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
stopError error
runtime = r.Context().Value("runtime").(*libpod.Runtime)
decoder = r.Context().Value("decoder").(*schema.Decoder)
+ responses map[string]error
+ errs []error
)
query := struct {
Timeout int `schema:"t"`
@@ -190,18 +112,28 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
}
if query.Timeout > 0 {
- _, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout)
+ responses, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout)
} else {
- _, stopError = pod.Stop(r.Context(), false)
+ responses, stopError = pod.Stop(r.Context(), false)
}
if stopError != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, err := range responses {
+ errs = append(errs, err)
+ }
+ report := entities.PodStopReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodStart(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -218,11 +150,19 @@ func PodStart(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusNotModified, "")
return
}
- if _, err := pod.Start(r.Context()); err != nil {
+ responses, err := pod.Start(r.Context())
+ if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, err := range responses {
+ errs = append(errs, err)
+ }
+ report := entities.PodStartReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodDelete(w http.ResponseWriter, r *http.Request) {
@@ -251,10 +191,16 @@ func PodDelete(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusNoContent, "")
+ report := entities.PodRmReport{
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodRestart(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -262,12 +208,19 @@ func PodRestart(w http.ResponseWriter, r *http.Request) {
utils.PodNotFound(w, name, err)
return
}
- _, err = pod.Restart(r.Context())
+ responses, err := pod.Restart(r.Context())
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, err := range responses {
+ errs = append(errs, err)
+ }
+ report := entities.PodRestartReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodPrune(w http.ResponseWriter, r *http.Request) {
@@ -283,6 +236,9 @@ func PodPrune(w http.ResponseWriter, r *http.Request) {
}
func PodPause(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -290,15 +246,25 @@ func PodPause(w http.ResponseWriter, r *http.Request) {
utils.PodNotFound(w, name, err)
return
}
- _, err = pod.Pause()
+ responses, err := pod.Pause()
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusNoContent, "")
+ for _, v := range responses {
+ errs = append(errs, v)
+ }
+ report := entities.PodPauseReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodUnpause(w http.ResponseWriter, r *http.Request) {
+ var (
+ errs []error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
@@ -306,12 +272,19 @@ func PodUnpause(w http.ResponseWriter, r *http.Request) {
utils.PodNotFound(w, name, err)
return
}
- _, err = pod.Unpause()
+ responses, err := pod.Unpause()
if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ utils.Error(w, "failed to pause pod", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ for _, v := range responses {
+ errs = append(errs, v)
+ }
+ report := entities.PodUnpauseReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, &report)
}
func PodKill(w http.ResponseWriter, r *http.Request) {
@@ -319,6 +292,7 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
runtime = r.Context().Value("runtime").(*libpod.Runtime)
decoder = r.Context().Value("decoder").(*schema.Decoder)
signal = "SIGKILL"
+ errs []error
)
query := struct {
Signal string `schema:"signal"`
@@ -361,12 +335,23 @@ func PodKill(w http.ResponseWriter, r *http.Request) {
utils.Error(w, msg, http.StatusConflict, errors.Errorf("cannot kill a pod with no running containers: %s", pod.ID()))
return
}
- _, err = pod.Kill(uint(sig))
+
+ responses, err := pod.Kill(uint(sig))
if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
+ utils.Error(w, "failed to kill pod", http.StatusInternalServerError, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+
+ for _, v := range responses {
+ if v != nil {
+ errs = append(errs, v)
+ }
+ }
+ report := &entities.PodKillReport{
+ Errs: errs,
+ Id: pod.ID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, report)
}
func PodExists(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go
index aec30ef56..1fad2dd1a 100644
--- a/pkg/api/handlers/libpod/swagger.go
+++ b/pkg/api/handlers/libpod/swagger.go
@@ -1,8 +1,94 @@
package libpod
+import (
+ "net/http"
+ "os"
+
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+)
+
+// DefaultPodmanSwaggerSpec provides the default path to the podman swagger spec file
+const DefaultPodmanSwaggerSpec = "/usr/share/containers/podman/swagger.yaml"
+
// List Containers
// swagger:response ListContainers
type swagInspectPodResponse struct {
// in:body
Body []ListContainer
}
+
+// Inspect Manifest
+// swagger:response InspectManifest
+type swagInspectManifestResponse struct {
+ // in:body
+ Body manifest.List
+}
+
+// Kill Pod
+// swagger:response PodKillReport
+type swagKillPodResponse struct {
+ // in:body
+ Body entities.PodKillReport
+}
+
+// Pause pod
+// swagger:response PodPauseReport
+type swagPausePodResponse struct {
+ // in:body
+ Body entities.PodPauseReport
+}
+
+// Unpause pod
+// swagger:response PodUnpauseReport
+type swagUnpausePodResponse struct {
+ // in:body
+ Body entities.PodUnpauseReport
+}
+
+// Stop pod
+// swagger:response PodStopReport
+type swagStopPodResponse struct {
+ // in:body
+ Body entities.PodStopReport
+}
+
+// Restart pod
+// swagger:response PodRestartReport
+type swagRestartPodResponse struct {
+ // in:body
+ Body entities.PodRestartReport
+}
+
+// Start pod
+// swagger:response PodStartReport
+type swagStartPodResponse struct {
+ // in:body
+ Body entities.PodStartReport
+}
+
+// Rm pod
+// swagger:response PodRmReport
+type swagRmPodResponse struct {
+ // in:body
+ Body entities.PodRmReport
+}
+
+func ServeSwagger(w http.ResponseWriter, r *http.Request) {
+ path := DefaultPodmanSwaggerSpec
+ if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found {
+ path = p
+ }
+ if _, err := os.Stat(path); err != nil {
+ if os.IsNotExist(err) {
+ utils.InternalServerError(w, errors.Errorf("file %q does not exist", path))
+ return
+ }
+ utils.InternalServerError(w, err)
+ return
+ }
+ w.Header().Set("Content-Type", "text/yaml")
+ http.ServeFile(w, r, path)
+}
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index 9b10ee890..5a6fc021e 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -3,16 +3,15 @@ package libpod
import (
"encoding/json"
"net/http"
- "strings"
"github.com/containers/libpod/cmd/podman/shared"
"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"
+ "github.com/containers/libpod/pkg/domain/filters"
"github.com/gorilla/schema"
"github.com/pkg/errors"
- log "github.com/sirupsen/logrus"
)
func CreateVolume(w http.ResponseWriter, r *http.Request) {
@@ -25,7 +24,7 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
}{
// override any golang type defaults
}
- input := handlers.VolumeCreateConfig{}
+ input := entities.VolumeCreateOptions{}
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()))
@@ -46,8 +45,8 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
if len(input.Label) > 0 {
volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(input.Label))
}
- if len(input.Opts) > 0 {
- parsedOptions, err := shared.ParseVolumeOptions(input.Opts)
+ if len(input.Options) > 0 {
+ parsedOptions, err := shared.ParseVolumeOptions(input.Options)
if err != nil {
utils.InternalServerError(w, err)
return
@@ -64,7 +63,17 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(w, http.StatusOK, config)
+ volResponse := entities.VolumeConfigResponse{
+ Name: config.Name,
+ Driver: config.Driver,
+ Mountpoint: config.MountPoint,
+ CreatedAt: config.CreatedTime,
+ Labels: config.Labels,
+ Options: config.Options,
+ UID: config.UID,
+ GID: config.GID,
+ }
+ utils.WriteResponse(w, http.StatusOK, volResponse)
}
func InspectVolume(w http.ResponseWriter, r *http.Request) {
@@ -75,21 +84,27 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) {
vol, err := runtime.GetVolume(name)
if err != nil {
utils.VolumeNotFound(w, name, err)
+ return
}
- inspect, err := vol.Inspect()
- if err != nil {
- utils.InternalServerError(w, err)
- }
- utils.WriteResponse(w, http.StatusOK, inspect)
+ volResponse := entities.VolumeConfigResponse{
+ Name: vol.Name(),
+ Driver: vol.Driver(),
+ Mountpoint: vol.MountPoint(),
+ CreatedAt: vol.CreatedTime(),
+ Labels: vol.Labels(),
+ Scope: vol.Scope(),
+ Options: vol.Options(),
+ UID: vol.UID(),
+ GID: vol.GID(),
+ }
+ utils.WriteResponse(w, http.StatusOK, volResponse)
}
func ListVolumes(w http.ResponseWriter, r *http.Request) {
var (
decoder = r.Context().Value("decoder").(*schema.Decoder)
- err error
runtime = r.Context().Value("runtime").(*libpod.Runtime)
- volumeConfigs []*libpod.VolumeConfig
- volumeFilters []libpod.VolumeFilter
+ volumeConfigs []*entities.VolumeListReport
)
query := struct {
Filters map[string][]string `schema:"filters"`
@@ -103,25 +118,30 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
return
}
- if len(query.Filters) > 0 {
- volumeFilters, err = generateVolumeFilters(query.Filters)
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
+ volumeFilters, err := filters.GenerateVolumeFilters(query.Filters)
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
}
+
vols, err := runtime.Volumes(volumeFilters...)
if err != nil {
utils.InternalServerError(w, err)
return
}
for _, v := range vols {
- config, err := v.Config()
- if err != nil {
- utils.InternalServerError(w, err)
- return
+ config := entities.VolumeConfigResponse{
+ Name: v.Name(),
+ Driver: v.Driver(),
+ Mountpoint: v.MountPoint(),
+ CreatedAt: v.CreatedTime(),
+ Labels: v.Labels(),
+ Scope: v.Scope(),
+ Options: v.Options(),
+ UID: v.UID(),
+ GID: v.GID(),
}
- volumeConfigs = append(volumeConfigs, config)
+ volumeConfigs = append(volumeConfigs, &entities.VolumeListReport{VolumeConfigResponse: config})
}
utils.WriteResponse(w, http.StatusOK, volumeConfigs)
}
@@ -129,17 +149,20 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
func PruneVolumes(w http.ResponseWriter, r *http.Request) {
var (
runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ reports []*entities.VolumePruneReport
)
- pruned, errs := runtime.PruneVolumes(r.Context())
- if errs != nil {
- if len(errs) > 1 {
- for _, err := range errs {
- log.Infof("Request Failed(%s): %s", http.StatusText(http.StatusInternalServerError), err.Error())
- }
- }
- utils.InternalServerError(w, errs[len(errs)-1])
+ pruned, err := runtime.PruneVolumes(r.Context())
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ for k, v := range pruned {
+ reports = append(reports, &entities.VolumePruneReport{
+ Err: v,
+ Id: k,
+ })
}
- utils.WriteResponse(w, http.StatusOK, pruned)
+ utils.WriteResponse(w, http.StatusOK, reports)
}
func RemoveVolume(w http.ResponseWriter, r *http.Request) {
@@ -174,65 +197,3 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusNoContent, "")
}
-
-func generateVolumeFilters(filters map[string][]string) ([]libpod.VolumeFilter, error) {
- var vf []libpod.VolumeFilter
- for filter, v := range filters {
- for _, val := range v {
- switch filter {
- case "name":
- nameVal := val
- vf = append(vf, func(v *libpod.Volume) bool {
- return nameVal == v.Name()
- })
- case "driver":
- driverVal := val
- vf = append(vf, func(v *libpod.Volume) bool {
- return v.Driver() == driverVal
- })
- case "scope":
- scopeVal := val
- vf = append(vf, func(v *libpod.Volume) bool {
- return v.Scope() == scopeVal
- })
- case "label":
- filterArray := strings.SplitN(val, "=", 2)
- filterKey := filterArray[0]
- var filterVal string
- if len(filterArray) > 1 {
- filterVal = filterArray[1]
- } else {
- filterVal = ""
- }
- vf = append(vf, func(v *libpod.Volume) bool {
- for labelKey, labelValue := range v.Labels() {
- if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) {
- return true
- }
- }
- return false
- })
- case "opt":
- filterArray := strings.SplitN(val, "=", 2)
- filterKey := filterArray[0]
- var filterVal string
- if len(filterArray) > 1 {
- filterVal = filterArray[1]
- } else {
- filterVal = ""
- }
- vf = append(vf, func(v *libpod.Volume) bool {
- for labelKey, labelValue := range v.Options() {
- if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) {
- return true
- }
- }
- return false
- })
- default:
- return nil, errors.Errorf("%q is in an invalid volume filter", filter)
- }
- }
- }
- return vf, nil
-}
diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go
index 4ba123ba9..e6e937729 100644
--- a/pkg/api/handlers/swagger.go
+++ b/pkg/api/handlers/swagger.go
@@ -2,7 +2,9 @@ package handlers
import (
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/inspect"
"github.com/docker/docker/api/types"
)
@@ -108,7 +110,7 @@ type swagDockerTopResponse struct {
type swagLibpodInspectContainerResponse struct {
// in:body
Body struct {
- libpod.InspectContainerData
+ define.InspectContainerData
}
}
@@ -116,7 +118,7 @@ type swagLibpodInspectContainerResponse struct {
// swagger:response ListPodsResponse
type swagListPodsResponse struct {
// in:body
- Body []libpod.PodInspect
+ Body []entities.ListPodsReport
}
// Inspect pod
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 2e429dc58..1ca5db3f9 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
libpodImage "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
docker "github.com/docker/docker/api/types"
dockerContainer "github.com/docker/docker/api/types/container"
dockerEvents "github.com/docker/docker/api/types/events"
@@ -45,12 +46,6 @@ type LibpodImagesPullReport struct {
ID string `json:"id"`
}
-type ImageSummary struct {
- docker.ImageSummary
- CreatedTime time.Time `json:"CreatedTime,omitempty"`
- ReadOnly bool `json:"ReadOnly,omitempty"`
-}
-
type ContainersPruneReport struct {
docker.ContainersPruneReport
}
@@ -128,19 +123,9 @@ type CreateContainerConfig struct {
NetworkingConfig dockerNetwork.NetworkingConfig
}
-// swagger:model VolumeCreate
-type VolumeCreateConfig struct {
- // New volume's name. Can be left blank
- Name string `schema:"name"`
- // Volume driver to use
- Driver string `schema:"driver"`
- // User-defined key/value metadata.
- Label map[string]string `schema:"label"`
- // Mapping of driver options and values.
- Opts map[string]string `schema:"opts"`
-}
-
+// swagger:model IDResponse
type IDResponse struct {
+ // ID
ID string `json:"id"`
}
@@ -148,19 +133,6 @@ type ContainerTopOKBody struct {
dockerContainer.ContainerTopOKBody
}
-// swagger:model PodCreateConfig
-type PodCreateConfig struct {
- Name string `json:"name"`
- CGroupParent string `json:"cgroup-parent"`
- Hostname string `json:"hostname"`
- Infra bool `json:"infra"`
- InfraCommand string `json:"infra-command"`
- InfraImage string `json:"infra-image"`
- Labels []string `json:"labels"`
- Publish []string `json:"publish"`
- Share string `json:"share"`
-}
-
type ErrorModel struct {
Message string `json:"message"`
}
@@ -187,6 +159,14 @@ type ImageTreeResponse struct {
Layers []ImageLayer `json:"layers"`
}
+type ExecCreateConfig struct {
+ docker.ExecConfig
+}
+
+type ExecCreateResponse struct {
+ docker.IDResponse
+}
+
func EventToApiEvent(e *events.Event) *Event {
return &Event{dockerEvents.Message{
Type: e.Type.String(),
@@ -205,23 +185,13 @@ func EventToApiEvent(e *events.Event) *Event {
}}
}
-func ImageToImageSummary(l *libpodImage.Image) (*ImageSummary, error) {
+func ImageToImageSummary(l *libpodImage.Image) (*entities.ImageSummary, error) {
containers, err := l.Containers()
if err != nil {
return nil, errors.Wrapf(err, "Failed to obtain Containers for image %s", l.ID())
}
containerCount := len(containers)
- var digests []string
- for _, d := range l.Digests() {
- digests = append(digests, string(d))
- }
-
- tags, err := l.RepoTags()
- if err != nil {
- return nil, errors.Wrapf(err, "Failed to obtain RepoTags for image %s", l.ID())
- }
-
// FIXME: GetParent() panics
// parent, err := l.GetParent(context.TODO())
// if err != nil {
@@ -237,20 +207,43 @@ func ImageToImageSummary(l *libpodImage.Image) (*ImageSummary, error) {
if err != nil {
return nil, errors.Wrapf(err, "Failed to obtain Size for image %s", l.ID())
}
- dockerSummary := docker.ImageSummary{
- Containers: int64(containerCount),
- Created: l.Created().Unix(),
- ID: l.ID(),
- Labels: labels,
- ParentID: l.Parent,
- RepoDigests: digests,
- RepoTags: tags,
- SharedSize: 0,
- Size: int64(*size),
- VirtualSize: int64(*size),
- }
- is := ImageSummary{
- ImageSummary: dockerSummary,
+
+ repoTags, err := l.RepoTags()
+ if err != nil {
+ return nil, errors.Wrapf(err, "Failed to obtain RepoTags for image %s", l.ID())
+ }
+
+ history, err := l.History(context.TODO())
+ if err != nil {
+ return nil, errors.Wrapf(err, "Failed to obtain History for image %s", l.ID())
+ }
+ historyIds := make([]string, len(history))
+ for i, h := range history {
+ historyIds[i] = h.ID
+ }
+
+ digests := make([]string, len(l.Digests()))
+ for i, d := range l.Digests() {
+ digests[i] = string(d)
+ }
+
+ is := entities.ImageSummary{
+ ID: l.ID(),
+ ParentId: l.Parent,
+ RepoTags: repoTags,
+ Created: l.Created().Unix(),
+ Size: int64(*size),
+ SharedSize: 0,
+ VirtualSize: l.VirtualSize,
+ Labels: labels,
+ Containers: containerCount,
+ ReadOnly: l.IsReadOnly(),
+ Dangling: l.Dangling(),
+ Names: l.Names(),
+ Digest: string(l.Digest()),
+ Digests: digests,
+ ConfigDigest: string(l.ConfigDigest),
+ History: historyIds,
}
return &is, nil
}
diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go
index d5a79bdc8..bbe4cee3c 100644
--- a/pkg/api/handlers/utils/containers.go
+++ b/pkg/api/handlers/utils/containers.go
@@ -16,7 +16,7 @@ import (
// ContainerCreateResponse is the response struct for creating a container
type ContainerCreateResponse struct {
// ID of the container created
- ID string `json:"id"`
+ ID string `json:"Id"`
// Warnings during container creation
Warnings []string `json:"Warnings"`
}
diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go
new file mode 100644
index 000000000..79d1a5090
--- /dev/null
+++ b/pkg/api/handlers/utils/pods.go
@@ -0,0 +1,84 @@
+package utils
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/gorilla/schema"
+)
+
+func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport, error) {
+ var (
+ lps []*entities.ListPodsReport
+ pods []*libpod.Pod
+ podErr error
+ )
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+
+ query := struct {
+ All bool
+ Filters map[string][]string `schema:"filters"`
+ Digests bool
+ }{}
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ return nil, err
+ }
+ var filters = []string{}
+ if _, found := r.URL.Query()["digests"]; found && query.Digests {
+ UnSupportedParameter("digests")
+ }
+
+ if len(query.Filters) > 0 {
+ for k, v := range query.Filters {
+ for _, val := range v {
+ filters = append(filters, fmt.Sprintf("%s=%s", k, val))
+ }
+ }
+ filterFuncs, err := shared.GenerateFilterFunction(runtime, filters)
+ if err != nil {
+ return nil, err
+ }
+ pods, podErr = shared.FilterAllPodsWithFilterFunc(runtime, filterFuncs...)
+ } else {
+ pods, podErr = runtime.GetAllPods()
+ }
+ if podErr != nil {
+ return nil, podErr
+ }
+ for _, pod := range pods {
+ status, err := pod.GetPodStatus()
+ if err != nil {
+ return nil, err
+ }
+ ctrs, err := pod.AllContainers()
+ if err != nil {
+ return nil, err
+ }
+ lp := entities.ListPodsReport{
+ Cgroup: pod.CgroupParent(),
+ Created: pod.CreatedTime(),
+ Id: pod.ID(),
+ Name: pod.Name(),
+ Namespace: pod.Namespace(),
+ Status: status,
+ }
+ for _, ctr := range ctrs {
+ state, err := ctr.State()
+ if err != nil {
+ return nil, err
+ }
+ lp.Containers = append(lp.Containers, &entities.ListPodContainer{
+ Id: ctr.ID(),
+ Names: ctr.Name(),
+ Status: state.String(),
+ })
+ }
+ lps = append(lps, &lp)
+ }
+ return lps, nil
+}
diff --git a/pkg/api/server/register_events.go b/pkg/api/server/register_events.go
index b0f403709..e909303da 100644
--- a/pkg/api/server/register_events.go
+++ b/pkg/api/server/register_events.go
@@ -63,6 +63,6 @@ func (s *APIServer) registerEventsHandlers(r *mux.Router) error {
// description: returns a string of json data describing an event
// 500:
// "$ref": "#/responses/InternalError"
- r.Handle(VersionedPath("/events"), s.APIHandler(compat.GetEvents)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/libpod/events"), s.APIHandler(compat.GetEvents)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/api/server/register_exec.go b/pkg/api/server/register_exec.go
index d27d21a04..71fb50307 100644
--- a/pkg/api/server/register_exec.go
+++ b/pkg/api/server/register_exec.go
@@ -8,7 +8,7 @@ import (
)
func (s *APIServer) registerExecHandlers(r *mux.Router) error {
- // swagger:operation POST /containers/{name}/create compat createExec
+ // swagger:operation POST /containers/{name}/exec compat createExec
// ---
// tags:
// - exec (compat)
@@ -74,9 +74,9 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// description: container is paused
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/containers/{name}/create"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/containers/{name}/exec"), s.APIHandler(compat.ExecCreateHandler)).Methods(http.MethodPost)
// Added non version path to URI to support docker non versioned paths
- r.Handle("/containers/{name}/create", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle("/containers/{name}/exec", s.APIHandler(compat.ExecCreateHandler)).Methods(http.MethodPost)
// swagger:operation POST /exec/{id}/start compat startExec
// ---
// tags:
@@ -169,15 +169,15 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchExecInstance"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/exec/{id}/json"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/exec/{id}/json"), s.APIHandler(compat.ExecInspectHandler)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths
- r.Handle("/exec/{id}/json", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodGet)
+ r.Handle("/exec/{id}/json", s.APIHandler(compat.ExecInspectHandler)).Methods(http.MethodGet)
/*
libpod api follows
*/
- // swagger:operation POST /libpod/containers/{name}/create libpod libpodCreateExec
+ // swagger:operation POST /libpod/containers/{name}/exec libpod libpodCreateExec
// ---
// tags:
// - exec
@@ -243,7 +243,7 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// description: container is paused
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/containers/{name}/create"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/libpod/containers/{name}/exec"), s.APIHandler(compat.ExecCreateHandler)).Methods(http.MethodPost)
// swagger:operation POST /libpod/exec/{id}/start libpod libpodStartExec
// ---
// tags:
@@ -332,6 +332,6 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error {
// $ref: "#/responses/NoSuchExecInstance"
// 500:
// $ref: "#/responses/InternalError"
- r.Handle(VersionedPath("/libpod/exec/{id}/json"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodGet)
+ r.Handle(VersionedPath("/libpod/exec/{id}/json"), s.APIHandler(compat.ExecInspectHandler)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index e6ad045a2..e8dfe2fa8 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -967,7 +967,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/{name:.*}/tag"), s.APIHandler(compat.TagImage)).Methods(http.MethodPost)
- // swagger:operation POST /commit libpod libpodCommitContainer
+ // swagger:operation POST /libpod/commit libpod libpodCommitContainer
// ---
// tags:
// - containers
@@ -978,6 +978,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// name: container
// type: string
// description: the name or ID of a container
+ // required: true
// - in: query
// name: repo
// type: string
@@ -1000,8 +1001,14 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: pause the container before committing it
// - in: query
// name: changes
+ // description: instructions to apply while committing in Dockerfile format (i.e. "CMD=/bin/foo")
+ // type: array
+ // items:
+ // type: string
+ // - in: query
+ // name: format
// type: string
- // description: instructions to apply while committing in Dockerfile format
+ // description: format of the image manifest and metadata (default "oci")
// produces:
// - application/json
// responses:
@@ -1011,6 +1018,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: '#/responses/NoSuchImage'
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/commit"), s.APIHandler(compat.CommitContainer)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/libpod/commit"), s.APIHandler(libpod.CommitContainer)).Methods(http.MethodPost)
return nil
}
diff --git a/pkg/api/server/register_manifest.go b/pkg/api/server/register_manifest.go
new file mode 100644
index 000000000..8fd84f205
--- /dev/null
+++ b/pkg/api/server/register_manifest.go
@@ -0,0 +1,145 @@
+package server
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
+ "github.com/gorilla/mux"
+)
+
+func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
+ // swagger:operation POST /libpod/manifests/create manifests Create
+ // ---
+ // summary: Create
+ // description: Create a manifest list
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: query
+ // name: name
+ // type: string
+ // description: manifest list name
+ // required: true
+ // - in: query
+ // name: image
+ // type: string
+ // description: name of the image
+ // - in: query
+ // name: all
+ // type: boolean
+ // description: add all contents if given list
+ // responses:
+ // 200:
+ // $ref: "#/definitions/IDResponse"
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 404:
+ // $ref: "#/responses/NoSuchImage"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/manifests/create"), s.APIHandler(libpod.ManifestCreate)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/manifests/{name:.*}/json manifests Inspect
+ // ---
+ // summary: Inspect
+ // description: Display a manifest list
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: the name or ID of the manifest
+ // responses:
+ // 200:
+ // $ref: "#/responses/InspectManifest"
+ // 404:
+ // $ref: "#/responses/NoSuchManifest"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/manifests/{name:.*}/json"), s.APIHandler(libpod.ManifestInspect)).Methods(http.MethodGet)
+ // swagger:operation POST /libpod/manifests/{name:.*}/add manifests AddManifest
+ // ---
+ // description: Add an image to a manifest list
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: the name or ID of the manifest
+ // - in: body
+ // name: options
+ // description: options for creating a manifest
+ // schema:
+ // $ref: "#/definitions/ManifestAddOpts"
+ // responses:
+ // 200:
+ // $ref: "#/definitions/IDResponse"
+ // 404:
+ // $ref: "#/responses/NoSuchManifest"
+ // 409:
+ // $ref: "#/responses/BadParamError"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/manifests/{name:.*}/add"), s.APIHandler(libpod.ManifestAdd)).Methods(http.MethodPost)
+ // swagger:operation DELETE /libpod/manifests/{name:.*} manifests RemoveManifest
+ // ---
+ // summary: Remove
+ // description: Remove an image from a manifest list
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: the image associated with the manifest
+ // - in: query
+ // name: digest
+ // type: string
+ // description: image digest to be removed
+ // responses:
+ // 200:
+ // $ref: "#/definitions/IDResponse"
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 404:
+ // $ref: "#/responses/NoSuchManifest"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/manifests/{name:.*}"), s.APIHandler(libpod.ManifestRemove)).Methods(http.MethodDelete)
+ // swagger:operation POST /libpod/manifests/{name}/push manifests PushManifest
+ // ---
+ // summary: Push
+ // description: Push a manifest list or image index to a registry
+ // produces:
+ // - application/json
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the manifest
+ // - in: query
+ // name: destination
+ // type: string
+ // required: true
+ // description: the destination for the manifest
+ // - in: query
+ // name: all
+ // description: push all images
+ // type: boolean
+ // responses:
+ // 200:
+ // $ref: "#/definitions/IDResponse"
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 404:
+ // $ref: "#/responses/NoSuchManifest"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/manifests/{name}/push"), s.APIHandler(libpod.ManifestPush)).Methods(http.MethodPost)
+ return nil
+}
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index af2330665..5ba2263e8 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -37,7 +37,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// description: attributes for creating a pod
// schema:
// type: object
- // $ref: "#/definitions/PodCreateConfig"
+ // $ref: "#/definitions/PodSpecGenerator"
// responses:
// 200:
// $ref: "#/definitions/IdResponse"
@@ -81,8 +81,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// type: boolean
// description : force removal of a running pod by first stopping all containers, then removing all containers in the pod
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodRmReport'
// 400:
// $ref: "#/responses/BadParamError"
// 404:
@@ -146,8 +146,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// description: signal to be sent to pod
// default: SIGKILL
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: "#/responses/PodKillReport"
// 400:
// $ref: "#/responses/BadParamError"
// 404:
@@ -170,8 +170,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// required: true
// description: the name or ID of the pod
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodPauseReport'
// 404:
// $ref: "#/responses/NoSuchPod"
// 500:
@@ -189,8 +189,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// required: true
// description: the name or ID of the pod
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodRestartReport'
// 404:
// $ref: "#/responses/NoSuchPod"
// 500:
@@ -208,8 +208,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// required: true
// description: the name or ID of the pod
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodStartReport'
// 304:
// $ref: "#/responses/PodAlreadyStartedError"
// 404:
@@ -233,8 +233,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// type: integer
// description: timeout
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodStopReport'
// 304:
// $ref: "#/responses/PodAlreadyStoppedError"
// 400:
@@ -256,8 +256,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// required: true
// description: the name or ID of the pod
// responses:
- // 204:
- // description: no error
+ // 200:
+ // $ref: '#/responses/PodUnpauseReport'
// 404:
// $ref: "#/responses/NoSuchPod"
// 500:
diff --git a/pkg/api/server/register_swagger.go b/pkg/api/server/register_swagger.go
index 5564ec096..9048c1951 100644
--- a/pkg/api/server/register_swagger.go
+++ b/pkg/api/server/register_swagger.go
@@ -2,25 +2,14 @@ package server
import (
"net/http"
- "os"
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/gorilla/mux"
)
-// DefaultPodmanSwaggerSpec provides the default path to the podman swagger spec file
-const DefaultPodmanSwaggerSpec = "/usr/share/containers/podman/swagger.yaml"
-
// RegisterSwaggerHandlers maps the swagger endpoint for the server
func (s *APIServer) RegisterSwaggerHandlers(r *mux.Router) error {
// This handler does _*NOT*_ provide an UI rather just a swagger spec that an UI could render
- r.PathPrefix("/swagger/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- path := DefaultPodmanSwaggerSpec
- if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found {
- path = p
- }
- w.Header().Set("Content-Type", "text/yaml")
-
- http.ServeFile(w, r, path)
- })
+ r.HandleFunc(VersionedPath("/libpod/swagger"), s.APIHandler(libpod.ServeSwagger)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/api/server/register_volumes.go b/pkg/api/server/register_volumes.go
index 2cf249cc3..93b972b6b 100644
--- a/pkg/api/server/register_volumes.go
+++ b/pkg/api/server/register_volumes.go
@@ -53,8 +53,8 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
// produces:
// - application/json
// responses:
- // '204':
- // description: no error
+ // '200':
+ // "$ref": "#/responses/VolumePruneResponse"
// '500':
// "$ref": "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/volumes/prune"), s.APIHandler(libpod.PruneVolumes)).Methods(http.MethodPost)
@@ -71,11 +71,11 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error {
// - application/json
// responses:
// '200':
- // "$ref": "#/responses/InspectVolumeResponse"
+ // "$ref": "#/responses/VolumeCreateResponse"
// '404':
- // "$ref": "#/responses/NoSuchVolume"
+ // "$ref": "#/responses/NoSuchVolume"
// '500':
- // "$ref": "#/responses/InternalError"
+ // "$ref": "#/responses/InternalError"
r.Handle(VersionedPath("/libpod/volumes/{name}/json"), s.APIHandler(libpod.InspectVolume)).Methods(http.MethodGet)
// swagger:operation DELETE /libpod/volumes/{name} volumes removeVolume
// ---
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index a0addb303..59f1f95cb 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -83,10 +83,6 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
ConnectionCh: make(chan int),
}
- server.Timer = time.AfterFunc(server.Duration, func() {
- server.ConnectionCh <- NOOPHandler
- })
-
router.NotFoundHandler = http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
// We can track user errors...
@@ -99,15 +95,17 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
server.registerAuthHandlers,
server.registerContainersHandlers,
server.registerDistributionHandlers,
- server.registerExecHandlers,
server.registerEventsHandlers,
+ server.registerExecHandlers,
server.registerHealthCheckHandlers,
server.registerImagesHandlers,
server.registerInfoHandlers,
+ server.registerManifestHandlers,
server.registerMonitorHandlers,
server.registerPingHandlers,
server.registerPluginsHandlers,
server.registerPodsHandlers,
+ server.RegisterSwaggerHandlers,
server.registerSwarmHandlers,
server.registerSystemHandlers,
server.registerVersionHandlers,
@@ -138,36 +136,15 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
// Serve starts responding to HTTP requests
func (s *APIServer) Serve() error {
- // stalker to count the connections. Should the timer expire it will shutdown the service.
- go func() {
- for delta := range s.ConnectionCh {
- switch delta {
- case EnterHandler:
- s.Timer.Stop()
- s.ActiveConnections += 1
- s.TotalConnections += 1
- case ExitHandler:
- s.Timer.Stop()
- s.ActiveConnections -= 1
- if s.ActiveConnections == 0 {
- // Server will be shutdown iff the timer expires before being reset or stopped
- s.Timer = time.AfterFunc(s.Duration, func() {
- if err := s.Shutdown(); err != nil {
- logrus.Errorf("Failed to shutdown APIServer: %v", err)
- os.Exit(1)
- }
- })
- } else {
- s.Timer.Reset(s.Duration)
- }
- case NOOPHandler:
- // push the check out another duration...
- s.Timer.Reset(s.Duration)
- default:
- logrus.Errorf("ConnectionCh received unsupported input %d", delta)
- }
- }
- }()
+ // This is initialized here as Timer is not needed until Serve'ing
+ if s.Duration > 0 {
+ s.Timer = time.AfterFunc(s.Duration, func() {
+ s.ConnectionCh <- NOOPHandler
+ })
+ go s.ReadChannelWithTimeout()
+ } else {
+ go s.ReadChannelNoTimeout()
+ }
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
@@ -192,6 +169,53 @@ func (s *APIServer) Serve() error {
return nil
}
+func (s *APIServer) ReadChannelWithTimeout() {
+ // stalker to count the connections. Should the timer expire it will shutdown the service.
+ for delta := range s.ConnectionCh {
+ switch delta {
+ case EnterHandler:
+ s.Timer.Stop()
+ s.ActiveConnections += 1
+ s.TotalConnections += 1
+ case ExitHandler:
+ s.Timer.Stop()
+ s.ActiveConnections -= 1
+ if s.ActiveConnections == 0 {
+ // Server will be shutdown iff the timer expires before being reset or stopped
+ s.Timer = time.AfterFunc(s.Duration, func() {
+ if err := s.Shutdown(); err != nil {
+ logrus.Errorf("Failed to shutdown APIServer: %v", err)
+ os.Exit(1)
+ }
+ })
+ } else {
+ s.Timer.Reset(s.Duration)
+ }
+ case NOOPHandler:
+ // push the check out another duration...
+ s.Timer.Reset(s.Duration)
+ default:
+ logrus.Warnf("ConnectionCh received unsupported input %d", delta)
+ }
+ }
+}
+
+func (s *APIServer) ReadChannelNoTimeout() {
+ // stalker to count the connections.
+ for delta := range s.ConnectionCh {
+ switch delta {
+ case EnterHandler:
+ s.ActiveConnections += 1
+ s.TotalConnections += 1
+ case ExitHandler:
+ s.ActiveConnections -= 1
+ case NOOPHandler:
+ default:
+ logrus.Warnf("ConnectionCh received unsupported input %d", delta)
+ }
+ }
+}
+
// Shutdown is a clean shutdown waiting on existing clients
func (s *APIServer) Shutdown() error {
// Duration == 0 flags no auto-shutdown of the server
diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go
index e3c991d6d..2433a6a05 100644
--- a/pkg/api/server/swagger.go
+++ b/pkg/api/server/swagger.go
@@ -2,8 +2,9 @@ package server
import (
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
)
// No such image
@@ -51,6 +52,15 @@ type swagErrNoSuchPod struct {
}
}
+// No such manifest
+// swagger:response NoSuchManifest
+type swagErrNoSuchManifest struct {
+ // in:body
+ Body struct {
+ utils.ErrorModel
+ }
+}
+
// Internal server error
// swagger:response InternalError
type swagInternalError struct {
@@ -118,7 +128,7 @@ type swagPodAlreadyStopped struct {
// swagger:response DockerImageSummary
type swagImageSummary struct {
// in:body
- Body []handlers.ImageSummary
+ Body []entities.ImageSummary
}
// List Containers
@@ -141,12 +151,19 @@ type ok struct {
}
}
+// Volume prune response
+// swagger:response VolumePruneResponse
+type swagVolumePruneResponse struct {
+ // in:body
+ Body []entities.VolumePruneReport
+}
+
// Volume create response
// swagger:response VolumeCreateResponse
type swagVolumeCreateResponse struct {
// in:body
Body struct {
- libpod.VolumeConfig
+ entities.VolumeConfigResponse
}
}
@@ -162,6 +179,6 @@ type swagVolumeListResponse struct {
type swagHealthCheckRunResponse struct {
// in:body
Body struct {
- libpod.HealthCheckResults
+ define.HealthCheckResults
}
}
diff --git a/pkg/api/tags.yaml b/pkg/api/tags.yaml
index 571f49e44..5b5d9f5bb 100644
--- a/pkg/api/tags.yaml
+++ b/pkg/api/tags.yaml
@@ -6,6 +6,8 @@ tags:
- name: images
description: Actions related to images
- name: pods
+ description: Actions related to manifests
+ - name: manifests
description: Actions related to pods
- name: volumes
description: Actions related to volumes