2014-03-28 22:59:29 +00:00
package server
2013-04-10 19:48:21 -07:00
import (
2013-11-20 07:37:03 +00:00
"bufio"
"bytes"
2015-04-03 15:17:49 -07:00
"time"
2014-11-07 15:21:19 -05:00
2013-08-30 22:49:37 +02:00
"encoding/base64"
2013-04-18 03:13:43 +02:00
"encoding/json"
2013-04-19 15:24:37 +02:00
"fmt"
2013-05-07 18:06:49 -07:00
"io"
2013-06-18 18:59:56 +00:00
"net"
2013-04-10 19:48:21 -07:00
"net/http"
2013-06-18 18:59:56 +00:00
"os"
2013-04-22 18:17:47 +02:00
"strconv"
2013-04-19 15:24:37 +02:00
"strings"
2014-03-28 22:59:29 +00:00
2014-05-02 21:43:51 +00:00
"code.google.com/p/go.net/websocket"
2014-07-24 13:37:44 -07:00
"github.com/gorilla/mux"
2014-05-02 21:43:51 +00:00
2015-03-26 23:22:04 +01:00
"github.com/Sirupsen/logrus"
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/api"
2015-02-24 11:12:47 -08:00
"github.com/docker/docker/api/types"
2015-03-31 16:21:37 -07:00
"github.com/docker/docker/daemon"
2015-03-30 18:06:16 -07:00
"github.com/docker/docker/daemon/networkdriver/bridge"
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/engine"
2015-04-07 18:57:54 -07:00
"github.com/docker/docker/graph"
2015-04-03 15:17:49 -07:00
"github.com/docker/docker/pkg/jsonmessage"
2014-07-28 17:23:38 -07:00
"github.com/docker/docker/pkg/parsers"
2015-04-03 15:17:49 -07:00
"github.com/docker/docker/pkg/parsers/filters"
2014-09-17 18:04:56 +03:00
"github.com/docker/docker/pkg/stdcopy"
2015-03-17 19:18:41 -07:00
"github.com/docker/docker/pkg/streamformatter"
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/pkg/version"
"github.com/docker/docker/registry"
"github.com/docker/docker/utils"
2013-04-10 19:48:21 -07:00
)
2014-02-15 20:24:55 -08:00
var (
2015-04-01 17:25:56 -07:00
activationLock = make ( chan struct { } )
2014-02-15 20:24:55 -08:00
)
2014-11-07 15:21:19 -05:00
type HttpServer struct {
srv * http . Server
l net . Listener
}
func ( s * HttpServer ) Serve ( ) error {
return s . srv . Serve ( s . l )
}
func ( s * HttpServer ) Close ( ) error {
return s . l . Close ( )
}
2014-02-21 23:15:28 +00:00
type HttpApiFunc func ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error
2013-07-13 16:59:07 +02:00
2013-05-07 18:06:49 -07:00
func hijackServer ( w http . ResponseWriter ) ( io . ReadCloser , io . Writer , error ) {
conn , _ , err := w . ( http . Hijacker ) . Hijack ( )
2013-05-06 11:31:22 +02:00
if err != nil {
return nil , nil , err
}
// Flush the options to make sure the client sets the raw mode
2013-05-07 18:06:49 -07:00
conn . Write ( [ ] byte { } )
return conn , conn , nil
2013-05-06 11:31:22 +02:00
}
2014-12-09 20:21:21 +01:00
func closeStreams ( streams ... interface { } ) {
for _ , stream := range streams {
if tcpc , ok := stream . ( interface {
CloseWrite ( ) error
} ) ; ok {
tcpc . CloseWrite ( )
} else if closer , ok := stream . ( io . Closer ) ; ok {
closer . Close ( )
}
}
}
2014-09-25 08:24:41 -07:00
// Check to make sure request's Content-Type is application/json
func checkForJson ( r * http . Request ) error {
ct := r . Header . Get ( "Content-Type" )
// No Content-Type header is ok as long as there's no Body
if ct == "" {
if r . Body == nil || r . ContentLength == 0 {
return nil
}
}
// Otherwise it better be json
if api . MatchesContentType ( ct , "application/json" ) {
return nil
}
return fmt . Errorf ( "Content-Type specified (%s) must be 'application/json'" , ct )
}
2013-05-08 18:52:01 +02:00
//If we don't do this, POST method without Content-type (even with empty body) will fail
func parseForm ( r * http . Request ) error {
2013-10-08 19:15:29 +00:00
if r == nil {
return nil
}
2013-05-08 18:52:01 +02:00
if err := r . ParseForm ( ) ; err != nil && ! strings . HasPrefix ( err . Error ( ) , "mime:" ) {
return err
}
return nil
}
2013-05-22 20:07:26 -07:00
func parseMultipartForm ( r * http . Request ) error {
if err := r . ParseMultipartForm ( 4096 ) ; err != nil && ! strings . HasPrefix ( err . Error ( ) , "mime:" ) {
return err
}
return nil
}
2013-05-06 11:31:22 +02:00
func httpError ( w http . ResponseWriter , err error ) {
2013-07-11 12:21:43 +00:00
statusCode := http . StatusInternalServerError
2013-11-14 23:53:43 +00:00
// FIXME: this is brittle and should not be necessary.
// If we need to differentiate between different possible error types, we should
// create appropriate error types with clearly defined meaning.
2014-10-23 13:34:06 -04:00
errStr := strings . ToLower ( err . Error ( ) )
if strings . Contains ( errStr , "no such" ) {
2013-07-11 12:21:43 +00:00
statusCode = http . StatusNotFound
2014-10-23 13:34:06 -04:00
} else if strings . Contains ( errStr , "bad parameter" ) {
2013-07-11 12:21:43 +00:00
statusCode = http . StatusBadRequest
2014-10-23 13:34:06 -04:00
} else if strings . Contains ( errStr , "conflict" ) {
2013-07-11 12:21:43 +00:00
statusCode = http . StatusConflict
2014-10-23 13:34:06 -04:00
} else if strings . Contains ( errStr , "impossible" ) {
2013-07-11 12:21:43 +00:00
statusCode = http . StatusNotAcceptable
2014-10-23 13:34:06 -04:00
} else if strings . Contains ( errStr , "wrong login/password" ) {
2013-07-11 12:21:43 +00:00
statusCode = http . StatusUnauthorized
2014-10-23 13:34:06 -04:00
} else if strings . Contains ( errStr , "hasn't been activated" ) {
2013-07-11 12:21:43 +00:00
statusCode = http . StatusForbidden
2013-10-16 17:08:14 -07:00
}
2013-10-08 02:54:47 -05:00
if err != nil {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "HTTP Error: statusCode=%d %v" , statusCode , err )
2013-10-16 17:08:14 -07:00
http . Error ( w , err . Error ( ) , statusCode )
}
2013-05-06 11:31:22 +02:00
}
2015-02-24 11:12:47 -08:00
// writeJSONEnv writes the engine.Env values to the http response stream as a
// json encoded body.
func writeJSONEnv ( w http . ResponseWriter , code int , v engine . Env ) error {
2013-05-10 21:11:59 +02:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2013-09-11 00:23:53 -07:00
w . WriteHeader ( code )
2014-01-24 23:15:40 -08:00
return v . Encode ( w )
2013-05-10 21:11:59 +02:00
}
2015-02-24 11:12:47 -08:00
// writeJSON writes the value v to the http response stream as json with standard
// json encoding.
func writeJSON ( w http . ResponseWriter , code int , v interface { } ) error {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( code )
return json . NewEncoder ( w ) . Encode ( v )
}
2014-02-13 19:21:27 +00:00
func streamJSON ( job * engine . Job , w http . ResponseWriter , flush bool ) {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
if flush {
job . Stdout . Add ( utils . NewWriteFlusher ( w ) )
} else {
job . Stdout . Add ( w )
}
}
2015-03-31 16:21:37 -07:00
func getDaemon ( eng * engine . Engine ) * daemon . Daemon {
return eng . HackGetGlobalVar ( "httpapi.daemon" ) . ( * daemon . Daemon )
}
2014-02-21 23:15:28 +00:00
func postAuth ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-03-31 16:21:37 -07:00
var config * registry . AuthConfig
err := json . NewDecoder ( r . Body ) . Decode ( & config )
r . Body . Close ( )
2013-05-07 16:33:12 -07:00
if err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2015-03-31 16:21:37 -07:00
d := getDaemon ( eng )
status , err := d . RegistryService . Auth ( config )
if err != nil {
2013-07-23 15:04:31 +00:00
return err
2013-06-03 12:09:16 +00:00
}
2015-03-31 16:21:37 -07:00
return writeJSON ( w , http . StatusOK , & types . AuthResponse {
Status : status ,
} )
2013-05-07 16:33:12 -07:00
}
2013-05-06 13:34:31 +02:00
2014-02-21 23:15:28 +00:00
func getVersion ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-08 09:14:00 +01:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2014-01-29 19:26:54 +00:00
eng . ServeHTTP ( w , r )
2013-12-08 07:41:53 +00:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-05-06 13:34:31 +02:00
2014-02-21 23:15:28 +00:00
func postContainersKill ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2013-09-11 23:50:26 -07:00
if err := parseForm ( r ) ; err != nil {
return err
}
2014-01-29 19:26:54 +00:00
job := eng . Job ( "kill" , vars [ "name" ] )
2013-11-17 00:26:04 +00:00
if sig := r . Form . Get ( "signal" ) ; sig != "" {
job . Args = append ( job . Args , sig )
}
if err := job . Run ( ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-05-09 21:42:29 +02:00
w . WriteHeader ( http . StatusNoContent )
2013-05-10 20:20:49 +02:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-05-06 13:34:31 +02:00
2014-05-21 15:06:18 -06:00
func postContainersPause ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-09 22:50:48 +02:00
name := vars [ "name" ]
d := getDaemon ( eng )
cont , err := d . Get ( name )
if err != nil {
2014-05-21 15:06:18 -06:00
return err
}
2015-04-09 22:50:48 +02:00
if err := cont . Pause ( ) ; err != nil {
return fmt . Errorf ( "Cannot pause container %s: %s" , name , err )
}
cont . LogEvent ( "pause" )
2014-05-21 15:06:18 -06:00
w . WriteHeader ( http . StatusNoContent )
2015-04-09 22:50:48 +02:00
2014-05-21 15:06:18 -06:00
return nil
}
func postContainersUnpause ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-09 22:50:48 +02:00
name := vars [ "name" ]
d := getDaemon ( eng )
cont , err := d . Get ( name )
if err != nil {
2014-05-21 15:06:18 -06:00
return err
}
2015-04-09 22:50:48 +02:00
if err := cont . Unpause ( ) ; err != nil {
return fmt . Errorf ( "Cannot unpause container %s: %s" , name , err )
}
cont . LogEvent ( "unpause" )
2014-05-21 15:06:18 -06:00
w . WriteHeader ( http . StatusNoContent )
2015-04-09 22:50:48 +02:00
2014-05-21 15:06:18 -06:00
return nil
}
2014-02-21 23:15:28 +00:00
func getContainersExport ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2014-01-29 19:26:54 +00:00
job := eng . Job ( "export" , vars [ "name" ] )
2014-01-22 15:54:22 -08:00
job . Stdout . Add ( w )
2013-12-08 01:33:37 +00:00
if err := job . Run ( ) ; err != nil {
2013-05-13 11:38:13 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-05-10 20:20:49 +02:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-04-24 14:01:40 +02:00
2014-02-21 23:15:28 +00:00
func getImagesJSON ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 18:52:01 +02:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2014-01-13 14:55:31 -08:00
2015-04-07 18:57:54 -07:00
imagesConfig := graph . ImagesConfig {
Filters : r . Form . Get ( "filters" ) ,
// FIXME this parameter could just be a match filter
Filter : r . Form . Get ( "filter" ) ,
All : toBool ( r . Form . Get ( "all" ) ) ,
}
2014-01-13 14:55:31 -08:00
2015-04-07 18:57:54 -07:00
images , err := getDaemon ( eng ) . Repositories ( ) . Images ( & imagesConfig )
if err != nil {
2014-01-17 18:54:02 -08:00
return err
2014-01-13 14:55:31 -08:00
}
2015-04-07 18:57:54 -07:00
if version . GreaterThanOrEqualTo ( "1.7" ) {
return writeJSON ( w , http . StatusOK , images )
2013-05-07 16:33:12 -07:00
}
2014-01-13 14:55:31 -08:00
2015-04-07 18:57:54 -07:00
legacyImages := [ ] types . LegacyImage { }
for _ , image := range images {
for _ , repoTag := range image . RepoTags {
repo , tag := parsers . ParseRepositoryTag ( repoTag )
legacyImage := types . LegacyImage {
Repository : repo ,
Tag : tag ,
ID : image . ID ,
Created : image . Created ,
Size : image . Size ,
VirtualSize : image . VirtualSize ,
2014-01-13 14:55:31 -08:00
}
2015-04-07 18:57:54 -07:00
legacyImages = append ( legacyImages , legacyImage )
2014-01-13 14:55:31 -08:00
}
}
2015-04-07 18:57:54 -07:00
return writeJSON ( w , http . StatusOK , legacyImages )
2013-05-09 23:52:12 +02:00
}
2014-02-21 23:15:28 +00:00
func getImagesViz ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if version . GreaterThan ( "1.6" ) {
2013-10-08 13:52:36 +00:00
w . WriteHeader ( http . StatusNotFound )
return fmt . Errorf ( "This is now implemented in the client." )
}
2014-01-29 19:26:54 +00:00
eng . ServeHTTP ( w , r )
2013-10-08 13:52:36 +00:00
return nil
}
2014-02-21 23:15:28 +00:00
func getInfo ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-08 09:14:00 +01:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2014-01-29 19:26:54 +00:00
eng . ServeHTTP ( w , r )
2013-12-11 10:35:21 -08:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-04-18 18:56:22 +02:00
2014-02-21 23:15:28 +00:00
func getEvents ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-07-12 16:29:23 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-03 15:17:49 -07:00
var since int64 = - 1
if r . Form . Get ( "since" ) != "" {
s , err := strconv . ParseInt ( r . Form . Get ( "since" ) , 10 , 64 )
if err != nil {
return err
}
since = s
}
2014-01-23 11:12:17 -08:00
2015-04-03 15:17:49 -07:00
var until int64 = - 1
if r . Form . Get ( "until" ) != "" {
u , err := strconv . ParseInt ( r . Form . Get ( "until" ) , 10 , 64 )
if err != nil {
return err
}
until = u
}
timer := time . NewTimer ( 0 )
timer . Stop ( )
if until > 0 {
dur := time . Unix ( until , 0 ) . Sub ( time . Now ( ) )
timer = time . NewTimer ( dur )
}
ef , err := filters . FromParam ( r . Form . Get ( "filters" ) )
if err != nil {
return err
}
isFiltered := func ( field string , filter [ ] string ) bool {
if len ( filter ) == 0 {
return false
}
for _ , v := range filter {
if v == field {
return false
}
if strings . Contains ( field , ":" ) {
image := strings . Split ( field , ":" )
if image [ 0 ] == v {
return false
}
}
}
return true
}
d := getDaemon ( eng )
es := d . EventsService
w . Header ( ) . Set ( "Content-Type" , "application/json" )
enc := json . NewEncoder ( utils . NewWriteFlusher ( w ) )
getContainerId := func ( cn string ) string {
c , err := d . Get ( cn )
if err != nil {
return ""
}
return c . ID
}
sendEvent := func ( ev * jsonmessage . JSONMessage ) error {
//incoming container filter can be name,id or partial id, convert and replace as a full container id
for i , cn := range ef [ "container" ] {
ef [ "container" ] [ i ] = getContainerId ( cn )
}
if isFiltered ( ev . Status , ef [ "event" ] ) || isFiltered ( ev . From , ef [ "image" ] ) ||
isFiltered ( ev . ID , ef [ "container" ] ) {
return nil
}
return enc . Encode ( ev )
}
current , l := es . Subscribe ( )
defer es . Evict ( l )
for _ , ev := range current {
if ev . Time < since {
continue
}
if err := sendEvent ( ev ) ; err != nil {
return err
}
}
for {
select {
case ev := <- l :
jev , ok := ev . ( * jsonmessage . JSONMessage )
if ! ok {
continue
}
if err := sendEvent ( jev ) ; err != nil {
return err
}
case <- timer . C :
return nil
}
}
2013-07-10 12:55:05 +00:00
}
2014-02-21 23:15:28 +00:00
func getImagesHistory ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2014-01-13 16:50:10 -08:00
2015-04-10 18:55:07 +08:00
name := vars [ "name" ]
history , err := getDaemon ( eng ) . Repositories ( ) . History ( name )
if err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2015-04-10 18:55:07 +08:00
return writeJSON ( w , http . StatusOK , history )
2013-05-07 16:33:12 -07:00
}
2013-04-19 15:24:37 +02:00
2014-02-21 23:15:28 +00:00
func getContainersChanges ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2014-01-14 16:51:59 -08:00
2015-04-08 11:14:16 +02:00
name := vars [ "name" ]
if name == "" {
return fmt . Errorf ( "Container name cannot be empty" )
}
d := getDaemon ( eng )
cont , err := d . Get ( name )
if err != nil {
return err
}
changes , err := cont . Changes ( )
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , changes )
2013-05-07 16:33:12 -07:00
}
2013-04-19 15:24:37 +02:00
2014-02-21 23:15:28 +00:00
func getContainersTop ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if version . LessThan ( "1.4" ) {
2013-07-19 10:34:55 +00:00
return fmt . Errorf ( "top was improved a lot since 1.3, Please upgrade your docker client." )
}
2013-06-28 15:51:58 +00:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2013-07-19 10:06:32 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
2014-01-16 14:58:20 -08:00
2014-01-29 19:26:54 +00:00
job := eng . Job ( "top" , vars [ "name" ] , r . Form . Get ( "ps_args" ) )
2014-02-13 19:21:27 +00:00
streamJSON ( job , w , false )
2014-01-16 14:58:20 -08:00
return job . Run ( )
2013-06-28 15:51:58 +00:00
}
2014-02-21 23:15:28 +00:00
func getContainersJSON ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-04-07 12:34:30 -07:00
var err error
if err = parseForm ( r ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2014-01-16 14:00:18 -08:00
2015-04-07 12:34:30 -07:00
config := & daemon . ContainersConfig {
2015-04-07 18:57:54 -07:00
All : toBool ( r . Form . Get ( "all" ) ) ,
Size : toBool ( r . Form . Get ( "size" ) ) ,
2015-04-07 12:34:30 -07:00
Since : r . Form . Get ( "since" ) ,
Before : r . Form . Get ( "before" ) ,
Filters : r . Form . Get ( "filters" ) ,
2013-06-20 14:19:50 +00:00
}
2015-04-07 12:34:30 -07:00
if tmpLimit := r . Form . Get ( "limit" ) ; tmpLimit != "" {
config . Limit , err = strconv . Atoi ( tmpLimit )
if err != nil {
2014-01-16 14:00:18 -08:00
return err
2013-09-04 23:41:44 +02:00
}
2013-05-07 16:33:12 -07:00
}
2015-04-07 12:34:30 -07:00
containers , err := getDaemon ( eng ) . Containers ( config )
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , containers )
2013-05-07 16:33:12 -07:00
}
2013-05-06 11:31:22 +02:00
2015-01-07 14:43:04 -08:00
func getContainersStats ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
name := vars [ "name" ]
job := eng . Job ( "container_stats" , name )
streamJSON ( job , w , true )
return job . Run ( )
}
2014-04-02 23:26:06 +04:00
func getContainersLogs ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
var (
2014-06-18 23:11:04 +04:00
inspectJob = eng . Job ( "container_inspect" , vars [ "name" ] )
logsJob = eng . Job ( "logs" , vars [ "name" ] )
c , err = inspectJob . Stdout . AddEnv ( )
2014-04-02 23:26:06 +04:00
)
if err != nil {
return err
}
2014-06-18 23:11:04 +04:00
logsJob . Setenv ( "follow" , r . Form . Get ( "follow" ) )
2014-06-03 15:09:33 +04:00
logsJob . Setenv ( "tail" , r . Form . Get ( "tail" ) )
2014-06-18 23:11:04 +04:00
logsJob . Setenv ( "stdout" , r . Form . Get ( "stdout" ) )
logsJob . Setenv ( "stderr" , r . Form . Get ( "stderr" ) )
logsJob . Setenv ( "timestamps" , r . Form . Get ( "timestamps" ) )
// Validate args here, because we can't return not StatusOK after job.Run() call
stdout , stderr := logsJob . GetenvBool ( "stdout" ) , logsJob . GetenvBool ( "stderr" )
if ! ( stdout || stderr ) {
return fmt . Errorf ( "Bad parameters: you must choose at least one stream" )
}
if err = inspectJob . Run ( ) ; err != nil {
2014-04-02 23:26:06 +04:00
return err
}
var outStream , errStream io . Writer
outStream = utils . NewWriteFlusher ( w )
if c . GetSubEnv ( "Config" ) != nil && ! c . GetSubEnv ( "Config" ) . GetBool ( "Tty" ) && version . GreaterThanOrEqualTo ( "1.6" ) {
2014-09-17 18:04:56 +03:00
errStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stderr )
outStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stdout )
2014-04-02 23:26:06 +04:00
} else {
errStream = outStream
}
2014-06-18 23:11:04 +04:00
logsJob . Stdout . Add ( outStream )
logsJob . Stderr . Set ( errStream )
if err := logsJob . Run ( ) ; err != nil {
2014-06-17 16:07:34 -04:00
fmt . Fprintf ( outStream , "Error running logs job: %s\n" , err )
2014-04-02 23:26:06 +04:00
}
return nil
}
2014-02-21 23:15:28 +00:00
func postImagesTag ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 18:52:01 +02:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2013-04-19 15:24:37 +02:00
2014-01-29 19:26:54 +00:00
job := eng . Job ( "tag" , vars [ "name" ] , r . Form . Get ( "repo" ) , r . Form . Get ( "tag" ) )
2013-12-11 17:52:41 -08:00
job . Setenv ( "force" , r . Form . Get ( "force" ) )
if err := job . Run ( ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
w . WriteHeader ( http . StatusCreated )
2013-05-10 20:20:49 +02:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-04-24 16:06:03 +02:00
2014-02-21 23:15:28 +00:00
func postCommit ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 18:52:01 +02:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2014-01-24 23:15:40 -08:00
var (
2014-05-09 18:09:59 +00:00
config engine . Env
job = eng . Job ( "commit" , r . Form . Get ( "container" ) )
stdoutBuffer = bytes . NewBuffer ( nil )
2014-01-24 23:15:40 -08:00
)
2014-09-25 08:24:41 -07:00
if err := checkForJson ( r ) ; err != nil {
return err
}
2014-02-10 23:52:15 +00:00
if err := config . Decode ( r . Body ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "%s" , err )
2013-05-07 16:33:12 -07:00
}
2013-12-11 17:03:48 -08:00
2014-06-07 23:37:31 -07:00
if r . FormValue ( "pause" ) == "" && version . GreaterThanOrEqualTo ( "1.13" ) {
job . Setenv ( "pause" , "1" )
} else {
job . Setenv ( "pause" , r . FormValue ( "pause" ) )
}
2013-12-11 17:03:48 -08:00
job . Setenv ( "repo" , r . Form . Get ( "repo" ) )
job . Setenv ( "tag" , r . Form . Get ( "tag" ) )
job . Setenv ( "author" , r . Form . Get ( "author" ) )
job . Setenv ( "comment" , r . Form . Get ( "comment" ) )
2015-02-05 12:26:05 +01:00
job . SetenvList ( "changes" , r . Form [ "changes" ] )
2014-01-28 03:26:24 +00:00
job . SetenvSubEnv ( "config" , & config )
2013-12-11 17:03:48 -08:00
2014-05-09 18:09:59 +00:00
job . Stdout . Add ( stdoutBuffer )
2013-12-11 17:03:48 -08:00
if err := job . Run ( ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2015-03-28 17:39:24 +01:00
return writeJSON ( w , http . StatusCreated , & types . ContainerCommitResponse {
ID : engine . Tail ( stdoutBuffer , 1 ) ,
} )
2013-05-07 16:33:12 -07:00
}
2013-04-24 16:06:03 +02:00
2013-05-09 17:50:56 -07:00
// Creates an image from Pull or from Import
2014-02-21 23:15:28 +00:00
func postImagesCreate ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 18:52:01 +02:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-04-22 23:37:22 +02:00
2014-01-22 13:35:35 -08:00
var (
image = r . Form . Get ( "fromImage" )
2014-07-07 23:09:42 +00:00
repo = r . Form . Get ( "repo" )
2014-01-22 13:35:35 -08:00
tag = r . Form . Get ( "tag" )
job * engine . Job
)
2013-08-30 22:49:37 +02:00
authEncoded := r . Header . Get ( "X-Registry-Auth" )
2014-03-10 17:16:58 -07:00
authConfig := & registry . AuthConfig { }
2013-08-30 22:49:37 +02:00
if authEncoded != "" {
authJson := base64 . NewDecoder ( base64 . URLEncoding , strings . NewReader ( authEncoded ) )
if err := json . NewDecoder ( authJson ) . Decode ( authConfig ) ; err != nil {
2013-09-03 20:45:49 +02:00
// for a pull it is not an error if no auth was given
2013-08-30 21:46:19 +02:00
// to increase compatibility with the existing api it is defaulting to be empty
2014-03-10 17:16:58 -07:00
authConfig = & registry . AuthConfig { }
2013-09-03 20:45:49 +02:00
}
}
2013-05-07 16:33:12 -07:00
if image != "" { //pull
2014-07-07 23:09:42 +00:00
if tag == "" {
2014-07-28 17:23:38 -07:00
image , tag = parsers . ParseRepositoryTag ( image )
2014-07-07 23:09:42 +00:00
}
2013-08-22 21:15:31 +02:00
metaHeaders := map [ string ] [ ] string { }
for k , v := range r . Header {
if strings . HasPrefix ( k , "X-Meta-" ) {
metaHeaders [ k ] = v
}
}
2014-07-07 23:09:42 +00:00
job = eng . Job ( "pull" , image , tag )
2014-02-21 23:15:28 +00:00
job . SetenvBool ( "parallel" , version . GreaterThan ( "1.3" ) )
2014-01-22 13:35:35 -08:00
job . SetenvJson ( "metaHeaders" , metaHeaders )
job . SetenvJson ( "authConfig" , authConfig )
2013-05-07 16:33:12 -07:00
} else { //import
2014-07-07 23:09:42 +00:00
if tag == "" {
2014-07-28 17:23:38 -07:00
repo , tag = parsers . ParseRepositoryTag ( repo )
2014-07-07 23:09:42 +00:00
}
job = eng . Job ( "import" , r . Form . Get ( "fromSrc" ) , repo , tag )
2014-01-22 13:35:35 -08:00
job . Stdin . Add ( r . Body )
2015-02-05 12:26:05 +01:00
job . SetenvList ( "changes" , r . Form [ "changes" ] )
2014-01-22 13:35:35 -08:00
}
2014-02-21 23:15:28 +00:00
if version . GreaterThan ( "1.0" ) {
2014-02-13 19:21:27 +00:00
job . SetenvBool ( "json" , true )
streamJSON ( job , w , true )
} else {
job . Stdout . Add ( utils . NewWriteFlusher ( w ) )
}
2014-01-22 13:35:35 -08:00
if err := job . Run ( ) ; err != nil {
if ! job . Stdout . Used ( ) {
2013-05-16 12:09:06 -07:00
return err
2013-05-07 17:19:41 +02:00
}
2015-03-17 19:18:41 -07:00
sf := streamformatter . NewStreamFormatter ( version . GreaterThan ( "1.0" ) )
2014-01-22 13:35:35 -08:00
w . Write ( sf . FormatError ( err ) )
2013-05-07 16:33:12 -07:00
}
2014-01-22 13:35:35 -08:00
2013-05-10 20:20:49 +02:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-05-07 17:19:41 +02:00
2014-02-21 23:15:28 +00:00
func getImagesSearch ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 18:52:01 +02:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2014-01-21 04:06:19 +00:00
var (
2015-03-31 16:21:37 -07:00
config * registry . AuthConfig
2014-01-21 04:06:19 +00:00
authEncoded = r . Header . Get ( "X-Registry-Auth" )
2015-03-31 16:21:37 -07:00
headers = map [ string ] [ ] string { }
2014-01-21 04:06:19 +00:00
)
if authEncoded != "" {
authJson := base64 . NewDecoder ( base64 . URLEncoding , strings . NewReader ( authEncoded ) )
2015-03-31 16:21:37 -07:00
if err := json . NewDecoder ( authJson ) . Decode ( & config ) ; err != nil {
2014-01-21 04:06:19 +00:00
// for a search it is not an error if no auth was given
// to increase compatibility with the existing api it is defaulting to be empty
2015-03-31 16:21:37 -07:00
config = & registry . AuthConfig { }
2014-01-21 04:06:19 +00:00
}
}
for k , v := range r . Header {
if strings . HasPrefix ( k , "X-Meta-" ) {
2015-03-31 16:21:37 -07:00
headers [ k ] = v
2014-01-21 04:06:19 +00:00
}
}
2015-03-31 16:21:37 -07:00
d := getDaemon ( eng )
query , err := d . RegistryService . Search ( r . Form . Get ( "term" ) , config , headers )
if err != nil {
return err
}
return json . NewEncoder ( w ) . Encode ( query . Results )
2013-05-07 16:33:12 -07:00
}
2013-04-30 17:04:31 +02:00
2014-02-21 23:15:28 +00:00
func postImagesPush ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-22 17:33:29 -08:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2013-08-22 21:15:31 +02:00
metaHeaders := map [ string ] [ ] string { }
for k , v := range r . Header {
if strings . HasPrefix ( k , "X-Meta-" ) {
metaHeaders [ k ] = v
}
}
2013-05-15 19:21:37 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
2014-03-10 17:16:58 -07:00
authConfig := & registry . AuthConfig { }
2013-08-23 09:38:33 +02:00
2013-08-30 22:49:37 +02:00
authEncoded := r . Header . Get ( "X-Registry-Auth" )
if authEncoded != "" {
// the new format is to handle the authConfig as a header
authJson := base64 . NewDecoder ( base64 . URLEncoding , strings . NewReader ( authEncoded ) )
if err := json . NewDecoder ( authJson ) . Decode ( authConfig ) ; err != nil {
// to increase compatibility to existing api it is defaulting to be empty
2014-03-10 17:16:58 -07:00
authConfig = & registry . AuthConfig { }
2013-08-23 09:38:33 +02:00
}
} else {
2013-08-30 21:46:19 +02:00
// the old format is supported for compatibility if there was no authConfig header
2013-08-23 09:38:33 +02:00
if err := json . NewDecoder ( r . Body ) . Decode ( authConfig ) ; err != nil {
2015-03-27 23:17:50 +08:00
return fmt . Errorf ( "Bad parameters and missing X-Registry-Auth: %v" , err )
2013-08-23 09:38:33 +02:00
}
}
2013-05-15 19:21:37 +00:00
2014-01-29 19:26:54 +00:00
job := eng . Job ( "push" , vars [ "name" ] )
2014-01-22 17:33:29 -08:00
job . SetenvJson ( "metaHeaders" , metaHeaders )
job . SetenvJson ( "authConfig" , authConfig )
2014-03-31 18:50:10 -07:00
job . Setenv ( "tag" , r . Form . Get ( "tag" ) )
2014-02-21 23:15:28 +00:00
if version . GreaterThan ( "1.0" ) {
2014-02-13 19:21:27 +00:00
job . SetenvBool ( "json" , true )
streamJSON ( job , w , true )
} else {
job . Stdout . Add ( utils . NewWriteFlusher ( w ) )
}
2014-01-22 17:33:29 -08:00
if err := job . Run ( ) ; err != nil {
if ! job . Stdout . Used ( ) {
return err
2013-05-25 15:09:46 +00:00
}
2015-03-17 19:18:41 -07:00
sf := streamformatter . NewStreamFormatter ( version . GreaterThan ( "1.0" ) )
2014-01-22 17:33:29 -08:00
w . Write ( sf . FormatError ( err ) )
2013-05-15 19:21:37 +00:00
}
2013-05-10 20:20:49 +02:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-05-07 19:23:50 +02:00
2014-02-21 23:15:28 +00:00
func getImagesGet ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-07 12:39:15 -08:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-05-01 00:26:24 -04:00
if err := parseForm ( r ) ; err != nil {
return err
}
2014-02-21 23:15:28 +00:00
if version . GreaterThan ( "1.0" ) {
2013-11-14 01:05:37 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/x-tar" )
}
2014-05-01 00:26:24 -04:00
var job * engine . Job
if name , ok := vars [ "name" ] ; ok {
job = eng . Job ( "image_export" , name )
} else {
job = eng . Job ( "image_export" , r . Form [ "names" ] ... )
}
2014-01-22 15:54:22 -08:00
job . Stdout . Add ( w )
2014-01-07 12:39:15 -08:00
return job . Run ( )
2013-09-02 09:06:17 -07:00
}
2014-02-21 23:15:28 +00:00
func postImagesLoad ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-29 19:26:54 +00:00
job := eng . Job ( "load" )
2014-01-20 16:09:17 -08:00
job . Stdin . Add ( r . Body )
2015-04-01 10:44:40 -04:00
job . Stdout . Add ( w )
2014-01-20 16:09:17 -08:00
return job . Run ( )
2013-09-02 09:06:17 -07:00
}
2014-02-21 23:15:28 +00:00
func postContainersCreate ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-10-28 16:58:59 -07:00
if err := parseForm ( r ) ; err != nil {
return nil
}
2015-02-24 11:12:47 -08:00
if err := checkForJson ( r ) ; err != nil {
return err
}
2014-01-24 23:15:40 -08:00
var (
2014-05-09 18:09:59 +00:00
job = eng . Job ( "create" , r . Form . Get ( "name" ) )
outWarnings [ ] string
stdoutBuffer = bytes . NewBuffer ( nil )
warnings = bytes . NewBuffer ( nil )
2014-01-24 23:15:40 -08:00
)
2014-09-25 08:24:41 -07:00
2013-10-27 19:20:00 -07:00
if err := job . DecodeEnv ( r . Body ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-10-27 19:20:00 -07:00
// Read container ID from the first line of stdout
2014-05-09 18:09:59 +00:00
job . Stdout . Add ( stdoutBuffer )
2013-10-27 19:20:00 -07:00
// Read warnings from stderr
2013-11-20 07:37:03 +00:00
job . Stderr . Add ( warnings )
2013-10-27 19:20:00 -07:00
if err := job . Run ( ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-11-20 07:37:03 +00:00
// Parse warnings from stderr
scanner := bufio . NewScanner ( warnings )
for scanner . Scan ( ) {
2014-01-24 23:15:40 -08:00
outWarnings = append ( outWarnings , scanner . Text ( ) )
2013-11-20 07:37:03 +00:00
}
2015-02-24 11:12:47 -08:00
return writeJSON ( w , http . StatusCreated , & types . ContainerCreateResponse {
ID : engine . Tail ( stdoutBuffer , 1 ) ,
Warnings : outWarnings ,
} )
2013-05-07 16:33:12 -07:00
}
2013-05-07 19:23:50 +02:00
2014-02-21 23:15:28 +00:00
func postContainersRestart ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 18:52:01 +02:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2014-01-29 19:26:54 +00:00
job := eng . Job ( "restart" , vars [ "name" ] )
2014-01-06 17:34:51 -08:00
job . Setenv ( "t" , r . Form . Get ( "t" ) )
if err := job . Run ( ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-05-09 21:42:29 +02:00
w . WriteHeader ( http . StatusNoContent )
2013-05-10 20:20:49 +02:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-05-06 13:34:31 +02:00
2014-10-05 02:47:54 +00:00
func postContainerRename ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-10 01:52:55 +08:00
d := getDaemon ( eng )
name := vars [ "name" ]
newName := r . Form . Get ( "name" )
if err := d . ContainerRename ( name , newName ) ; err != nil {
2014-10-05 02:47:54 +00:00
return err
}
w . WriteHeader ( http . StatusNoContent )
return nil
}
2014-02-21 23:15:28 +00:00
func deleteContainers ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 18:52:01 +02:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2014-07-06 22:43:24 +02:00
2015-04-08 23:38:53 +02:00
name := vars [ "name" ]
if name == "" {
return fmt . Errorf ( "Container name cannot be empty" )
}
2014-07-06 22:43:24 +02:00
2015-04-08 23:38:53 +02:00
d := getDaemon ( eng )
config := & daemon . ContainerRmConfig {
ForceRemove : toBool ( r . Form . Get ( "force" ) ) ,
RemoveVolume : toBool ( r . Form . Get ( "v" ) ) ,
RemoveLink : toBool ( r . Form . Get ( "link" ) ) ,
}
if err := d . ContainerRm ( name , config ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2015-04-08 23:38:53 +02:00
2013-05-09 21:42:29 +02:00
w . WriteHeader ( http . StatusNoContent )
2015-04-08 23:38:53 +02:00
2013-05-10 20:20:49 +02:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-04-22 23:37:22 +02:00
2014-02-21 23:15:28 +00:00
func deleteImages ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-20 18:31:45 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2014-01-16 18:40:33 -08:00
2015-04-09 13:59:50 +02:00
d := getDaemon ( eng )
name := vars [ "name" ]
force := toBool ( r . Form . Get ( "force" ) )
noprune := toBool ( r . Form . Get ( "noprune" ) )
list , err := d . ImageDelete ( name , force , noprune )
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , list )
2013-05-07 16:33:12 -07:00
}
2013-04-19 15:24:37 +02:00
2014-02-21 23:15:28 +00:00
func postContainersStart ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-10-26 19:24:01 -07:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-06-24 18:31:56 +00:00
var (
name = vars [ "name" ]
job = eng . Job ( "start" , name )
)
2014-09-30 22:06:31 -04:00
// If contentLength is -1, we can assumed chunked encoding
// or more technically that the length is unknown
// http://golang.org/src/pkg/net/http/request.go#L139
// net/http otherwise seems to swallow any headers related to chunked encoding
// including r.TransferEncoding
2013-05-13 17:39:54 -06:00
// allow a nil body for backwards compatibility
2014-09-30 22:06:31 -04:00
if r . Body != nil && ( r . ContentLength > 0 || r . ContentLength == - 1 ) {
2014-09-25 08:24:41 -07:00
if err := checkForJson ( r ) ; err != nil {
return err
2014-08-01 15:26:30 -07:00
}
if err := job . DecodeEnv ( r . Body ) ; err != nil {
return err
2013-05-13 17:39:54 -06:00
}
}
2014-08-01 15:59:19 -07:00
2013-10-26 19:24:01 -07:00
if err := job . Run ( ) ; err != nil {
2014-06-24 18:31:56 +00:00
if err . Error ( ) == "Container already started" {
w . WriteHeader ( http . StatusNotModified )
return nil
}
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-05-09 21:42:29 +02:00
w . WriteHeader ( http . StatusNoContent )
2013-05-10 20:20:49 +02:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-04-19 15:24:37 +02:00
2014-02-21 23:15:28 +00:00
func postContainersStop ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 18:52:01 +02:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2014-01-29 19:26:54 +00:00
job := eng . Job ( "stop" , vars [ "name" ] )
2013-12-11 15:36:50 -08:00
job . Setenv ( "t" , r . Form . Get ( "t" ) )
2013-11-17 03:00:16 +00:00
if err := job . Run ( ) ; err != nil {
2014-06-24 18:31:56 +00:00
if err . Error ( ) == "Container already stopped" {
w . WriteHeader ( http . StatusNotModified )
return nil
}
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-05-09 21:42:29 +02:00
w . WriteHeader ( http . StatusNoContent )
2013-05-10 20:20:49 +02:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-04-19 15:24:37 +02:00
2014-02-21 23:15:28 +00:00
func postContainersWait ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2014-01-24 23:15:40 -08:00
var (
2014-05-09 18:09:59 +00:00
stdoutBuffer = bytes . NewBuffer ( nil )
job = eng . Job ( "wait" , vars [ "name" ] )
2014-01-24 23:15:40 -08:00
)
2014-05-09 18:09:59 +00:00
job . Stdout . Add ( stdoutBuffer )
2013-11-25 01:05:59 +00:00
if err := job . Run ( ) ; err != nil {
return err
}
2015-03-25 21:01:14 -06:00
statusCode , err := strconv . Atoi ( engine . Tail ( stdoutBuffer , 1 ) )
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , & types . ContainerWaitResponse {
StatusCode : statusCode ,
} )
2013-05-07 16:33:12 -07:00
}
2013-05-06 11:31:22 +02:00
2014-02-21 23:15:28 +00:00
func postContainersResize ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-23 19:33:28 -07:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-09 23:51:22 +02:00
height , err := strconv . Atoi ( r . Form . Get ( "h" ) )
if err != nil {
return nil
}
width , err := strconv . Atoi ( r . Form . Get ( "w" ) )
if err != nil {
return nil
}
d := getDaemon ( eng )
cont , err := d . Get ( vars [ "name" ] )
if err != nil {
2013-05-23 19:33:28 -07:00
return err
}
2015-04-09 23:51:22 +02:00
if err := cont . Resize ( height , width ) ; err != nil {
return err
}
2013-05-23 19:33:28 -07:00
return nil
}
2014-02-21 23:15:28 +00:00
func postContainersAttach ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 18:52:01 +02:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2013-05-07 16:33:12 -07:00
2015-04-06 12:19:38 -07:00
d := getDaemon ( eng )
cont , err := d . Get ( vars [ "name" ] )
2014-01-28 03:26:24 +00:00
if err != nil {
2014-01-15 17:43:57 -08:00
return err
}
2013-05-28 16:19:12 +00:00
2013-09-11 11:35:09 -07:00
inStream , outStream , err := hijackServer ( w )
2013-05-07 16:33:12 -07:00
if err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2014-12-09 20:21:21 +01:00
defer closeStreams ( inStream , outStream )
2013-04-10 19:48:21 -07:00
2013-09-11 11:35:09 -07:00
var errStream io . Writer
2014-12-15 19:57:39 +01:00
if _ , ok := r . Header [ "Upgrade" ] ; ok {
fmt . Fprintf ( outStream , "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n" )
} else {
fmt . Fprintf ( outStream , "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n" )
}
2013-09-11 11:35:09 -07:00
2015-04-06 12:19:38 -07:00
if ! cont . Config . Tty && version . GreaterThanOrEqualTo ( "1.6" ) {
2014-09-17 18:04:56 +03:00
errStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stderr )
outStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stdout )
2013-09-11 11:35:09 -07:00
} else {
errStream = outStream
}
2015-04-09 09:58:44 -07:00
logs := toBool ( r . Form . Get ( "logs" ) )
stream := toBool ( r . Form . Get ( "stream" ) )
2013-09-11 11:35:09 -07:00
2015-04-09 09:58:44 -07:00
var stdin io . ReadCloser
var stdout , stderr io . Writer
if toBool ( r . Form . Get ( "stdin" ) ) {
stdin = inStream
}
if toBool ( r . Form . Get ( "stdout" ) ) {
stdout = outStream
}
if toBool ( r . Form . Get ( "stderr" ) ) {
stderr = errStream
}
if err := cont . AttachWithLogs ( stdin , stdout , stderr , logs , stream ) ; err != nil {
2014-06-17 16:07:34 -04:00
fmt . Fprintf ( outStream , "Error attaching: %s\n" , err )
2013-05-07 16:33:12 -07:00
}
2013-05-10 20:20:49 +02:00
return nil
2013-05-07 16:33:12 -07:00
}
2013-05-02 05:07:06 +02:00
2014-02-21 23:15:28 +00:00
func wsContainersAttach ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-07-05 19:55:15 +02:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-06 12:19:38 -07:00
d := getDaemon ( eng )
2013-07-05 19:55:15 +02:00
2015-04-06 12:19:38 -07:00
cont , err := d . Get ( vars [ "name" ] )
if err != nil {
2013-07-05 19:55:15 +02:00
return err
}
2013-07-13 16:59:07 +02:00
h := websocket . Handler ( func ( ws * websocket . Conn ) {
defer ws . Close ( )
2015-04-06 12:19:38 -07:00
logs := r . Form . Get ( "logs" ) != ""
stream := r . Form . Get ( "stream" ) != ""
if err := cont . AttachWithLogs ( ws , ws , ws , logs , stream ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "Error attaching websocket: %s" , err )
2013-07-13 16:59:07 +02:00
}
} )
h . ServeHTTP ( w , r )
2013-07-05 19:55:15 +02:00
return nil
}
2014-02-21 23:15:28 +00:00
func getContainersByName ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2014-05-20 19:36:15 +00:00
var job = eng . Job ( "container_inspect" , vars [ "name" ] )
2014-05-31 01:13:37 +00:00
if version . LessThan ( "1.12" ) {
2014-06-17 00:06:21 +00:00
job . SetenvBool ( "raw" , true )
2014-05-31 01:13:37 +00:00
}
2014-02-13 19:21:27 +00:00
streamJSON ( job , w , false )
2014-01-20 15:15:00 -08:00
return job . Run ( )
2013-05-07 16:33:12 -07:00
}
2013-05-02 05:07:06 +02:00
2014-11-17 15:50:09 -08:00
func getExecByID ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if vars == nil {
return fmt . Errorf ( "Missing parameter 'id'" )
}
var job = eng . Job ( "execInspect" , vars [ "id" ] )
streamJSON ( job , w , false )
return job . Run ( )
}
2014-02-21 23:15:28 +00:00
func getImagesByName ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 16:28:47 -07:00
if vars == nil {
2013-05-10 20:20:49 +02:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 16:28:47 -07:00
}
2014-05-20 19:36:15 +00:00
var job = eng . Job ( "image_inspect" , vars [ "name" ] )
2014-05-31 01:13:37 +00:00
if version . LessThan ( "1.12" ) {
2014-06-17 00:06:21 +00:00
job . SetenvBool ( "raw" , true )
2014-05-31 01:13:37 +00:00
}
2014-02-13 19:21:27 +00:00
streamJSON ( job , w , false )
2014-01-20 15:15:00 -08:00
return job . Run ( )
2013-05-19 10:46:24 -07:00
}
2014-02-21 23:15:28 +00:00
func postBuild ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if version . LessThan ( "1.3" ) {
2013-06-19 14:59:28 -07:00
return fmt . Errorf ( "Multipart upload for build is no longer supported. Please upgrade your docker client." )
2013-05-22 20:07:26 -07:00
}
2013-12-09 16:25:19 -08:00
var (
2014-05-07 22:59:13 +00:00
authEncoded = r . Header . Get ( "X-Registry-Auth" )
authConfig = & registry . AuthConfig { }
2014-01-03 15:13:32 -05:00
configFileEncoded = r . Header . Get ( "X-Registry-Config" )
2014-03-10 17:16:58 -07:00
configFile = & registry . ConfigFile { }
2014-01-29 19:26:54 +00:00
job = eng . Job ( "build" )
2013-12-09 16:25:19 -08:00
)
2014-01-03 15:13:32 -05:00
// This block can be removed when API versions prior to 1.9 are deprecated.
// Both headers will be parsed and sent along to the daemon, but if a non-empty
// ConfigFile is present, any value provided as an AuthConfig directly will
// be overridden. See BuildFile::CmdFrom for details.
2014-02-21 23:15:28 +00:00
if version . LessThan ( "1.9" ) && authEncoded != "" {
2013-12-06 14:27:10 -08:00
authJson := base64 . NewDecoder ( base64 . URLEncoding , strings . NewReader ( authEncoded ) )
if err := json . NewDecoder ( authJson ) . Decode ( authConfig ) ; err != nil {
// for a pull it is not an error if no auth was given
// to increase compatibility with the existing api it is defaulting to be empty
2014-03-10 17:16:58 -07:00
authConfig = & registry . AuthConfig { }
2013-12-06 14:27:10 -08:00
}
}
2013-05-22 20:07:26 -07:00
2014-01-03 15:13:32 -05:00
if configFileEncoded != "" {
configFileJson := base64 . NewDecoder ( base64 . URLEncoding , strings . NewReader ( configFileEncoded ) )
if err := json . NewDecoder ( configFileJson ) . Decode ( configFile ) ; err != nil {
// for a pull it is not an error if no auth was given
// to increase compatibility with the existing api it is defaulting to be empty
2014-03-10 17:16:58 -07:00
configFile = & registry . ConfigFile { }
2014-01-03 15:13:32 -05:00
}
}
2014-02-21 23:15:28 +00:00
if version . GreaterThanOrEqualTo ( "1.8" ) {
2014-01-23 12:19:52 -08:00
job . SetenvBool ( "json" , true )
2014-02-13 19:21:27 +00:00
streamJSON ( job , w , true )
} else {
job . Stdout . Add ( utils . NewWriteFlusher ( w ) )
2013-10-12 14:14:52 +02:00
}
2014-05-19 21:01:55 +03:00
2015-04-07 18:57:54 -07:00
if toBool ( r . FormValue ( "forcerm" ) ) && version . GreaterThanOrEqualTo ( "1.12" ) {
2014-05-16 14:47:33 +03:00
job . Setenv ( "rm" , "1" )
2014-05-19 21:01:55 +03:00
} else if r . FormValue ( "rm" ) == "" && version . GreaterThanOrEqualTo ( "1.12" ) {
job . Setenv ( "rm" , "1" )
2014-05-16 14:47:33 +03:00
} else {
job . Setenv ( "rm" , r . FormValue ( "rm" ) )
}
2015-04-07 18:57:54 -07:00
if toBool ( r . FormValue ( "pull" ) ) && version . GreaterThanOrEqualTo ( "1.16" ) {
2014-11-21 19:51:32 +02:00
job . Setenv ( "pull" , "1" )
}
2014-01-23 12:19:52 -08:00
job . Stdin . Add ( r . Body )
job . Setenv ( "remote" , r . FormValue ( "remote" ) )
2014-09-11 07:42:17 -07:00
job . Setenv ( "dockerfile" , r . FormValue ( "dockerfile" ) )
2014-01-23 12:19:52 -08:00
job . Setenv ( "t" , r . FormValue ( "t" ) )
job . Setenv ( "q" , r . FormValue ( "q" ) )
job . Setenv ( "nocache" , r . FormValue ( "nocache" ) )
2014-05-16 14:47:33 +03:00
job . Setenv ( "forcerm" , r . FormValue ( "forcerm" ) )
2014-05-07 22:59:13 +00:00
job . SetenvJson ( "authConfig" , authConfig )
2014-05-07 22:58:09 +00:00
job . SetenvJson ( "configFile" , configFile )
2015-02-06 09:33:01 -05:00
job . Setenv ( "memswap" , r . FormValue ( "memswap" ) )
job . Setenv ( "memory" , r . FormValue ( "memory" ) )
job . Setenv ( "cpusetcpus" , r . FormValue ( "cpusetcpus" ) )
job . Setenv ( "cpushares" , r . FormValue ( "cpushares" ) )
2014-01-23 12:19:52 -08:00
2015-03-10 22:10:00 +00:00
// Job cancellation. Note: not all job types support this.
if closeNotifier , ok := w . ( http . CloseNotifier ) ; ok {
finished := make ( chan struct { } )
defer close ( finished )
go func ( ) {
select {
case <- finished :
case <- closeNotifier . CloseNotify ( ) :
2015-03-26 23:22:04 +01:00
logrus . Infof ( "Client disconnected, cancelling job: %s" , job . Name )
2015-03-10 22:10:00 +00:00
job . Cancel ( )
}
} ( )
}
2014-01-23 12:19:52 -08:00
if err := job . Run ( ) ; err != nil {
if ! job . Stdout . Used ( ) {
return err
2013-10-12 14:14:52 +02:00
}
2015-03-17 19:18:41 -07:00
sf := streamformatter . NewStreamFormatter ( version . GreaterThanOrEqualTo ( "1.8" ) )
2014-01-23 12:19:52 -08:00
w . Write ( sf . FormatError ( err ) )
2013-05-22 20:07:26 -07:00
}
return nil
}
2014-02-21 23:15:28 +00:00
func postContainersCopy ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-07-16 19:07:41 -09:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-09-25 08:24:41 -07:00
if err := checkForJson ( r ) ; err != nil {
return err
}
2015-04-09 13:05:31 -07:00
cfg := types . CopyConfig { }
if err := json . NewDecoder ( r . Body ) . Decode ( & cfg ) ; err != nil {
2014-09-25 08:24:41 -07:00
return err
2013-07-16 19:07:41 -09:00
}
2015-04-09 13:05:31 -07:00
if cfg . Resource == "" {
2013-11-16 20:15:04 +10:00
return fmt . Errorf ( "Path cannot be empty" )
2013-07-16 19:07:41 -09:00
}
2014-03-16 17:48:46 +00:00
2015-04-09 13:05:31 -07:00
res := cfg . Resource
2014-03-16 17:48:46 +00:00
2015-04-09 13:05:31 -07:00
if res [ 0 ] == '/' {
res = res [ 1 : ]
2013-07-16 19:07:41 -09:00
}
2015-04-09 13:05:31 -07:00
cont , err := getDaemon ( eng ) . Get ( vars [ "name" ] )
if err != nil {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "%v" , err )
2014-12-16 15:06:35 -08:00
if strings . Contains ( strings . ToLower ( err . Error ( ) ) , "no such id" ) {
2014-02-15 00:43:55 +00:00
w . WriteHeader ( http . StatusNotFound )
2015-04-09 13:05:31 -07:00
return nil
}
}
data , err := cont . Copy ( res )
if err != nil {
logrus . Errorf ( "%v" , err )
if os . IsNotExist ( err ) {
return fmt . Errorf ( "Could not find the file %s in container %s" , cfg . Resource , vars [ "name" ] )
2014-02-15 00:43:55 +00:00
}
2015-04-09 13:05:31 -07:00
return err
2013-07-16 19:07:41 -09:00
}
2015-04-09 13:05:31 -07:00
defer data . Close ( )
w . Header ( ) . Set ( "Content-Type" , "application/x-tar" )
if _ , err := io . Copy ( w , data ) ; err != nil {
return err
}
2013-07-16 19:07:41 -09:00
return nil
}
2014-09-15 22:56:47 +00:00
func postContainerExecCreate ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-09-09 05:51:53 +00:00
if err := parseForm ( r ) ; err != nil {
return nil
}
var (
2014-09-15 22:56:47 +00:00
name = vars [ "name" ]
job = eng . Job ( "execCreate" , name )
stdoutBuffer = bytes . NewBuffer ( nil )
2015-03-23 22:03:54 +01:00
outWarnings [ ] string
warnings = bytes . NewBuffer ( nil )
2014-09-15 22:56:47 +00:00
)
2014-09-25 08:24:41 -07:00
2014-09-15 22:56:47 +00:00
if err := job . DecodeEnv ( r . Body ) ; err != nil {
return err
}
job . Stdout . Add ( stdoutBuffer )
2015-03-23 22:03:54 +01:00
// Read warnings from stderr
job . Stderr . Add ( warnings )
2014-09-15 22:56:47 +00:00
// Register an instance of Exec in container.
if err := job . Run ( ) ; err != nil {
fmt . Fprintf ( os . Stderr , "Error setting up exec command in container %s: %s\n" , name , err )
return err
}
2015-03-23 22:03:54 +01:00
// Parse warnings from stderr
scanner := bufio . NewScanner ( warnings )
for scanner . Scan ( ) {
outWarnings = append ( outWarnings , scanner . Text ( ) )
}
2014-09-15 22:56:47 +00:00
2015-03-23 22:03:54 +01:00
return writeJSON ( w , http . StatusCreated , & types . ContainerExecCreateResponse {
ID : engine . Tail ( stdoutBuffer , 1 ) ,
Warnings : outWarnings ,
} )
2014-09-15 22:56:47 +00:00
}
2014-09-17 18:36:51 +00:00
// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
2014-09-15 22:56:47 +00:00
func postContainerExecStart ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := parseForm ( r ) ; err != nil {
return nil
}
var (
name = vars [ "name" ]
job = eng . Job ( "execStart" , name )
errOut io . Writer = os . Stderr
2014-09-09 05:51:53 +00:00
)
2014-09-25 08:24:41 -07:00
2014-09-09 05:51:53 +00:00
if err := job . DecodeEnv ( r . Body ) ; err != nil {
return err
}
if ! job . GetenvBool ( "Detach" ) {
// Setting up the streaming http interface.
inStream , outStream , err := hijackServer ( w )
if err != nil {
return err
}
2014-12-09 20:21:21 +01:00
defer closeStreams ( inStream , outStream )
2014-09-09 05:51:53 +00:00
var errStream io . Writer
2014-12-15 19:57:39 +01:00
if _ , ok := r . Header [ "Upgrade" ] ; ok {
fmt . Fprintf ( outStream , "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n" )
} else {
fmt . Fprintf ( outStream , "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n" )
}
2014-09-09 05:51:53 +00:00
if ! job . GetenvBool ( "Tty" ) && version . GreaterThanOrEqualTo ( "1.6" ) {
2014-09-17 18:04:56 +03:00
errStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stderr )
outStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stdout )
2014-09-09 05:51:53 +00:00
} else {
errStream = outStream
}
job . Stdin . Add ( inStream )
job . Stdout . Add ( outStream )
job . Stderr . Set ( errStream )
errOut = outStream
}
// Now run the user process in container.
2014-09-16 06:43:43 +00:00
job . SetCloseIO ( false )
2014-09-09 05:51:53 +00:00
if err := job . Run ( ) ; err != nil {
2014-09-15 22:56:47 +00:00
fmt . Fprintf ( errOut , "Error starting exec command in container %s: %s\n" , name , err )
2014-09-09 05:51:53 +00:00
return err
}
w . WriteHeader ( http . StatusNoContent )
2014-09-17 18:36:51 +00:00
2014-09-09 05:51:53 +00:00
return nil
}
2014-09-15 22:56:47 +00:00
func postContainerExecResize ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-09 23:51:22 +02:00
height , err := strconv . Atoi ( r . Form . Get ( "h" ) )
if err != nil {
return nil
}
width , err := strconv . Atoi ( r . Form . Get ( "w" ) )
if err != nil {
return nil
}
d := getDaemon ( eng )
if err := d . ContainerExecResize ( vars [ "name" ] , height , width ) ; err != nil {
2014-09-15 22:56:47 +00:00
return err
}
2015-04-09 23:51:22 +02:00
2014-09-15 22:56:47 +00:00
return nil
}
2014-02-21 23:15:28 +00:00
func optionsHandler ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-06-10 16:10:40 -09:00
w . WriteHeader ( http . StatusOK )
return nil
}
2015-02-09 15:15:07 +08:00
func writeCorsHeaders ( w http . ResponseWriter , r * http . Request , corsHeaders string ) {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "CORS header is enabled and set to: %s" , corsHeaders )
2015-02-09 15:15:07 +08:00
w . Header ( ) . Add ( "Access-Control-Allow-Origin" , corsHeaders )
2014-11-01 19:22:28 +03:00
w . Header ( ) . Add ( "Access-Control-Allow-Headers" , "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth" )
2013-06-09 17:17:35 -09:00
w . Header ( ) . Add ( "Access-Control-Allow-Methods" , "GET, POST, DELETE, PUT, OPTIONS" )
2013-06-03 21:39:00 -04:00
}
2014-05-02 21:43:51 +00:00
func ping ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-05-02 22:03:59 +00:00
_ , err := w . Write ( [ ] byte { 'O' , 'K' } )
return err
2014-05-02 21:43:51 +00:00
}
2015-02-09 15:15:07 +08:00
func makeHttpHandler ( eng * engine . Engine , logging bool , localMethod string , localRoute string , handlerFunc HttpApiFunc , corsHeaders string , dockerVersion version . Version ) http . HandlerFunc {
2013-07-13 16:59:07 +02:00
return func ( w http . ResponseWriter , r * http . Request ) {
// log the request
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Calling %s %s" , localMethod , localRoute )
2013-07-13 16:59:07 +02:00
if logging {
2015-03-26 23:22:04 +01:00
logrus . Infof ( "%s %s" , r . Method , r . RequestURI )
2013-07-13 16:59:07 +02:00
}
if strings . Contains ( r . Header . Get ( "User-Agent" ) , "Docker-Client/" ) {
userAgent := strings . Split ( r . Header . Get ( "User-Agent" ) , "/" )
2014-04-01 15:46:52 -07:00
if len ( userAgent ) == 2 && ! dockerVersion . Equal ( version . Version ( userAgent [ 1 ] ) ) {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Warning: client and server don't have the same version (client: %s, server: %s)" , userAgent [ 1 ] , dockerVersion )
2013-07-13 16:59:07 +02:00
}
}
2014-02-21 23:15:28 +00:00
version := version . Version ( mux . Vars ( r ) [ "version" ] )
2014-02-14 22:53:53 +00:00
if version == "" {
2014-03-28 22:59:29 +00:00
version = api . APIVERSION
2013-07-13 16:59:07 +02:00
}
2015-02-09 15:15:07 +08:00
if corsHeaders != "" {
writeCorsHeaders ( w , r , corsHeaders )
2013-07-13 16:59:07 +02:00
}
2014-03-28 22:59:29 +00:00
if version . GreaterThan ( api . APIVERSION ) {
2015-03-26 19:18:23 -04:00
http . Error ( w , fmt . Errorf ( "client and server don't have same version (client API version: %s, server API version: %s)" , version , api . APIVERSION ) . Error ( ) , http . StatusNotFound )
2013-07-13 16:59:07 +02:00
return
}
2014-01-29 19:26:54 +00:00
if err := handlerFunc ( eng , version , w , r , mux . Vars ( r ) ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Errorf ( "Handler for %s %s returned error: %s" , localMethod , localRoute , err )
2013-07-13 16:59:07 +02:00
httpError ( w , err )
}
}
}
2015-02-09 15:15:07 +08:00
// we keep enableCors just for legacy usage, need to be removed in the future
func createRouter ( eng * engine . Engine , logging , enableCors bool , corsHeaders string , dockerVersion string ) * mux . Router {
2013-05-07 16:33:12 -07:00
r := mux . NewRouter ( )
2013-11-19 14:25:17 -05:00
if os . Getenv ( "DEBUG" ) != "" {
2015-04-01 11:56:30 +03:00
ProfilerSetup ( r , "/debug/" )
2013-11-19 14:25:17 -05:00
}
2013-07-13 19:19:38 +02:00
m := map [ string ] map [ string ] HttpApiFunc {
2013-05-07 16:33:12 -07:00
"GET" : {
2014-05-02 21:43:51 +00:00
"/_ping" : ping ,
2013-08-07 23:30:28 +02:00
"/events" : getEvents ,
2013-07-13 16:59:07 +02:00
"/info" : getInfo ,
2013-08-07 23:30:28 +02:00
"/version" : getVersion ,
2013-07-13 16:59:07 +02:00
"/images/json" : getImagesJSON ,
2013-10-08 13:52:36 +00:00
"/images/viz" : getImagesViz ,
2013-07-13 16:59:07 +02:00
"/images/search" : getImagesSearch ,
2014-05-01 00:26:24 -04:00
"/images/get" : getImagesGet ,
2013-09-02 09:06:17 -07:00
"/images/{name:.*}/get" : getImagesGet ,
2013-07-13 16:59:07 +02:00
"/images/{name:.*}/history" : getImagesHistory ,
"/images/{name:.*}/json" : getImagesByName ,
"/containers/ps" : getContainersJSON ,
"/containers/json" : getContainersJSON ,
"/containers/{name:.*}/export" : getContainersExport ,
"/containers/{name:.*}/changes" : getContainersChanges ,
"/containers/{name:.*}/json" : getContainersByName ,
2013-08-07 23:30:28 +02:00
"/containers/{name:.*}/top" : getContainersTop ,
2014-04-02 23:26:06 +04:00
"/containers/{name:.*}/logs" : getContainersLogs ,
2015-01-07 14:43:04 -08:00
"/containers/{name:.*}/stats" : getContainersStats ,
2013-07-13 16:59:07 +02:00
"/containers/{name:.*}/attach/ws" : wsContainersAttach ,
2014-11-17 15:50:09 -08:00
"/exec/{id:.*}/json" : getExecByID ,
2013-05-07 16:33:12 -07:00
} ,
"POST" : {
2013-05-09 17:50:56 -07:00
"/auth" : postAuth ,
2013-05-07 16:33:12 -07:00
"/commit" : postCommit ,
2013-05-22 20:07:26 -07:00
"/build" : postBuild ,
2013-05-09 17:50:56 -07:00
"/images/create" : postImagesCreate ,
2013-09-02 09:06:17 -07:00
"/images/load" : postImagesLoad ,
2013-05-09 22:28:52 -07:00
"/images/{name:.*}/push" : postImagesPush ,
2013-05-09 17:50:56 -07:00
"/images/{name:.*}/tag" : postImagesTag ,
"/containers/create" : postContainersCreate ,
"/containers/{name:.*}/kill" : postContainersKill ,
2014-05-21 15:06:18 -06:00
"/containers/{name:.*}/pause" : postContainersPause ,
"/containers/{name:.*}/unpause" : postContainersUnpause ,
2013-05-07 16:33:12 -07:00
"/containers/{name:.*}/restart" : postContainersRestart ,
"/containers/{name:.*}/start" : postContainersStart ,
"/containers/{name:.*}/stop" : postContainersStop ,
"/containers/{name:.*}/wait" : postContainersWait ,
2013-05-23 19:33:28 -07:00
"/containers/{name:.*}/resize" : postContainersResize ,
2013-05-07 16:33:12 -07:00
"/containers/{name:.*}/attach" : postContainersAttach ,
2013-07-16 19:07:41 -09:00
"/containers/{name:.*}/copy" : postContainersCopy ,
2014-09-15 22:56:47 +00:00
"/containers/{name:.*}/exec" : postContainerExecCreate ,
"/exec/{name:.*}/start" : postContainerExecStart ,
"/exec/{name:.*}/resize" : postContainerExecResize ,
2014-10-05 02:47:54 +00:00
"/containers/{name:.*}/rename" : postContainerRename ,
2013-05-07 16:33:12 -07:00
} ,
"DELETE" : {
"/containers/{name:.*}" : deleteContainers ,
"/images/{name:.*}" : deleteImages ,
} ,
2013-06-10 16:10:40 -09:00
"OPTIONS" : {
"" : optionsHandler ,
} ,
2013-05-07 16:33:12 -07:00
}
2015-02-09 15:15:07 +08:00
// If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
// otherwise, all head values will be passed to HTTP handler
if corsHeaders == "" && enableCors {
corsHeaders = "*"
}
2013-05-07 16:33:12 -07:00
for method , routes := range m {
for route , fct := range routes {
2015-03-26 23:22:04 +01:00
logrus . Debugf ( "Registering %s, %s" , method , route )
2013-05-07 16:33:12 -07:00
// NOTE: scope issue, make sure the variables are local and won't be changed
localRoute := route
localFct := fct
2013-07-13 16:59:07 +02:00
localMethod := method
2013-06-20 18:18:36 -07:00
2013-07-13 16:59:07 +02:00
// build the handler function
2015-02-09 15:15:07 +08:00
f := makeHttpHandler ( eng , logging , localMethod , localRoute , localFct , corsHeaders , version . Version ( dockerVersion ) )
2013-06-10 16:10:40 -09:00
2013-07-13 16:59:07 +02:00
// add the new route
2013-06-10 16:10:40 -09:00
if localRoute == "" {
r . Methods ( localMethod ) . HandlerFunc ( f )
} else {
r . Path ( "/v{version:[0-9.]+}" + localRoute ) . Methods ( localMethod ) . HandlerFunc ( f )
r . Path ( localRoute ) . Methods ( localMethod ) . HandlerFunc ( f )
}
2013-04-29 17:46:41 +02:00
}
2013-05-07 16:33:12 -07:00
}
2013-07-05 19:55:15 +02:00
2015-01-13 22:41:34 +01:00
return r
2013-06-10 13:02:40 -09:00
}
2013-11-14 06:08:08 +00:00
// ServeRequest processes a single http request to the docker remote api.
// FIXME: refactor this to be part of Server and not require re-creating a new
// router each time. This requires first moving ListenAndServe into Server.
2015-01-13 22:41:34 +01:00
func ServeRequest ( eng * engine . Engine , apiversion version . Version , w http . ResponseWriter , req * http . Request ) {
2015-02-09 15:15:07 +08:00
router := createRouter ( eng , false , true , "" , "" )
2013-11-14 06:08:08 +00:00
// Insert APIVERSION into the request as a convenience
2014-02-18 01:44:53 +00:00
req . URL . Path = fmt . Sprintf ( "/v%s%s" , apiversion , req . URL . Path )
2013-11-14 06:08:08 +00:00
router . ServeHTTP ( w , req )
}
2014-11-14 20:38:02 -08:00
func allocateDaemonPort ( addr string ) error {
host , port , err := net . SplitHostPort ( addr )
if err != nil {
return err
}
intPort , err := strconv . Atoi ( port )
if err != nil {
return err
}
var hostIPs [ ] net . IP
if parsedIP := net . ParseIP ( host ) ; parsedIP != nil {
hostIPs = append ( hostIPs , parsedIP )
} else if hostIPs , err = net . LookupIP ( host ) ; err != nil {
return fmt . Errorf ( "failed to lookup %s address in host specification" , host )
}
for _ , hostIP := range hostIPs {
2015-03-30 18:06:16 -07:00
if _ , err := bridge . RequestPort ( hostIP , "tcp" , intPort ) ; err != nil {
2014-11-14 20:38:02 -08:00
return fmt . Errorf ( "failed to allocate daemon listening port %d (err: %v)" , intPort , err )
}
}
return nil
}
2014-11-07 15:21:19 -05:00
type Server interface {
Serve ( ) error
Close ( ) error
2013-04-10 19:48:21 -07:00
}
2014-01-31 19:30:43 +00:00
// ServeApi loops through all of the protocols sent in to docker and spawns
// off a go routine to setup a serving http.Server for each.
2015-03-25 08:44:12 +01:00
func ServeApi ( job * engine . Job ) error {
2014-05-01 16:08:39 -07:00
if len ( job . Args ) == 0 {
2015-03-25 08:44:12 +01:00
return fmt . Errorf ( "usage: %s PROTO://ADDR [PROTO://ADDR ...]" , job . Name )
2014-05-01 16:08:39 -07:00
}
2014-02-15 20:24:55 -08:00
var (
protoAddrs = job . Args
chErrors = make ( chan error , len ( protoAddrs ) )
)
2014-01-31 19:30:43 +00:00
for _ , protoAddr := range protoAddrs {
protoAddrParts := strings . SplitN ( protoAddr , "://" , 2 )
2014-05-01 16:08:39 -07:00
if len ( protoAddrParts ) != 2 {
2015-03-25 08:44:12 +01:00
return fmt . Errorf ( "usage: %s PROTO://ADDR [PROTO://ADDR ...]" , job . Name )
2014-05-01 16:08:39 -07:00
}
2014-01-31 19:30:43 +00:00
go func ( ) {
2015-03-26 23:22:04 +01:00
logrus . Infof ( "Listening for HTTP on %s (%s)" , protoAddrParts [ 0 ] , protoAddrParts [ 1 ] )
2014-11-07 15:21:19 -05:00
srv , err := NewServer ( protoAddrParts [ 0 ] , protoAddrParts [ 1 ] , job )
if err != nil {
chErrors <- err
return
}
2015-02-28 16:55:59 +01:00
job . Eng . OnShutdown ( func ( ) {
if err := srv . Close ( ) ; err != nil {
2015-03-26 23:22:04 +01:00
logrus . Error ( err )
2015-02-28 16:55:59 +01:00
}
} )
2015-03-18 17:26:14 -04:00
if err = srv . Serve ( ) ; err != nil && strings . Contains ( err . Error ( ) , "use of closed network connection" ) {
err = nil
}
chErrors <- err
2014-01-31 19:30:43 +00:00
} ( )
}
2014-08-30 21:43:48 +04:00
for i := 0 ; i < len ( protoAddrs ) ; i ++ {
2014-01-31 19:30:43 +00:00
err := <- chErrors
if err != nil {
2015-03-25 08:44:12 +01:00
return err
2014-01-31 19:30:43 +00:00
}
}
2015-03-25 08:44:12 +01:00
return nil
2014-02-15 20:24:55 -08:00
}
2015-04-07 18:57:54 -07:00
func toBool ( s string ) bool {
s = strings . ToLower ( strings . TrimSpace ( s ) )
return ! ( s == "" || s == "0" || s == "no" || s == "false" || s == "none" )
}