2014-03-28 18:59:29 -04:00
package server
2013-04-10 22:48:21 -04:00
import (
2013-11-20 02:37:03 -05:00
"bufio"
"bytes"
2015-04-16 15:48:04 -04:00
"runtime"
2015-04-03 18:17:49 -04:00
"time"
2014-11-07 15:21:19 -05:00
2013-08-30 16:49:37 -04:00
"encoding/base64"
2013-04-17 21:13:43 -04:00
"encoding/json"
2013-04-19 09:24:37 -04:00
"fmt"
2013-05-07 21:06:49 -04:00
"io"
2013-06-18 14:59:56 -04:00
"net"
2013-04-10 22:48:21 -04:00
"net/http"
2013-06-18 14:59:56 -04:00
"os"
2013-04-22 12:17:47 -04:00
"strconv"
2013-04-19 09:24:37 -04:00
"strings"
2014-03-28 18:59:29 -04:00
2014-05-02 17:43:51 -04:00
"code.google.com/p/go.net/websocket"
2014-07-24 16:37:44 -04:00
"github.com/gorilla/mux"
2014-05-02 17:43:51 -04:00
2015-03-26 18:22:04 -04:00
"github.com/Sirupsen/logrus"
2014-07-24 18:19:50 -04:00
"github.com/docker/docker/api"
2015-02-24 14:12:47 -05:00
"github.com/docker/docker/api/types"
2015-04-16 15:48:04 -04:00
"github.com/docker/docker/autogen/dockerversion"
2015-04-16 14:11:26 -04:00
"github.com/docker/docker/builder"
2015-03-31 19:21:37 -04:00
"github.com/docker/docker/daemon"
2015-03-30 21:06:16 -04:00
"github.com/docker/docker/daemon/networkdriver/bridge"
2014-07-24 18:19:50 -04:00
"github.com/docker/docker/engine"
2015-04-07 21:57:54 -04:00
"github.com/docker/docker/graph"
2015-04-03 18:17:49 -04:00
"github.com/docker/docker/pkg/jsonmessage"
2014-07-28 20:23:38 -04:00
"github.com/docker/docker/pkg/parsers"
2015-04-03 18:17:49 -04:00
"github.com/docker/docker/pkg/parsers/filters"
2015-04-16 15:48:04 -04:00
"github.com/docker/docker/pkg/parsers/kernel"
2015-04-09 14:56:47 -04:00
"github.com/docker/docker/pkg/signal"
2014-09-17 11:04:56 -04:00
"github.com/docker/docker/pkg/stdcopy"
2015-03-17 22:18:41 -04:00
"github.com/docker/docker/pkg/streamformatter"
2014-07-24 18:19:50 -04:00
"github.com/docker/docker/pkg/version"
"github.com/docker/docker/registry"
2015-04-10 20:05:21 -04:00
"github.com/docker/docker/runconfig"
2014-07-24 18:19:50 -04:00
"github.com/docker/docker/utils"
2013-04-10 22:48:21 -04:00
)
2015-04-16 15:48:04 -04:00
type ServerConfig struct {
Logging bool
EnableCors bool
CorsHeaders string
Version string
SocketGroup string
Tls bool
TlsVerify bool
TlsCa string
TlsCert string
TlsKey string
}
2015-04-17 17:32:18 -04:00
type Server struct {
daemon * daemon . Daemon
cfg * ServerConfig
router * mux . Router
start chan struct { }
// TODO: delete engine
eng * engine . Engine
}
func New ( cfg * ServerConfig , eng * engine . Engine ) * Server {
2015-04-17 18:18:28 -04:00
srv := & Server {
cfg : cfg ,
start : make ( chan struct { } ) ,
eng : eng ,
2015-04-17 17:32:18 -04:00
}
2015-04-17 18:18:28 -04:00
r := createRouter ( srv , eng )
srv . router = r
return srv
2015-04-17 17:32:18 -04:00
}
func ( s * Server ) SetDaemon ( d * daemon . Daemon ) {
s . daemon = d
}
type serverCloser interface {
Serve ( ) error
Close ( ) error
}
// 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.
func ( s * Server ) ServeApi ( protoAddrs [ ] string ) error {
var chErrors = make ( chan error , len ( protoAddrs ) )
for _ , protoAddr := range protoAddrs {
protoAddrParts := strings . SplitN ( protoAddr , "://" , 2 )
if len ( protoAddrParts ) != 2 {
return fmt . Errorf ( "bad format, expected PROTO://ADDR" )
}
go func ( proto , addr string ) {
logrus . Infof ( "Listening for HTTP on %s (%s)" , proto , addr )
srv , err := s . newServer ( proto , addr )
if err != nil {
chErrors <- err
return
}
s . eng . OnShutdown ( func ( ) {
if err := srv . Close ( ) ; err != nil {
logrus . Error ( err )
}
} )
if err = srv . Serve ( ) ; err != nil && strings . Contains ( err . Error ( ) , "use of closed network connection" ) {
err = nil
}
chErrors <- err
} ( protoAddrParts [ 0 ] , protoAddrParts [ 1 ] )
}
for i := 0 ; i < len ( protoAddrs ) ; i ++ {
err := <- chErrors
if err != nil {
return err
}
}
return nil
}
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 18:15:28 -05:00
type HttpApiFunc func ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error
2013-07-13 10:59:07 -04:00
2013-05-07 21:06:49 -04:00
func hijackServer ( w http . ResponseWriter ) ( io . ReadCloser , io . Writer , error ) {
conn , _ , err := w . ( http . Hijacker ) . Hijack ( )
2013-05-06 05:31:22 -04:00
if err != nil {
return nil , nil , err
}
// Flush the options to make sure the client sets the raw mode
2013-05-07 21:06:49 -04:00
conn . Write ( [ ] byte { } )
return conn , conn , nil
2013-05-06 05:31:22 -04:00
}
2014-12-09 14:21:21 -05: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 11:24:41 -04: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 12:52:01 -04: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 15:15:29 -04:00
if r == nil {
return nil
}
2013-05-08 12:52:01 -04:00
if err := r . ParseForm ( ) ; err != nil && ! strings . HasPrefix ( err . Error ( ) , "mime:" ) {
return err
}
return nil
}
2013-05-22 23:07:26 -04: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 05:31:22 -04:00
func httpError ( w http . ResponseWriter , err error ) {
2015-04-20 05:49:51 -04:00
if err == nil || w == nil {
logrus . WithFields ( logrus . Fields { "error" : err , "writer" : w } ) . Error ( "unexpected HTTP error handling" )
return
}
2013-07-11 08:21:43 -04:00
statusCode := http . StatusInternalServerError
2013-11-14 18:53:43 -05: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 08:21:43 -04:00
statusCode = http . StatusNotFound
2014-10-23 13:34:06 -04:00
} else if strings . Contains ( errStr , "bad parameter" ) {
2013-07-11 08:21:43 -04:00
statusCode = http . StatusBadRequest
2014-10-23 13:34:06 -04:00
} else if strings . Contains ( errStr , "conflict" ) {
2013-07-11 08:21:43 -04:00
statusCode = http . StatusConflict
2014-10-23 13:34:06 -04:00
} else if strings . Contains ( errStr , "impossible" ) {
2013-07-11 08:21:43 -04:00
statusCode = http . StatusNotAcceptable
2014-10-23 13:34:06 -04:00
} else if strings . Contains ( errStr , "wrong login/password" ) {
2013-07-11 08:21:43 -04:00
statusCode = http . StatusUnauthorized
2014-10-23 13:34:06 -04:00
} else if strings . Contains ( errStr , "hasn't been activated" ) {
2013-07-11 08:21:43 -04:00
statusCode = http . StatusForbidden
2013-10-16 20:08:14 -04:00
}
2015-04-20 05:49:51 -04:00
logrus . WithFields ( logrus . Fields { "statusCode" : statusCode , "err" : err } ) . Error ( "HTTP Error" )
http . Error ( w , err . Error ( ) , statusCode )
2013-05-06 05:31:22 -04:00
}
2015-02-24 14:12:47 -05: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 )
}
2015-04-16 14:11:26 -04:00
func streamJSON ( out * engine . Output , w http . ResponseWriter , flush bool ) {
2014-02-13 14:21:27 -05:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
if flush {
2015-04-16 14:11:26 -04:00
out . Add ( utils . NewWriteFlusher ( w ) )
2014-02-13 14:21:27 -05:00
} else {
2015-04-16 14:11:26 -04:00
out . Add ( w )
2014-02-13 14:21:27 -05:00
}
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) postAuth ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-03-31 19:21:37 -04:00
var config * registry . AuthConfig
err := json . NewDecoder ( r . Body ) . Decode ( & config )
r . Body . Close ( )
2013-05-07 19:33:12 -04:00
if err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2015-04-17 18:18:28 -04:00
status , err := s . daemon . RegistryService . Auth ( config )
2015-03-31 19:21:37 -04:00
if err != nil {
2013-07-23 11:04:31 -04:00
return err
2013-06-03 08:09:16 -04:00
}
2015-03-31 19:21:37 -04:00
return writeJSON ( w , http . StatusOK , & types . AuthResponse {
Status : status ,
} )
2013-05-07 19:33:12 -04:00
}
2013-05-06 07:34:31 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) getVersion ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-08 03:14:00 -05:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2015-04-16 15:48:04 -04:00
v := & types . Version {
Version : dockerversion . VERSION ,
ApiVersion : api . APIVERSION ,
GitCommit : dockerversion . GITCOMMIT ,
GoVersion : runtime . Version ( ) ,
Os : runtime . GOOS ,
Arch : runtime . GOARCH ,
}
if kernelVersion , err := kernel . GetKernelVersion ( ) ; err == nil {
v . KernelVersion = kernelVersion . String ( )
}
return writeJSON ( w , http . StatusOK , v )
2013-05-07 19:33:12 -04:00
}
2013-05-06 07:34:31 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersKill ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2015-04-09 14:56:47 -04:00
err := parseForm ( r )
if err != nil {
2013-09-12 02:50:26 -04:00
return err
}
2015-04-09 14:56:47 -04:00
var sig uint64
name := vars [ "name" ]
// If we have a signal, look at it. Otherwise, do nothing
if sigStr := vars [ "signal" ] ; sigStr != "" {
// Check if we passed the signal as a number:
// The largest legal signal is 31, so let's parse on 5 bits
sig , err = strconv . ParseUint ( sigStr , 10 , 5 )
if err != nil {
// The signal is not a number, treat it as a string (either like
// "KILL" or like "SIGKILL")
sig = uint64 ( signal . SignalMap [ strings . TrimPrefix ( sigStr , "SIG" ) ] )
}
if sig == 0 {
return fmt . Errorf ( "Invalid signal: %s" , sigStr )
}
2013-11-16 19:26:04 -05:00
}
2015-04-09 14:56:47 -04:00
2015-04-17 18:18:28 -04:00
if err = s . daemon . ContainerKill ( name , sig ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2015-04-09 14:56:47 -04:00
2013-05-09 15:42:29 -04:00
w . WriteHeader ( http . StatusNoContent )
2013-05-10 14:20:49 -04:00
return nil
2013-05-07 19:33:12 -04:00
}
2013-05-06 07:34:31 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersPause ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-05-21 17:06:18 -04:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-09 16:50:48 -04:00
name := vars [ "name" ]
2015-04-17 18:18:28 -04:00
cont , err := s . daemon . Get ( name )
2015-04-09 16:50:48 -04:00
if err != nil {
2014-05-21 17:06:18 -04:00
return err
}
2015-04-09 16:50:48 -04:00
if err := cont . Pause ( ) ; err != nil {
return fmt . Errorf ( "Cannot pause container %s: %s" , name , err )
}
cont . LogEvent ( "pause" )
2014-05-21 17:06:18 -04:00
w . WriteHeader ( http . StatusNoContent )
2015-04-09 16:50:48 -04:00
2014-05-21 17:06:18 -04:00
return nil
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersUnpause ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-05-21 17:06:18 -04:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-09 16:50:48 -04:00
name := vars [ "name" ]
2015-04-17 18:18:28 -04:00
cont , err := s . daemon . Get ( name )
2015-04-09 16:50:48 -04:00
if err != nil {
2014-05-21 17:06:18 -04:00
return err
}
2015-04-09 16:50:48 -04:00
if err := cont . Unpause ( ) ; err != nil {
return fmt . Errorf ( "Cannot unpause container %s: %s" , name , err )
}
cont . LogEvent ( "unpause" )
2014-05-21 17:06:18 -04:00
w . WriteHeader ( http . StatusNoContent )
2015-04-09 16:50:48 -04:00
2014-05-21 17:06:18 -04:00
return nil
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) getContainersExport ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2015-04-12 10:04:01 -04:00
2015-04-17 18:18:28 -04:00
return s . daemon . ContainerExport ( vars [ "name" ] , w )
2013-05-07 19:33:12 -04:00
}
2013-04-24 08:01:40 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) getImagesJSON ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 12:52:01 -04:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2014-01-13 17:55:31 -05:00
2015-04-07 21:57:54 -04:00
imagesConfig := graph . ImagesConfig {
Filters : r . Form . Get ( "filters" ) ,
// FIXME this parameter could just be a match filter
Filter : r . Form . Get ( "filter" ) ,
2015-04-16 17:26:33 -04:00
All : boolValue ( r , "all" ) ,
2015-04-07 21:57:54 -04:00
}
2014-01-13 17:55:31 -05:00
2015-04-17 18:18:28 -04:00
images , err := s . daemon . Repositories ( ) . Images ( & imagesConfig )
2015-04-07 21:57:54 -04:00
if err != nil {
2014-01-17 21:54:02 -05:00
return err
2014-01-13 17:55:31 -05:00
}
2015-04-07 21:57:54 -04:00
if version . GreaterThanOrEqualTo ( "1.7" ) {
return writeJSON ( w , http . StatusOK , images )
2013-05-07 19:33:12 -04:00
}
2014-01-13 17:55:31 -05:00
2015-04-07 21:57:54 -04: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 17:55:31 -05:00
}
2015-04-07 21:57:54 -04:00
legacyImages = append ( legacyImages , legacyImage )
2014-01-13 17:55:31 -05:00
}
}
2015-04-07 21:57:54 -04:00
return writeJSON ( w , http . StatusOK , legacyImages )
2013-05-09 17:52:12 -04:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) getImagesViz ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-02-21 18:15:28 -05:00
if version . GreaterThan ( "1.6" ) {
2013-10-08 09:52:36 -04:00
w . WriteHeader ( http . StatusNotFound )
return fmt . Errorf ( "This is now implemented in the client." )
}
2014-01-29 14:26:54 -05:00
eng . ServeHTTP ( w , r )
2013-10-08 09:52:36 -04:00
return nil
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) getInfo ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-08 03:14:00 -05:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2015-04-10 13:26:30 -04:00
2015-04-17 18:18:28 -04:00
info , err := s . daemon . SystemInfo ( )
2015-04-10 13:26:30 -04:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , info )
2013-05-07 19:33:12 -04:00
}
2013-04-18 12:56:22 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) getEvents ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-07-12 12:29:23 -04:00
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-03 18:17:49 -04: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 14:12:17 -05:00
2015-04-03 18:17:49 -04: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
}
2015-04-17 18:18:28 -04:00
d := s . daemon
2015-04-03 18:17:49 -04:00
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 08:55:05 -04:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) getImagesHistory ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2014-01-13 19:50:10 -05:00
2015-04-10 06:55:07 -04:00
name := vars [ "name" ]
2015-04-17 18:18:28 -04:00
history , err := s . daemon . Repositories ( ) . History ( name )
2015-04-10 06:55:07 -04:00
if err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2015-04-10 06:55:07 -04:00
return writeJSON ( w , http . StatusOK , history )
2013-05-07 19:33:12 -04:00
}
2013-04-19 09:24:37 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) getContainersChanges ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2014-01-14 19:51:59 -05:00
2015-04-08 05:14:16 -04:00
name := vars [ "name" ]
2015-04-17 18:18:28 -04:00
cont , err := s . daemon . Get ( name )
2015-04-08 05:14:16 -04:00
if err != nil {
return err
}
changes , err := cont . Changes ( )
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , changes )
2013-05-07 19:33:12 -04:00
}
2013-04-19 09:24:37 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) getContainersTop ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-02-21 18:15:28 -05:00
if version . LessThan ( "1.4" ) {
2013-07-19 06:34:55 -04:00
return fmt . Errorf ( "top was improved a lot since 1.3, Please upgrade your docker client." )
}
2015-04-09 18:13:01 -04:00
2013-06-28 11:51:58 -04:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-09 18:13:01 -04:00
2013-07-19 06:06:32 -04:00
if err := parseForm ( r ) ; err != nil {
return err
}
2014-01-16 17:58:20 -05:00
2015-04-17 18:18:28 -04:00
procList , err := s . daemon . ContainerTop ( vars [ "name" ] , r . Form . Get ( "ps_args" ) )
2015-04-09 18:13:01 -04:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , procList )
2013-06-28 11:51:58 -04:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) getContainersJSON ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-04-14 12:29:53 -04:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2014-01-16 17:00:18 -05:00
2015-04-07 15:34:30 -04:00
config := & daemon . ContainersConfig {
2015-04-16 17:26:33 -04:00
All : boolValue ( r , "all" ) ,
Size : boolValue ( r , "size" ) ,
2015-04-07 15:34:30 -04:00
Since : r . Form . Get ( "since" ) ,
Before : r . Form . Get ( "before" ) ,
Filters : r . Form . Get ( "filters" ) ,
2013-06-20 10:19:50 -04:00
}
2015-04-07 15:34:30 -04:00
if tmpLimit := r . Form . Get ( "limit" ) ; tmpLimit != "" {
2015-04-14 12:29:53 -04:00
limit , err := strconv . Atoi ( tmpLimit )
2015-04-07 15:34:30 -04:00
if err != nil {
2014-01-16 17:00:18 -05:00
return err
2013-09-04 17:41:44 -04:00
}
2015-04-14 12:29:53 -04:00
config . Limit = limit
2013-05-07 19:33:12 -04:00
}
2015-04-07 15:34:30 -04:00
2015-04-17 18:18:28 -04:00
containers , err := s . daemon . Containers ( config )
2015-04-07 15:34:30 -04:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , containers )
2013-05-07 19:33:12 -04:00
}
2013-05-06 05:31:22 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) getContainersStats ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-01-07 17:43:04 -05:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-13 02:33:53 -04:00
2015-04-17 18:18:28 -04:00
return s . daemon . ContainerStats ( vars [ "name" ] , utils . NewWriteFlusher ( w ) )
2015-01-07 17:43:04 -05:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) getContainersLogs ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-04-02 15:26:06 -04:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-06-18 15:11:04 -04:00
// Validate args here, because we can't return not StatusOK after job.Run() call
2015-04-16 17:26:33 -04:00
stdout , stderr := boolValue ( r , "stdout" ) , boolValue ( r , "stderr" )
2014-06-18 15:11:04 -04:00
if ! ( stdout || stderr ) {
return fmt . Errorf ( "Bad parameters: you must choose at least one stream" )
}
2014-04-02 15:26:06 -04:00
2015-04-11 17:49:14 -04:00
logsConfig := & daemon . ContainerLogsConfig {
2015-04-16 17:26:33 -04:00
Follow : boolValue ( r , "follow" ) ,
Timestamps : boolValue ( r , "timestamps" ) ,
2015-04-11 17:49:14 -04:00
Tail : r . Form . Get ( "tail" ) ,
UseStdout : stdout ,
UseStderr : stderr ,
OutStream : utils . NewWriteFlusher ( w ) ,
2014-04-02 15:26:06 -04:00
}
2015-04-17 18:18:28 -04:00
if err := s . daemon . ContainerLogs ( vars [ "name" ] , logsConfig ) ; err != nil {
2015-04-11 17:49:14 -04:00
fmt . Fprintf ( w , "Error running logs job: %s\n" , err )
2014-04-02 15:26:06 -04:00
}
2015-04-11 17:49:14 -04:00
2014-04-02 15:26:06 -04:00
return nil
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) postImagesTag ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 12:52:01 -04:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2013-04-19 09:24:37 -04:00
2015-04-13 22:46:29 -04:00
repo := r . Form . Get ( "repo" )
tag := r . Form . Get ( "tag" )
2015-04-16 17:26:33 -04:00
force := boolValue ( r , "force" )
2015-04-17 18:18:28 -04:00
if err := s . daemon . Repositories ( ) . Tag ( repo , tag , vars [ "name" ] , force ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
w . WriteHeader ( http . StatusCreated )
2013-05-10 14:20:49 -04:00
return nil
2013-05-07 19:33:12 -04:00
}
2013-04-24 10:06:03 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) postCommit ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 12:52:01 -04:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2014-09-25 11:24:41 -04:00
if err := checkForJson ( r ) ; err != nil {
return err
}
2015-04-10 16:41:43 -04:00
cont := r . Form . Get ( "container" )
2013-12-11 20:03:48 -05:00
2015-04-16 17:26:33 -04:00
pause := boolValue ( r , "pause" )
2014-06-08 02:37:31 -04:00
if r . FormValue ( "pause" ) == "" && version . GreaterThanOrEqualTo ( "1.13" ) {
2015-04-10 16:41:43 -04:00
pause = true
}
2015-04-16 17:26:33 -04:00
c , _ , err := runconfig . DecodeContainerConfig ( r . Body )
if err != nil && err != io . EOF { //Do not fail if body is empty.
return err
}
if c == nil {
c = & runconfig . Config { }
}
2015-04-10 16:41:43 -04:00
containerCommitConfig := & daemon . ContainerCommitConfig {
Pause : pause ,
Repo : r . Form . Get ( "repo" ) ,
Tag : r . Form . Get ( "tag" ) ,
Author : r . Form . Get ( "author" ) ,
Comment : r . Form . Get ( "comment" ) ,
Changes : r . Form [ "changes" ] ,
2015-04-16 17:26:33 -04:00
Config : c ,
2014-06-08 02:37:31 -04:00
}
2015-04-21 17:23:48 -04:00
imgID , err := builder . Commit ( s . daemon , cont , containerCommitConfig )
2015-04-10 16:41:43 -04:00
if err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2015-04-10 16:41:43 -04:00
2015-03-28 12:39:24 -04:00
return writeJSON ( w , http . StatusCreated , & types . ContainerCommitResponse {
2015-04-10 16:41:43 -04:00
ID : imgID ,
2015-03-28 12:39:24 -04:00
} )
2013-05-07 19:33:12 -04:00
}
2013-04-24 10:06:03 -04:00
2013-05-09 20:50:56 -04:00
// Creates an image from Pull or from Import
2015-04-17 18:18:28 -04:00
func ( s * Server ) postImagesCreate ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 12:52:01 -04:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2013-04-22 17:37:22 -04:00
2014-01-22 16:35:35 -05:00
var (
image = r . Form . Get ( "fromImage" )
2014-07-07 19:09:42 -04:00
repo = r . Form . Get ( "repo" )
2014-01-22 16:35:35 -05:00
tag = r . Form . Get ( "tag" )
)
2013-08-30 16:49:37 -04:00
authEncoded := r . Header . Get ( "X-Registry-Auth" )
2014-03-10 20:16:58 -04:00
authConfig := & registry . AuthConfig { }
2013-08-30 16:49:37 -04:00
if authEncoded != "" {
authJson := base64 . NewDecoder ( base64 . URLEncoding , strings . NewReader ( authEncoded ) )
if err := json . NewDecoder ( authJson ) . Decode ( authConfig ) ; err != nil {
2013-09-03 14:45:49 -04:00
// for a pull it is not an error if no auth was given
2013-08-30 15:46:19 -04:00
// to increase compatibility with the existing api it is defaulting to be empty
2014-03-10 20:16:58 -04:00
authConfig = & registry . AuthConfig { }
2013-09-03 14:45:49 -04:00
}
}
2015-04-15 07:43:15 -04:00
2013-05-07 19:33:12 -04:00
if image != "" { //pull
2014-07-07 19:09:42 -04:00
if tag == "" {
2014-07-28 20:23:38 -04:00
image , tag = parsers . ParseRepositoryTag ( image )
2014-07-07 19:09:42 -04:00
}
2013-08-22 15:15:31 -04:00
metaHeaders := map [ string ] [ ] string { }
for k , v := range r . Header {
if strings . HasPrefix ( k , "X-Meta-" ) {
metaHeaders [ k ] = v
}
}
2015-04-15 07:43:15 -04:00
imagePullConfig := & graph . ImagePullConfig {
Parallel : version . GreaterThan ( "1.3" ) ,
MetaHeaders : metaHeaders ,
AuthConfig : authConfig ,
OutStream : utils . NewWriteFlusher ( w ) ,
}
if version . GreaterThan ( "1.0" ) {
imagePullConfig . Json = true
w . Header ( ) . Set ( "Content-Type" , "application/json" )
} else {
imagePullConfig . Json = false
}
2015-04-21 17:23:48 -04:00
if err := s . daemon . Repositories ( ) . Pull ( image , tag , imagePullConfig ) ; err != nil {
2015-04-15 07:43:15 -04:00
return err
}
2013-05-07 19:33:12 -04:00
} else { //import
2014-07-07 19:09:42 -04:00
if tag == "" {
2014-07-28 20:23:38 -04:00
repo , tag = parsers . ParseRepositoryTag ( repo )
2014-07-07 19:09:42 -04:00
}
2014-01-22 16:35:35 -05:00
2015-04-15 07:43:15 -04:00
src := r . Form . Get ( "fromSrc" )
imageImportConfig := & graph . ImageImportConfig {
Changes : r . Form [ "changes" ] ,
InConfig : r . Body ,
OutStream : utils . NewWriteFlusher ( w ) ,
}
if version . GreaterThan ( "1.0" ) {
imageImportConfig . Json = true
w . Header ( ) . Set ( "Content-Type" , "application/json" )
} else {
imageImportConfig . Json = false
}
2015-04-21 17:23:48 -04:00
newConfig , err := builder . BuildFromConfig ( s . daemon , & runconfig . Config { } , imageImportConfig . Changes )
2015-04-16 17:26:33 -04:00
if err != nil {
2013-05-16 15:09:06 -04:00
return err
2013-05-07 11:19:41 -04:00
}
2015-04-16 17:26:33 -04:00
imageImportConfig . ContainerConfig = newConfig
2015-04-15 07:43:15 -04:00
2015-04-16 17:26:33 -04:00
if err := s . daemon . Repositories ( ) . Import ( src , repo , tag , imageImportConfig ) ; err != nil {
return err
}
2013-05-07 19:33:12 -04:00
}
2014-01-22 16:35:35 -05:00
2013-05-10 14:20:49 -04:00
return nil
2013-05-07 19:33:12 -04:00
}
2013-05-07 11:19:41 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) getImagesSearch ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 12:52:01 -04:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2014-01-20 23:06:19 -05:00
var (
2015-03-31 19:21:37 -04:00
config * registry . AuthConfig
2014-01-20 23:06:19 -05:00
authEncoded = r . Header . Get ( "X-Registry-Auth" )
2015-03-31 19:21:37 -04:00
headers = map [ string ] [ ] string { }
2014-01-20 23:06:19 -05:00
)
if authEncoded != "" {
authJson := base64 . NewDecoder ( base64 . URLEncoding , strings . NewReader ( authEncoded ) )
2015-03-31 19:21:37 -04:00
if err := json . NewDecoder ( authJson ) . Decode ( & config ) ; err != nil {
2014-01-20 23:06:19 -05: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 19:21:37 -04:00
config = & registry . AuthConfig { }
2014-01-20 23:06:19 -05:00
}
}
for k , v := range r . Header {
if strings . HasPrefix ( k , "X-Meta-" ) {
2015-03-31 19:21:37 -04:00
headers [ k ] = v
2014-01-20 23:06:19 -05:00
}
}
2015-04-17 18:18:28 -04:00
query , err := s . daemon . RegistryService . Search ( r . Form . Get ( "term" ) , config , headers )
2015-03-31 19:21:37 -04:00
if err != nil {
return err
}
return json . NewEncoder ( w ) . Encode ( query . Results )
2013-05-07 19:33:12 -04:00
}
2013-04-30 11:04:31 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) postImagesPush ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-22 20:33:29 -05:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2013-08-22 15:15:31 -04:00
metaHeaders := map [ string ] [ ] string { }
for k , v := range r . Header {
if strings . HasPrefix ( k , "X-Meta-" ) {
metaHeaders [ k ] = v
}
}
2013-05-15 15:21:37 -04:00
if err := parseForm ( r ) ; err != nil {
return err
}
2014-03-10 20:16:58 -04:00
authConfig := & registry . AuthConfig { }
2013-08-23 03:38:33 -04:00
2013-08-30 16:49:37 -04: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 20:16:58 -04:00
authConfig = & registry . AuthConfig { }
2013-08-23 03:38:33 -04:00
}
} else {
2013-08-30 15:46:19 -04:00
// the old format is supported for compatibility if there was no authConfig header
2013-08-23 03:38:33 -04:00
if err := json . NewDecoder ( r . Body ) . Decode ( authConfig ) ; err != nil {
2015-03-27 11:17:50 -04:00
return fmt . Errorf ( "Bad parameters and missing X-Registry-Auth: %v" , err )
2013-08-23 03:38:33 -04:00
}
}
2013-05-15 15:21:37 -04:00
2014-01-29 14:26:54 -05:00
job := eng . Job ( "push" , vars [ "name" ] )
2014-01-22 20:33:29 -05:00
job . SetenvJson ( "metaHeaders" , metaHeaders )
job . SetenvJson ( "authConfig" , authConfig )
2014-03-31 21:50:10 -04:00
job . Setenv ( "tag" , r . Form . Get ( "tag" ) )
2014-02-21 18:15:28 -05:00
if version . GreaterThan ( "1.0" ) {
2014-02-13 14:21:27 -05:00
job . SetenvBool ( "json" , true )
2015-04-16 14:11:26 -04:00
streamJSON ( job . Stdout , w , true )
2014-02-13 14:21:27 -05:00
} else {
job . Stdout . Add ( utils . NewWriteFlusher ( w ) )
}
2014-01-22 20:33:29 -05:00
if err := job . Run ( ) ; err != nil {
if ! job . Stdout . Used ( ) {
return err
2013-05-25 11:09:46 -04:00
}
2015-03-17 22:18:41 -04:00
sf := streamformatter . NewStreamFormatter ( version . GreaterThan ( "1.0" ) )
2014-01-22 20:33:29 -05:00
w . Write ( sf . FormatError ( err ) )
2013-05-15 15:21:37 -04:00
}
2013-05-10 14:20:49 -04:00
return nil
2013-05-07 19:33:12 -04:00
}
2013-05-07 13:23:50 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) getImagesGet ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-07 15:39:15 -05: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 18:15:28 -05:00
if version . GreaterThan ( "1.0" ) {
2013-11-13 20:05:37 -05: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 18:54:22 -05:00
job . Stdout . Add ( w )
2014-01-07 15:39:15 -05:00
return job . Run ( )
2013-09-02 12:06:17 -04:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) postImagesLoad ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-29 14:26:54 -05:00
job := eng . Job ( "load" )
2014-01-20 19:09:17 -05:00
job . Stdin . Add ( r . Body )
2015-04-01 10:44:40 -04:00
job . Stdout . Add ( w )
2014-01-20 19:09:17 -05:00
return job . Run ( )
2013-09-02 12:06:17 -04:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersCreate ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-10-28 19:58:59 -04:00
if err := parseForm ( r ) ; err != nil {
return nil
}
2015-02-24 14:12:47 -05:00
if err := checkForJson ( r ) ; err != nil {
return err
}
2014-01-25 02:15:40 -05:00
var (
2015-04-09 17:49:22 -04:00
warnings [ ] string
name = r . Form . Get ( "name" )
2014-01-25 02:15:40 -05:00
)
2014-09-25 11:24:41 -04:00
2015-04-10 20:05:21 -04:00
config , hostConfig , err := runconfig . DecodeContainerConfig ( r . Body )
if err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2015-04-09 17:49:22 -04:00
2015-04-17 18:18:28 -04:00
containerId , warnings , err := s . daemon . ContainerCreate ( name , config , hostConfig )
2015-04-09 17:49:22 -04:00
if err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2015-04-09 17:49:22 -04:00
2015-02-24 14:12:47 -05:00
return writeJSON ( w , http . StatusCreated , & types . ContainerCreateResponse {
2015-04-09 17:49:22 -04:00
ID : containerId ,
Warnings : warnings ,
2015-02-24 14:12:47 -05:00
} )
2013-05-07 19:33:12 -04:00
}
2013-05-07 13:23:50 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersRestart ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 12:52:01 -04:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2015-04-13 19:34:34 -04:00
2015-04-17 18:18:28 -04:00
timeout , err := strconv . Atoi ( r . Form . Get ( "t" ) )
2015-04-13 19:34:34 -04:00
if err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2015-04-13 19:34:34 -04:00
2015-04-17 18:18:28 -04:00
if err := s . daemon . ContainerRestart ( vars [ "name" ] , timeout ) ; err != nil {
2015-04-13 19:34:34 -04:00
return err
}
2013-05-09 15:42:29 -04:00
w . WriteHeader ( http . StatusNoContent )
2015-04-13 19:34:34 -04:00
2013-05-10 14:20:49 -04:00
return nil
2013-05-07 19:33:12 -04:00
}
2013-05-06 07:34:31 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainerRename ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-10-04 22:47:54 -04:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-09 13:52:55 -04:00
name := vars [ "name" ]
newName := r . Form . Get ( "name" )
2015-04-17 18:18:28 -04:00
if err := s . daemon . ContainerRename ( name , newName ) ; err != nil {
2014-10-04 22:47:54 -04:00
return err
}
w . WriteHeader ( http . StatusNoContent )
return nil
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) deleteContainers ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 12:52:01 -04:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2014-07-06 16:43:24 -04:00
2015-04-08 17:38:53 -04:00
name := vars [ "name" ]
config := & daemon . ContainerRmConfig {
2015-04-16 17:26:33 -04:00
ForceRemove : boolValue ( r , "force" ) ,
RemoveVolume : boolValue ( r , "v" ) ,
RemoveLink : boolValue ( r , "link" ) ,
2015-04-08 17:38:53 -04:00
}
2015-04-17 18:18:28 -04:00
if err := s . daemon . ContainerRm ( name , config ) ; err != nil {
2015-04-10 12:53:40 -04:00
// Force a 404 for the empty string
if strings . Contains ( strings . ToLower ( err . Error ( ) ) , "prefix can't be empty" ) {
return fmt . Errorf ( "no such id: \"\"" )
}
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2015-04-08 17:38:53 -04:00
2013-05-09 15:42:29 -04:00
w . WriteHeader ( http . StatusNoContent )
2015-04-08 17:38:53 -04:00
2013-05-10 14:20:49 -04:00
return nil
2013-05-07 19:33:12 -04:00
}
2013-04-22 17:37:22 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) deleteImages ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-20 14:31:45 -04:00
if err := parseForm ( r ) ; err != nil {
return err
}
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2014-01-16 21:40:33 -05:00
2015-04-09 07:59:50 -04:00
name := vars [ "name" ]
2015-04-16 17:26:33 -04:00
force := boolValue ( r , "force" )
noprune := boolValue ( r , "noprune" )
2015-04-09 07:59:50 -04:00
2015-04-17 18:18:28 -04:00
list , err := s . daemon . ImageDelete ( name , force , noprune )
2015-04-09 07:59:50 -04:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , list )
2013-05-07 19:33:12 -04:00
}
2013-04-19 09:24:37 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersStart ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-10-26 22:24:01 -04:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-06-24 14:31:56 -04:00
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
2015-04-11 13:31:34 -04:00
// https://golang.org/src/pkg/net/http/request.go#L139
2014-09-30 22:06:31 -04:00
// net/http otherwise seems to swallow any headers related to chunked encoding
// including r.TransferEncoding
2013-05-13 19:39:54 -04:00
// allow a nil body for backwards compatibility
2015-04-10 20:05:21 -04:00
var hostConfig * runconfig . HostConfig
2014-09-30 22:06:31 -04:00
if r . Body != nil && ( r . ContentLength > 0 || r . ContentLength == - 1 ) {
2014-09-25 11:24:41 -04:00
if err := checkForJson ( r ) ; err != nil {
return err
2014-08-01 18:26:30 -04:00
}
2015-04-10 20:05:21 -04:00
c , err := runconfig . DecodeHostConfig ( r . Body )
if err != nil {
2014-08-01 18:26:30 -04:00
return err
2013-05-13 19:39:54 -04:00
}
2015-04-10 20:05:21 -04:00
hostConfig = c
2013-05-13 19:39:54 -04:00
}
2014-08-01 18:59:19 -04:00
2015-04-17 18:18:28 -04:00
if err := s . daemon . ContainerStart ( vars [ "name" ] , hostConfig ) ; err != nil {
2014-06-24 14:31:56 -04:00
if err . Error ( ) == "Container already started" {
w . WriteHeader ( http . StatusNotModified )
return nil
}
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2013-05-09 15:42:29 -04:00
w . WriteHeader ( http . StatusNoContent )
2013-05-10 14:20:49 -04:00
return nil
2013-05-07 19:33:12 -04:00
}
2013-04-19 09:24:37 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersStop ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 12:52:01 -04:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2015-04-11 18:41:16 -04:00
seconds , err := strconv . Atoi ( r . Form . Get ( "t" ) )
if err != nil {
return err
}
2015-04-17 18:18:28 -04:00
if err := s . daemon . ContainerStop ( vars [ "name" ] , seconds ) ; err != nil {
2014-06-24 14:31:56 -04:00
if err . Error ( ) == "Container already stopped" {
w . WriteHeader ( http . StatusNotModified )
return nil
}
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2013-05-09 15:42:29 -04:00
w . WriteHeader ( http . StatusNoContent )
2015-04-11 18:41:16 -04:00
2013-05-10 14:20:49 -04:00
return nil
2013-05-07 19:33:12 -04:00
}
2013-04-19 09:24:37 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersWait ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2015-04-09 17:11:11 -04:00
name := vars [ "name" ]
2015-04-17 18:18:28 -04:00
cont , err := s . daemon . Get ( name )
2015-03-25 23:01:14 -04:00
if err != nil {
return err
}
2015-04-09 17:11:11 -04:00
status , _ := cont . WaitStop ( - 1 * time . Second )
2015-03-25 23:01:14 -04:00
return writeJSON ( w , http . StatusOK , & types . ContainerWaitResponse {
2015-04-09 17:11:11 -04:00
StatusCode : status ,
2015-03-25 23:01:14 -04:00
} )
2013-05-07 19:33:12 -04:00
}
2013-05-06 05:31:22 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersResize ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-23 22:33:28 -04:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-09 17:51:22 -04:00
height , err := strconv . Atoi ( r . Form . Get ( "h" ) )
if err != nil {
2015-04-13 02:36:04 -04:00
return err
2015-04-09 17:51:22 -04:00
}
width , err := strconv . Atoi ( r . Form . Get ( "w" ) )
if err != nil {
2015-04-13 02:36:04 -04:00
return err
2015-04-09 17:51:22 -04:00
}
2015-04-17 18:18:28 -04:00
cont , err := s . daemon . Get ( vars [ "name" ] )
2015-04-09 17:51:22 -04:00
if err != nil {
2013-05-23 22:33:28 -04:00
return err
}
2015-04-09 17:51:22 -04:00
2015-04-13 02:36:04 -04:00
return cont . Resize ( height , width )
2013-05-23 22:33:28 -04:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersAttach ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 12:52:01 -04:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2013-05-07 19:33:12 -04:00
2015-04-17 18:18:28 -04:00
cont , err := s . daemon . Get ( vars [ "name" ] )
2014-01-27 22:26:24 -05:00
if err != nil {
2014-01-15 20:43:57 -05:00
return err
}
2013-05-28 12:19:12 -04:00
2013-09-11 14:35:09 -04:00
inStream , outStream , err := hijackServer ( w )
2013-05-07 19:33:12 -04:00
if err != nil {
2013-05-10 14:20:49 -04:00
return err
2013-05-07 19:33:12 -04:00
}
2014-12-09 14:21:21 -05:00
defer closeStreams ( inStream , outStream )
2013-04-10 22:48:21 -04:00
2013-09-11 14:35:09 -04:00
var errStream io . Writer
2014-12-15 13:57:39 -05: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 14:35:09 -04:00
2015-04-06 15:19:38 -04:00
if ! cont . Config . Tty && version . GreaterThanOrEqualTo ( "1.6" ) {
2014-09-17 11:04:56 -04:00
errStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stderr )
outStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stdout )
2013-09-11 14:35:09 -04:00
} else {
errStream = outStream
}
2015-04-16 17:26:33 -04:00
logs := boolValue ( r , "logs" )
stream := boolValue ( r , "stream" )
2013-09-11 14:35:09 -04:00
2015-04-09 12:58:44 -04:00
var stdin io . ReadCloser
var stdout , stderr io . Writer
2015-04-16 17:26:33 -04:00
if boolValue ( r , "stdin" ) {
2015-04-09 12:58:44 -04:00
stdin = inStream
}
2015-04-16 17:26:33 -04:00
if boolValue ( r , "stdout" ) {
2015-04-09 12:58:44 -04:00
stdout = outStream
}
2015-04-16 17:26:33 -04:00
if boolValue ( r , "stderr" ) {
2015-04-09 12:58:44 -04:00
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 19:33:12 -04:00
}
2013-05-10 14:20:49 -04:00
return nil
2013-05-07 19:33:12 -04:00
}
2013-05-01 23:07:06 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) wsContainersAttach ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-07-05 13:55:15 -04:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-17 18:18:28 -04:00
cont , err := s . daemon . Get ( vars [ "name" ] )
2015-04-06 15:19:38 -04:00
if err != nil {
2013-07-05 13:55:15 -04:00
return err
}
2013-07-13 10:59:07 -04:00
h := websocket . Handler ( func ( ws * websocket . Conn ) {
defer ws . Close ( )
2015-04-06 15:19:38 -04: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 18:22:04 -04:00
logrus . Errorf ( "Error attaching websocket: %s" , err )
2013-07-13 10:59:07 -04:00
}
} )
h . ServeHTTP ( w , r )
2013-07-05 13:55:15 -04:00
return nil
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) getContainersByName ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2014-05-20 15:36:15 -04:00
var job = eng . Job ( "container_inspect" , vars [ "name" ] )
2014-05-30 21:13:37 -04:00
if version . LessThan ( "1.12" ) {
2014-06-16 20:06:21 -04:00
job . SetenvBool ( "raw" , true )
2014-05-30 21:13:37 -04:00
}
2015-04-16 14:11:26 -04:00
streamJSON ( job . Stdout , w , false )
2014-01-20 18:15:00 -05:00
return job . Run ( )
2013-05-07 19:33:12 -04:00
}
2013-05-01 23:07:06 -04:00
2015-04-17 18:18:28 -04:00
func ( s * Server ) getExecByID ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-11-17 18:50:09 -05:00
if vars == nil {
return fmt . Errorf ( "Missing parameter 'id'" )
}
2015-04-11 18:15:34 -04:00
2015-04-17 18:18:28 -04:00
eConfig , err := s . daemon . ContainerExecInspect ( vars [ "id" ] )
2015-04-11 18:15:34 -04:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , eConfig )
2014-11-17 18:50:09 -05:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) getImagesByName ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 19:28:47 -04:00
if vars == nil {
2013-05-10 14:20:49 -04:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 19:28:47 -04:00
}
2014-05-20 15:36:15 -04:00
var job = eng . Job ( "image_inspect" , vars [ "name" ] )
2014-05-30 21:13:37 -04:00
if version . LessThan ( "1.12" ) {
2014-06-16 20:06:21 -04:00
job . SetenvBool ( "raw" , true )
2014-05-30 21:13:37 -04:00
}
2015-04-16 14:11:26 -04:00
streamJSON ( job . Stdout , w , false )
2014-01-20 18:15:00 -05:00
return job . Run ( )
2013-05-19 13:46:24 -04:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) postBuild ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-02-21 18:15:28 -05:00
if version . LessThan ( "1.3" ) {
2013-06-19 17:59:28 -04:00
return fmt . Errorf ( "Multipart upload for build is no longer supported. Please upgrade your docker client." )
2013-05-22 23:07:26 -04:00
}
2013-12-09 19:25:19 -05:00
var (
2014-05-07 18:59:13 -04: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 20:16:58 -04:00
configFile = & registry . ConfigFile { }
2015-04-16 17:26:33 -04:00
buildConfig = builder . NewBuildConfig ( )
2013-12-09 19:25:19 -05: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 18:15:28 -05:00
if version . LessThan ( "1.9" ) && authEncoded != "" {
2013-12-06 17:27:10 -05: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 20:16:58 -04:00
authConfig = & registry . AuthConfig { }
2013-12-06 17:27:10 -05:00
}
}
2013-05-22 23:07:26 -04: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 20:16:58 -04:00
configFile = & registry . ConfigFile { }
2014-01-03 15:13:32 -05:00
}
}
2014-02-21 18:15:28 -05:00
if version . GreaterThanOrEqualTo ( "1.8" ) {
2015-04-16 17:26:33 -04:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
buildConfig . JSONFormat = true
2013-10-12 08:14:52 -04:00
}
2014-05-19 14:01:55 -04:00
2015-04-16 17:26:33 -04:00
if boolValue ( r , "forcerm" ) && version . GreaterThanOrEqualTo ( "1.12" ) {
buildConfig . Remove = true
2014-05-19 14:01:55 -04:00
} else if r . FormValue ( "rm" ) == "" && version . GreaterThanOrEqualTo ( "1.12" ) {
2015-04-16 17:26:33 -04:00
buildConfig . Remove = true
2014-05-16 07:47:33 -04:00
} else {
2015-04-16 17:26:33 -04:00
buildConfig . Remove = boolValue ( r , "rm" )
2014-05-16 07:47:33 -04:00
}
2015-04-16 17:26:33 -04:00
if boolValue ( r , "pull" ) && version . GreaterThanOrEqualTo ( "1.16" ) {
buildConfig . Pull = true
2014-11-21 12:51:32 -05:00
}
2015-04-16 14:11:26 -04:00
2015-04-20 17:18:14 -04:00
output := utils . NewWriteFlusher ( w )
buildConfig . Stdout = output
2015-04-16 17:26:33 -04:00
buildConfig . Context = r . Body
buildConfig . RemoteURL = r . FormValue ( "remote" )
buildConfig . DockerfileName = r . FormValue ( "dockerfile" )
buildConfig . RepoName = r . FormValue ( "t" )
buildConfig . SuppressOutput = boolValue ( r , "q" )
buildConfig . NoCache = boolValue ( r , "nocache" )
buildConfig . ForceRemove = boolValue ( r , "forcerm" )
buildConfig . AuthConfig = authConfig
buildConfig . ConfigFile = configFile
buildConfig . MemorySwap = int64Value ( r , "memswap" )
buildConfig . Memory = int64Value ( r , "memory" )
buildConfig . CpuShares = int64Value ( r , "cpushares" )
buildConfig . CpuSetCpus = r . FormValue ( "cpusetcpus" )
buildConfig . CpuSetMems = r . FormValue ( "cpusetmems" )
2014-01-23 15:19:52 -05:00
2015-03-10 18:10:00 -04: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-04-16 14:11:26 -04:00
logrus . Infof ( "Client disconnected, cancelling job: build" )
2015-04-16 17:26:33 -04:00
buildConfig . Cancel ( )
2015-03-10 18:10:00 -04:00
}
} ( )
}
2015-04-21 17:23:48 -04:00
if err := builder . Build ( s . daemon , buildConfig ) ; err != nil {
2015-04-20 17:18:14 -04:00
// Do not write the error in the http output if it's still empty.
// This prevents from writing a 200(OK) when there is an interal error.
if ! output . Flushed ( ) {
2014-01-23 15:19:52 -05:00
return err
2013-10-12 08:14:52 -04:00
}
2015-03-17 22:18:41 -04:00
sf := streamformatter . NewStreamFormatter ( version . GreaterThanOrEqualTo ( "1.8" ) )
2014-01-23 15:19:52 -05:00
w . Write ( sf . FormatError ( err ) )
2013-05-22 23:07:26 -04:00
}
return nil
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainersCopy ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-07-17 00:07:41 -04:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-09-25 11:24:41 -04:00
if err := checkForJson ( r ) ; err != nil {
return err
}
2015-04-09 16:05:31 -04:00
cfg := types . CopyConfig { }
if err := json . NewDecoder ( r . Body ) . Decode ( & cfg ) ; err != nil {
2014-09-25 11:24:41 -04:00
return err
2013-07-17 00:07:41 -04:00
}
2015-04-09 16:05:31 -04:00
if cfg . Resource == "" {
2013-11-16 05:15:04 -05:00
return fmt . Errorf ( "Path cannot be empty" )
2013-07-17 00:07:41 -04:00
}
2014-03-16 13:48:46 -04:00
2015-04-09 16:05:31 -04:00
res := cfg . Resource
2014-03-16 13:48:46 -04:00
2015-04-09 16:05:31 -04:00
if res [ 0 ] == '/' {
res = res [ 1 : ]
2013-07-17 00:07:41 -04:00
}
2015-04-17 18:18:28 -04:00
cont , err := s . daemon . Get ( vars [ "name" ] )
2015-04-09 16:05:31 -04:00
if err != nil {
2015-03-26 18:22:04 -04:00
logrus . Errorf ( "%v" , err )
2014-12-16 18:06:35 -05:00
if strings . Contains ( strings . ToLower ( err . Error ( ) ) , "no such id" ) {
2014-02-14 19:43:55 -05:00
w . WriteHeader ( http . StatusNotFound )
2015-04-09 16:05:31 -04: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-14 19:43:55 -05:00
}
2015-04-09 16:05:31 -04:00
return err
2013-07-17 00:07:41 -04:00
}
2015-04-09 16:05:31 -04:00
defer data . Close ( )
w . Header ( ) . Set ( "Content-Type" , "application/x-tar" )
if _ , err := io . Copy ( w , data ) ; err != nil {
return err
}
2013-07-17 00:07:41 -04:00
return nil
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainerExecCreate ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-09-09 01:51:53 -04:00
if err := parseForm ( r ) ; err != nil {
return nil
}
var (
2014-09-15 18:56:47 -04:00
name = vars [ "name" ]
job = eng . Job ( "execCreate" , name )
stdoutBuffer = bytes . NewBuffer ( nil )
2015-03-23 17:03:54 -04:00
outWarnings [ ] string
warnings = bytes . NewBuffer ( nil )
2014-09-15 18:56:47 -04:00
)
2014-09-25 11:24:41 -04:00
2014-09-15 18:56:47 -04:00
if err := job . DecodeEnv ( r . Body ) ; err != nil {
return err
}
job . Stdout . Add ( stdoutBuffer )
2015-03-23 17:03:54 -04:00
// Read warnings from stderr
job . Stderr . Add ( warnings )
2014-09-15 18:56:47 -04: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 17:03:54 -04:00
// Parse warnings from stderr
scanner := bufio . NewScanner ( warnings )
for scanner . Scan ( ) {
outWarnings = append ( outWarnings , scanner . Text ( ) )
}
2014-09-15 18:56:47 -04:00
2015-03-23 17:03:54 -04:00
return writeJSON ( w , http . StatusCreated , & types . ContainerExecCreateResponse {
ID : engine . Tail ( stdoutBuffer , 1 ) ,
Warnings : outWarnings ,
} )
2014-09-15 18:56:47 -04:00
}
2014-09-17 14:36:51 -04:00
// TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainerExecStart ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-09-15 18:56:47 -04:00
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 01:51:53 -04:00
)
2014-09-25 11:24:41 -04:00
2014-09-09 01:51:53 -04: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 14:21:21 -05:00
defer closeStreams ( inStream , outStream )
2014-09-09 01:51:53 -04:00
var errStream io . Writer
2014-12-15 13:57:39 -05: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 01:51:53 -04:00
if ! job . GetenvBool ( "Tty" ) && version . GreaterThanOrEqualTo ( "1.6" ) {
2014-09-17 11:04:56 -04:00
errStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stderr )
outStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stdout )
2014-09-09 01:51:53 -04: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 02:43:43 -04:00
job . SetCloseIO ( false )
2014-09-09 01:51:53 -04:00
if err := job . Run ( ) ; err != nil {
2014-09-15 18:56:47 -04:00
fmt . Fprintf ( errOut , "Error starting exec command in container %s: %s\n" , name , err )
2014-09-09 01:51:53 -04:00
return err
}
w . WriteHeader ( http . StatusNoContent )
2014-09-17 14:36:51 -04:00
2014-09-09 01:51:53 -04:00
return nil
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) postContainerExecResize ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-09-15 18:56:47 -04:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-09 17:51:22 -04:00
height , err := strconv . Atoi ( r . Form . Get ( "h" ) )
if err != nil {
2015-04-13 02:36:04 -04:00
return err
2015-04-09 17:51:22 -04:00
}
width , err := strconv . Atoi ( r . Form . Get ( "w" ) )
if err != nil {
2015-04-13 02:36:04 -04:00
return err
2015-04-09 17:51:22 -04:00
}
2015-04-17 18:18:28 -04:00
return s . daemon . ContainerExecResize ( vars [ "name" ] , height , width )
2014-09-15 18:56:47 -04:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) optionsHandler ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-06-10 21:10:40 -04:00
w . WriteHeader ( http . StatusOK )
return nil
}
2015-02-09 02:15:07 -05:00
func writeCorsHeaders ( w http . ResponseWriter , r * http . Request , corsHeaders string ) {
2015-03-26 18:22:04 -04:00
logrus . Debugf ( "CORS header is enabled and set to: %s" , corsHeaders )
2015-02-09 02:15:07 -05:00
w . Header ( ) . Add ( "Access-Control-Allow-Origin" , corsHeaders )
2014-11-01 12:22:28 -04:00
w . Header ( ) . Add ( "Access-Control-Allow-Headers" , "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth" )
2013-06-09 22:17:35 -04:00
w . Header ( ) . Add ( "Access-Control-Allow-Methods" , "GET, POST, DELETE, PUT, OPTIONS" )
2013-06-03 21:39:00 -04:00
}
2015-04-17 18:18:28 -04:00
func ( s * Server ) ping ( eng * engine . Engine , version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-05-02 18:03:59 -04:00
_ , err := w . Write ( [ ] byte { 'O' , 'K' } )
return err
2014-05-02 17:43:51 -04:00
}
2015-02-09 02:15:07 -05:00
func makeHttpHandler ( eng * engine . Engine , logging bool , localMethod string , localRoute string , handlerFunc HttpApiFunc , corsHeaders string , dockerVersion version . Version ) http . HandlerFunc {
2013-07-13 10:59:07 -04:00
return func ( w http . ResponseWriter , r * http . Request ) {
// log the request
2015-03-26 18:22:04 -04:00
logrus . Debugf ( "Calling %s %s" , localMethod , localRoute )
2013-07-13 10:59:07 -04:00
if logging {
2015-03-26 18:22:04 -04:00
logrus . Infof ( "%s %s" , r . Method , r . RequestURI )
2013-07-13 10:59:07 -04:00
}
if strings . Contains ( r . Header . Get ( "User-Agent" ) , "Docker-Client/" ) {
userAgent := strings . Split ( r . Header . Get ( "User-Agent" ) , "/" )
2014-04-01 18:46:52 -04:00
if len ( userAgent ) == 2 && ! dockerVersion . Equal ( version . Version ( userAgent [ 1 ] ) ) {
2015-03-26 18:22:04 -04:00
logrus . Debugf ( "Warning: client and server don't have the same version (client: %s, server: %s)" , userAgent [ 1 ] , dockerVersion )
2013-07-13 10:59:07 -04:00
}
}
2014-02-21 18:15:28 -05:00
version := version . Version ( mux . Vars ( r ) [ "version" ] )
2014-02-14 17:53:53 -05:00
if version == "" {
2014-03-28 18:59:29 -04:00
version = api . APIVERSION
2013-07-13 10:59:07 -04:00
}
2015-02-09 02:15:07 -05:00
if corsHeaders != "" {
writeCorsHeaders ( w , r , corsHeaders )
2013-07-13 10:59:07 -04:00
}
2014-03-28 18:59:29 -04: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 10:59:07 -04:00
return
}
2014-01-29 14:26:54 -05:00
if err := handlerFunc ( eng , version , w , r , mux . Vars ( r ) ) ; err != nil {
2015-03-26 18:22:04 -04:00
logrus . Errorf ( "Handler for %s %s returned error: %s" , localMethod , localRoute , err )
2013-07-13 10:59:07 -04:00
httpError ( w , err )
}
}
}
2015-02-09 02:15:07 -05:00
// we keep enableCors just for legacy usage, need to be removed in the future
2015-04-17 18:18:28 -04:00
func createRouter ( s * Server , eng * engine . Engine ) * mux . Router {
2013-05-07 19:33:12 -04:00
r := mux . NewRouter ( )
2013-11-19 14:25:17 -05:00
if os . Getenv ( "DEBUG" ) != "" {
2015-04-01 04:56:30 -04:00
ProfilerSetup ( r , "/debug/" )
2013-11-19 14:25:17 -05:00
}
2013-07-13 13:19:38 -04:00
m := map [ string ] map [ string ] HttpApiFunc {
2013-05-07 19:33:12 -04:00
"GET" : {
2015-04-17 18:18:28 -04:00
"/_ping" : s . ping ,
"/events" : s . getEvents ,
"/info" : s . getInfo ,
"/version" : s . getVersion ,
"/images/json" : s . getImagesJSON ,
"/images/viz" : s . getImagesViz ,
"/images/search" : s . getImagesSearch ,
"/images/get" : s . getImagesGet ,
"/images/{name:.*}/get" : s . getImagesGet ,
"/images/{name:.*}/history" : s . getImagesHistory ,
"/images/{name:.*}/json" : s . getImagesByName ,
"/containers/ps" : s . getContainersJSON ,
"/containers/json" : s . getContainersJSON ,
"/containers/{name:.*}/export" : s . getContainersExport ,
"/containers/{name:.*}/changes" : s . getContainersChanges ,
"/containers/{name:.*}/json" : s . getContainersByName ,
"/containers/{name:.*}/top" : s . getContainersTop ,
"/containers/{name:.*}/logs" : s . getContainersLogs ,
"/containers/{name:.*}/stats" : s . getContainersStats ,
"/containers/{name:.*}/attach/ws" : s . wsContainersAttach ,
"/exec/{id:.*}/json" : s . getExecByID ,
2013-05-07 19:33:12 -04:00
} ,
"POST" : {
2015-04-17 18:18:28 -04:00
"/auth" : s . postAuth ,
"/commit" : s . postCommit ,
"/build" : s . postBuild ,
"/images/create" : s . postImagesCreate ,
"/images/load" : s . postImagesLoad ,
"/images/{name:.*}/push" : s . postImagesPush ,
"/images/{name:.*}/tag" : s . postImagesTag ,
"/containers/create" : s . postContainersCreate ,
"/containers/{name:.*}/kill" : s . postContainersKill ,
"/containers/{name:.*}/pause" : s . postContainersPause ,
"/containers/{name:.*}/unpause" : s . postContainersUnpause ,
"/containers/{name:.*}/restart" : s . postContainersRestart ,
"/containers/{name:.*}/start" : s . postContainersStart ,
"/containers/{name:.*}/stop" : s . postContainersStop ,
"/containers/{name:.*}/wait" : s . postContainersWait ,
"/containers/{name:.*}/resize" : s . postContainersResize ,
"/containers/{name:.*}/attach" : s . postContainersAttach ,
"/containers/{name:.*}/copy" : s . postContainersCopy ,
"/containers/{name:.*}/exec" : s . postContainerExecCreate ,
"/exec/{name:.*}/start" : s . postContainerExecStart ,
"/exec/{name:.*}/resize" : s . postContainerExecResize ,
"/containers/{name:.*}/rename" : s . postContainerRename ,
2013-05-07 19:33:12 -04:00
} ,
"DELETE" : {
2015-04-17 18:18:28 -04:00
"/containers/{name:.*}" : s . deleteContainers ,
"/images/{name:.*}" : s . deleteImages ,
2013-05-07 19:33:12 -04:00
} ,
2013-06-10 21:10:40 -04:00
"OPTIONS" : {
2015-04-17 18:18:28 -04:00
"" : s . optionsHandler ,
2013-06-10 21:10:40 -04:00
} ,
2013-05-07 19:33:12 -04:00
}
2015-02-09 02:15:07 -05: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
2015-04-17 18:18:28 -04:00
corsHeaders := s . cfg . CorsHeaders
if corsHeaders == "" && s . cfg . EnableCors {
2015-02-09 02:15:07 -05:00
corsHeaders = "*"
}
2013-05-07 19:33:12 -04:00
for method , routes := range m {
for route , fct := range routes {
2015-03-26 18:22:04 -04:00
logrus . Debugf ( "Registering %s, %s" , method , route )
2013-05-07 19:33:12 -04:00
// NOTE: scope issue, make sure the variables are local and won't be changed
localRoute := route
localFct := fct
2013-07-13 10:59:07 -04:00
localMethod := method
2013-06-20 21:18:36 -04:00
2013-07-13 10:59:07 -04:00
// build the handler function
2015-04-17 18:18:28 -04:00
f := makeHttpHandler ( eng , s . cfg . Logging , localMethod , localRoute , localFct , corsHeaders , version . Version ( s . cfg . Version ) )
2013-06-10 21:10:40 -04:00
2013-07-13 10:59:07 -04:00
// add the new route
2013-06-10 21:10:40 -04: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 11:46:41 -04:00
}
2013-05-07 19:33:12 -04:00
}
2013-07-05 13:55:15 -04:00
2015-01-13 16:41:34 -05:00
return r
2013-06-10 18:02:40 -04:00
}
2013-11-14 01:08:08 -05: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 16:41:34 -05:00
func ServeRequest ( eng * engine . Engine , apiversion version . Version , w http . ResponseWriter , req * http . Request ) {
2015-04-17 18:18:28 -04:00
cfg := & ServerConfig {
EnableCors : true ,
Version : string ( apiversion ) ,
}
api := New ( cfg , eng )
daemon , _ := eng . HackGetGlobalVar ( "httpapi.daemon" ) . ( * daemon . Daemon )
api . AcceptConnections ( daemon )
router := createRouter ( api , eng )
2013-11-14 01:08:08 -05:00
// Insert APIVERSION into the request as a convenience
2014-02-17 20:44:53 -05:00
req . URL . Path = fmt . Sprintf ( "/v%s%s" , apiversion , req . URL . Path )
2013-11-14 01:08:08 -05:00
router . ServeHTTP ( w , req )
}
2014-11-14 23:38:02 -05: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 21:06:16 -04:00
if _ , err := bridge . RequestPort ( hostIP , "tcp" , intPort ) ; err != nil {
2014-11-14 23:38:02 -05:00
return fmt . Errorf ( "failed to allocate daemon listening port %d (err: %v)" , intPort , err )
}
}
return nil
}