summaryrefslogtreecommitdiff
path: root/pkg/api
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api')
-rw-r--r--pkg/api/handlers/compat/images.go49
-rw-r--r--pkg/api/handlers/compat/images_build.go31
-rw-r--r--pkg/api/handlers/compat/networks.go33
-rw-r--r--pkg/api/handlers/compat/resize.go15
-rw-r--r--pkg/api/handlers/compat/swagger.go7
-rw-r--r--pkg/api/handlers/libpod/images_pull.go2
-rw-r--r--pkg/api/handlers/libpod/networks.go3
-rw-r--r--pkg/api/handlers/libpod/swagger.go12
-rw-r--r--pkg/api/server/handler_api.go6
-rw-r--r--pkg/api/server/register_containers.go2
-rw-r--r--pkg/api/server/register_images.go20
-rw-r--r--pkg/api/server/register_networks.go27
-rw-r--r--pkg/api/server/server.go21
13 files changed, 173 insertions, 55 deletions
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index 7b336c470..7baa1145a 100644
--- a/pkg/api/handlers/compat/images.go
+++ b/pkg/api/handlers/compat/images.go
@@ -166,8 +166,11 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
query := struct {
- FromSrc string `schema:"fromSrc"`
- Changes []string `schema:"changes"`
+ Changes []string `schema:"changes"`
+ FromSrc string `schema:"fromSrc"`
+ Message string `schema:"message"`
+ Platform string `schema:"platform"`
+ Repo string `shchema:"repo"`
}{
// This is where you can override the golang default value for one of fields
}
@@ -184,14 +187,27 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to create tempfile"))
return
}
+
source = f.Name()
if err := SaveFromBody(f, r); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to write temporary file"))
}
}
+ platformSpecs := strings.Split(query.Platform, "/")
+ opts := entities.ImageImportOptions{
+ Source: source,
+ Changes: query.Changes,
+ Message: query.Message,
+ Reference: query.Repo,
+ OS: platformSpecs[0],
+ }
+ if len(platformSpecs) > 1 {
+ opts.Architecture = platformSpecs[1]
+ }
+
imageEngine := abi.ImageEngine{Libpod: runtime}
- report, err := imageEngine.Import(r.Context(), entities.ImageImportOptions{Source: source, Changes: query.Changes})
+ report, err := imageEngine.Import(r.Context(), opts)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to import tarball"))
return
@@ -224,10 +240,10 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) {
query := struct {
FromImage string `schema:"fromImage"`
Tag string `schema:"tag"`
+ Platform string `schema:"platform"`
}{
// This is where you can override the golang default value for one of fields
}
-
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
return
@@ -250,12 +266,36 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) {
}
defer auth.RemoveAuthfile(authfile)
+ platformSpecs := strings.Split(query.Platform, "/") // split query into its parts
+
+ addOS := true // default assume true due to structure of if/else below
+ addArch := false
+ addVariant := false
+
+ if len(platformSpecs) > 1 { // if we have two arguments then we have os and arch
+ addArch = true
+ if len(platformSpecs) > 2 { // if we have 3 arguments then we have os arch and variant
+ addVariant = true
+ }
+ } else if len(platformSpecs) == 0 {
+ addOS = false
+ }
+
pullOptions := &libimage.PullOptions{}
pullOptions.AuthFilePath = authfile
if authConf != nil {
pullOptions.Username = authConf.Username
pullOptions.Password = authConf.Password
pullOptions.IdentityToken = authConf.IdentityToken
+ if addOS { // if the len is not 0
+ pullOptions.OS = platformSpecs[0]
+ if addArch {
+ pullOptions.Architecture = platformSpecs[1]
+ }
+ if addVariant {
+ pullOptions.Variant = platformSpecs[2]
+ }
+ }
}
pullOptions.Writer = os.Stderr // allows for debugging on the server
@@ -294,7 +334,6 @@ loop: // break out of for/select infinite loop
Error string `json:"error,omitempty"`
Id string `json:"id,omitempty"` // nolint
}
-
select {
case e := <-progress:
switch e.Event {
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index 6ff557291..e933b9811 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -139,6 +139,31 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
addCaps = m
}
+ // convert addcaps formats
+ containerFiles := []string{}
+ if _, found := r.URL.Query()["dockerfile"]; found {
+ var m = []string{}
+ if err := json.Unmarshal([]byte(query.Dockerfile), &m); err != nil {
+ // it's not json, assume just a string
+ m = append(m, query.Dockerfile)
+ }
+ containerFiles = m
+ } else {
+ containerFiles = []string{"Dockerfile"}
+ if utils.IsLibpodRequest(r) {
+ containerFiles = []string{"Containerfile"}
+ if _, err = os.Stat(filepath.Join(contextDirectory, "Containerfile")); err != nil {
+ if _, err1 := os.Stat(filepath.Join(contextDirectory, "Dockerfile")); err1 == nil {
+ containerFiles = []string{"Dockerfile"}
+ } else {
+ utils.BadRequest(w, "dockerfile", query.Dockerfile, err)
+ }
+ }
+ } else {
+ containerFiles = []string{"Dockerfile"}
+ }
+ }
+
addhosts := []string{}
if _, found := r.URL.Query()["extrahosts"]; found {
if err := json.Unmarshal([]byte(query.AddHosts), &addhosts); err != nil {
@@ -164,8 +189,8 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
var devices = []string{}
if _, found := r.URL.Query()["devices"]; found {
var m = []string{}
- if err := json.Unmarshal([]byte(query.DropCapabilities), &m); err != nil {
- utils.BadRequest(w, "devices", query.DropCapabilities, err)
+ if err := json.Unmarshal([]byte(query.Devices), &m); err != nil {
+ utils.BadRequest(w, "devices", query.Devices, err)
return
}
devices = m
@@ -470,7 +495,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
runCtx, cancel := context.WithCancel(context.Background())
go func() {
defer cancel()
- imageID, _, err = runtime.Build(r.Context(), buildOptions, query.Dockerfile)
+ imageID, _, err = runtime.Build(r.Context(), buildOptions, containerFiles...)
if err == nil {
success = true
} else {
diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go
index 77ed548d8..4e1f31404 100644
--- a/pkg/api/handlers/compat/networks.go
+++ b/pkg/api/handlers/compat/networks.go
@@ -28,19 +28,24 @@ import (
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
- // }
+ // scope is only used to see if the user passes any illegal value, verbose is not used but implemented
+ // for compatibility purposes only.
+ query := struct {
+ scope string `schema:"scope"`
+ verbose bool `schema:"verbose"`
+ }{
+ scope: "local",
+ }
+ 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
+ }
+
+ if query.scope != "local" {
+ utils.Error(w, "Invalid scope value. Can only be local.", http.StatusBadRequest, define.ErrInvalidArg)
+ return
+ }
config, err := runtime.GetConfig()
if err != nil {
utils.InternalServerError(w, err)
@@ -414,7 +419,7 @@ func Prune(w http.ResponseWriter, r *http.Request) {
type response struct {
NetworksDeleted []string
}
- var prunedNetworks []string //nolint
+ prunedNetworks := []string{}
for _, pr := range pruneReports {
if pr.Error != nil {
logrus.Error(pr.Error)
diff --git a/pkg/api/handlers/compat/resize.go b/pkg/api/handlers/compat/resize.go
index 23ed33a22..f65e313fc 100644
--- a/pkg/api/handlers/compat/resize.go
+++ b/pkg/api/handlers/compat/resize.go
@@ -46,20 +46,13 @@ func ResizeTTY(w http.ResponseWriter, r *http.Request) {
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 && !query.IgnoreNotRunning {
- utils.Error(w, "Container not running", http.StatusConflict,
- fmt.Errorf("container %q in wrong state %q", name, state.String()))
- return
- }
- // If container is not running, ignore since this can be a race condition, and is expected
if err := ctnr.AttachResize(sz); err != nil {
- if errors.Cause(err) != define.ErrCtrStateInvalid || !query.IgnoreNotRunning {
+ if errors.Cause(err) != define.ErrCtrStateInvalid {
utils.InternalServerError(w, errors.Wrapf(err, "cannot resize container"))
- return
+ } else {
+ utils.Error(w, "Container not running", http.StatusConflict, err)
}
+ return
}
// This is not a 204, even though we write nothing, for compatibility
// reasons.
diff --git a/pkg/api/handlers/compat/swagger.go b/pkg/api/handlers/compat/swagger.go
index a0783e723..b773799ef 100644
--- a/pkg/api/handlers/compat/swagger.go
+++ b/pkg/api/handlers/compat/swagger.go
@@ -77,10 +77,3 @@ type swagCompatNetworkDisconnectRequest struct {
// in:body
Body struct{ types.NetworkDisconnect }
}
-
-// Network prune
-// swagger:response NetworkPruneResponse
-type swagCompatNetworkPruneResponse struct {
- // in:body
- Body []string
-}
diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go
index fe56aa31d..e88b53a4b 100644
--- a/pkg/api/handlers/libpod/images_pull.go
+++ b/pkg/api/handlers/libpod/images_pull.go
@@ -85,7 +85,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
var pulledImages []*libimage.Image
var pullError error
- runCtx, cancel := context.WithCancel(context.Background())
+ runCtx, cancel := context.WithCancel(r.Context())
go func() {
defer cancel()
pulledImages, pullError = runtime.LibimageRuntime().Pull(runCtx, query.Reference, config.PullPolicyAlways, pullOptions)
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index 5417f778e..e4f450e12 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -190,5 +190,8 @@ func Prune(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
}
+ if pruneReports == nil {
+ pruneReports = []*entities.NetworkPruneReport{}
+ }
utils.WriteResponse(w, http.StatusOK, pruneReports)
}
diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go
index 9450a70d9..6116a7274 100644
--- a/pkg/api/handlers/libpod/swagger.go
+++ b/pkg/api/handlers/libpod/swagger.go
@@ -4,6 +4,7 @@ import (
"net/http"
"os"
+ "github.com/containernetworking/cni/libcni"
"github.com/containers/image/v5/manifest"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/api/handlers/utils"
@@ -95,14 +96,14 @@ type swagInfoResponse struct {
// swagger:response NetworkRmReport
type swagNetworkRmReport struct {
// in:body
- Body entities.NetworkRmReport
+ Body []entities.NetworkRmReport
}
// Network inspect
// swagger:response NetworkInspectReport
type swagNetworkInspectReport struct {
// in:body
- Body entities.NetworkInspectReport
+ Body libcni.NetworkConfigList
}
// Network list
@@ -119,6 +120,13 @@ type swagNetworkCreateReport struct {
Body entities.NetworkCreateReport
}
+// Network prune
+// swagger:response NetworkPruneResponse
+type swagNetworkPruneResponse struct {
+ // in:body
+ Body []entities.NetworkPruneReport
+}
+
func ServeSwagger(w http.ResponseWriter, r *http.Request) {
path := DefaultPodmanSwaggerSpec
if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found {
diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go
index 28b8706a8..becc674c0 100644
--- a/pkg/api/server/handler_api.go
+++ b/pkg/api/server/handler_api.go
@@ -63,6 +63,12 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc {
w.Header().Set("Libpod-API-Version", lv)
w.Header().Set("Server", "Libpod/"+lv+" ("+runtime.GOOS+")")
+ if s.CorsHeaders != "" {
+ w.Header().Set("Access-Control-Allow-Origin", s.CorsHeaders)
+ w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth, Connection, Upgrade, X-Registry-Config")
+ w.Header().Set("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS")
+ }
+
h(w, r)
logrus.Debugf("APIHandler(%s) -- %s %s END", rid, r.Method, r.URL.String())
}
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index aa999905e..88ebb4df5 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -1364,6 +1364,8 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// $ref: "#/responses/ok"
// 404:
// $ref: "#/responses/NoSuchContainer"
+ // 409:
+ // $ref: "#/responses/ConflictError"
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name}/resize"), s.APIHandler(compat.ResizeTTY)).Methods(http.MethodPost)
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index cbe75ded1..d075cd098 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -28,15 +28,28 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - in: query
// name: fromImage
// type: string
- // description: needs description
+ // description: 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.
// - in: query
// name: fromSrc
// type: string
- // description: needs description
+ // description: Source to import. The value may be a URL from which the image can be retrieved or - to read the image from the request body. This parameter may only be used when importing an image
+ // - in: query
+ // name: repo
+ // type: string
+ // description: 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.
// - in: query
// name: tag
// type: string
- // description: needs description
+ // description: Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled.
+ // - in: query
+ // name: message
+ // type: string
+ // description: Set commit message for imported image.
+ // - in: query
+ // name: platform
+ // type: string
+ // description: Platform in the format os[/arch[/variant]]
+ // default: ""
// - in: header
// name: X-Registry-Auth
// type: string
@@ -45,6 +58,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// name: request
// schema:
// type: string
+ // format: binary
// description: Image content if fromSrc parameter was used
// responses:
// 200:
diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go
index 9a5ccb789..cacf83a7f 100644
--- a/pkg/api/server/register_networks.go
+++ b/pkg/api/server/register_networks.go
@@ -44,6 +44,16 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// type: string
// required: true
// description: the name of the network
+ // - in: query
+ // name: verbose
+ // type: boolean
+ // required: false
+ // description: Detailed inspect output for troubleshooting
+ // - in: query
+ // name: scope
+ // type: string
+ // required: false
+ // description: Filter the network by scope (swarm, global, or local)
// produces:
// - application/json
// responses:
@@ -180,9 +190,12 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// 200:
// description: OK
// schema:
- // type: array
- // items:
- // type: string
+ // type: object
+ // properties:
+ // NetworksDeleted:
+ // type: array
+ // items:
+ // type: string
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/networks/prune"), s.APIHandler(compat.Prune)).Methods(http.MethodPost)
@@ -241,7 +254,9 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// tags:
// - networks
// summary: List networks
- // description: Display summary of network configurations
+ // description: |
+ // Display summary of network configurations.
+ // - In a 200 response, all of the fields named Bytes are returned as a Base64 encoded string.
// parameters:
// - in: query
// name: filters
@@ -266,7 +281,9 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// tags:
// - networks
// summary: Inspect a network
- // description: Display low level configuration for a CNI network
+ // description: |
+ // Display low level configuration for a CNI network.
+ // - In a 200 response, all of the fields named Bytes are returned as a Base64 encoded string.
// parameters:
// - in: path
// name: name
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index 972541bc6..1e8faf8f5 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -34,10 +34,12 @@ type APIServer struct {
context.CancelFunc // Stop APIServer
idleTracker *idle.Tracker // Track connections to support idle shutdown
pprof *http.Server // Sidecar http server for providing performance data
+ CorsHeaders string // Inject CORS headers to each request
}
// Number of seconds to wait for next request, if exceeded shutdown server
const (
+ DefaultCorsHeaders = ""
DefaultServiceDuration = 300 * time.Second
UnlimitedServiceDuration = 0 * time.Second
)
@@ -45,17 +47,22 @@ const (
// shutdownOnce ensures Shutdown() may safely be called from several go routines
var shutdownOnce sync.Once
+type Options struct {
+ Timeout time.Duration
+ CorsHeaders string
+}
+
// NewServer will create and configure a new API server with all defaults
func NewServer(runtime *libpod.Runtime) (*APIServer, error) {
- return newServer(runtime, DefaultServiceDuration, nil)
+ return newServer(runtime, DefaultServiceDuration, nil, DefaultCorsHeaders)
}
// NewServerWithSettings will create and configure a new API server using provided settings
-func NewServerWithSettings(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) {
- return newServer(runtime, duration, listener)
+func NewServerWithSettings(runtime *libpod.Runtime, listener *net.Listener, opts Options) (*APIServer, error) {
+ return newServer(runtime, opts.Timeout, listener, opts.CorsHeaders)
}
-func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) {
+func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener, corsHeaders string) (*APIServer, error) {
// If listener not provided try socket activation protocol
if listener == nil {
if _, found := os.LookupEnv("LISTEN_PID"); !found {
@@ -71,6 +78,11 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
}
listener = &listeners[0]
}
+ if corsHeaders == "" {
+ logrus.Debug("CORS Headers were not set")
+ } else {
+ logrus.Debugf("CORS Headers were set to %s", corsHeaders)
+ }
logrus.Infof("API server listening on %q", (*listener).Addr())
router := mux.NewRouter().UseEncodedPath()
@@ -88,6 +100,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
idleTracker: idle,
Listener: *listener,
Runtime: runtime,
+ CorsHeaders: corsHeaders,
}
router.NotFoundHandler = http.HandlerFunc(