2014-03-28 22:59:29 +00:00
package server
2013-04-10 19:48:21 -07:00
import (
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"
2015-04-27 23:11:29 +02:00
"runtime"
2013-04-22 18:17:47 +02:00
"strconv"
2013-04-19 15:24:37 +02:00
"strings"
2015-04-27 23:11:29 +02:00
"time"
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-04-16 21:48:04 +02:00
"github.com/docker/docker/autogen/dockerversion"
2015-04-16 11:11:26 -07:00
"github.com/docker/docker/builder"
2015-04-22 05:06:58 -07:00
"github.com/docker/docker/cliconfig"
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"
2015-04-07 18:57:54 -07:00
"github.com/docker/docker/graph"
2015-05-08 12:33:33 -06:00
"github.com/docker/docker/pkg/ioutils"
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"
2015-04-16 21:48:04 +02:00
"github.com/docker/docker/pkg/parsers/kernel"
2015-04-09 11:56:47 -07:00
"github.com/docker/docker/pkg/signal"
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"
2015-04-10 17:05:21 -07:00
"github.com/docker/docker/runconfig"
2013-04-10 19:48:21 -07:00
)
2015-04-16 21:48:04 +02: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 14:32:18 -07:00
type Server struct {
2015-04-27 23:11:29 +02:00
daemon * daemon . Daemon
cfg * ServerConfig
router * mux . Router
start chan struct { }
servers [ ] serverCloser
2015-04-17 14:32:18 -07:00
}
2015-04-27 23:11:29 +02:00
func New ( cfg * ServerConfig ) * Server {
2015-04-17 15:18:28 -07:00
srv := & Server {
cfg : cfg ,
start : make ( chan struct { } ) ,
2015-04-17 14:32:18 -07:00
}
2015-04-27 23:11:29 +02:00
r := createRouter ( srv )
2015-04-17 15:18:28 -07:00
srv . router = r
return srv
2015-04-17 14:32:18 -07:00
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) Close ( ) {
for _ , srv := range s . servers {
if err := srv . Close ( ) ; err != nil {
logrus . Error ( err )
}
}
}
2015-04-17 14:32:18 -07:00
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" )
}
2015-04-27 23:11:29 +02:00
srv , err := s . newServer ( protoAddrParts [ 0 ] , protoAddrParts [ 1 ] )
if err != nil {
return err
}
s . servers = append ( s . servers , srv )
2015-04-17 14:32:18 -07:00
go func ( proto , addr string ) {
logrus . Infof ( "Listening for HTTP on %s (%s)" , proto , addr )
2015-04-27 23:11:29 +02:00
if err := srv . Serve ( ) ; err != nil && strings . Contains ( err . Error ( ) , "use of closed network connection" ) {
2015-04-17 14:32:18 -07:00
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 ( )
}
2015-04-27 23:11:29 +02:00
type HttpApiFunc func ( 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 ) {
2015-04-20 17:49:51 +08:00
if err == nil || w == nil {
logrus . WithFields ( logrus . Fields { "error" : err , "writer" : w } ) . Error ( "unexpected HTTP error handling" )
return
}
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 ( ) )
2015-04-23 19:37:47 +08:00
for keyword , status := range map [ string ] int {
"not found" : http . StatusNotFound ,
"no such" : http . StatusNotFound ,
"bad parameter" : http . StatusBadRequest ,
"conflict" : http . StatusConflict ,
"impossible" : http . StatusNotAcceptable ,
"wrong login/password" : http . StatusUnauthorized ,
"hasn't been activated" : http . StatusForbidden ,
} {
if strings . Contains ( errStr , keyword ) {
statusCode = status
break
}
2013-10-16 17:08:14 -07:00
}
2015-04-20 17:49:51 +08:00
logrus . WithFields ( logrus . Fields { "statusCode" : statusCode , "err" : err } ) . Error ( "HTTP Error" )
http . Error ( w , err . Error ( ) , statusCode )
2013-05-06 11:31:22 +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 )
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) postAuth ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-04-22 05:06:58 -07:00
var config * cliconfig . AuthConfig
2015-03-31 16:21:37 -07:00
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-04-17 15:18:28 -07:00
status , err := s . daemon . RegistryService . Auth ( config )
2015-03-31 16:21:37 -07:00
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
2015-04-27 23:11:29 +02:00
func ( s * Server ) getVersion ( 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" )
2015-04-16 21:48:04 +02: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 16:33:12 -07:00
}
2013-05-06 13:34:31 +02:00
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersKill ( 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
}
2015-04-26 18:50:25 +02:00
if err := parseForm ( r ) ; err != nil {
2013-09-11 23:50:26 -07:00
return err
}
2015-04-09 11:56:47 -07: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
2015-04-26 18:50:25 +02:00
sig , err := strconv . ParseUint ( sigStr , 10 , 5 )
2015-04-09 11:56:47 -07:00
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-17 00:26:04 +00:00
}
2015-04-09 11:56:47 -07:00
2015-04-26 18:50:25 +02:00
if err := s . daemon . ContainerKill ( name , sig ) ; err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2015-04-09 11:56:47 -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
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersPause ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-05-21 15:06:18 -06:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-09 22:50:48 +02:00
2015-05-02 03:03:35 +02:00
if err := s . daemon . ContainerPause ( vars [ "name" ] ) ; err != nil {
2014-05-21 15:06:18 -06:00
return err
}
2015-04-09 22:50:48 +02:00
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
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersUnpause ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-05-21 15:06:18 -06:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-09 22:50:48 +02:00
2015-05-02 03:03:35 +02:00
if err := s . daemon . ContainerUnpause ( vars [ "name" ] ) ; err != nil {
2014-05-21 15:06:18 -06:00
return err
}
2015-04-09 22:50:48 +02:00
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
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) getContainersExport ( 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
}
2015-04-12 16:04:01 +02:00
2015-04-17 15:18:28 -07:00
return s . daemon . ContainerExport ( vars [ "name" ] , w )
2013-05-07 16:33:12 -07:00
}
2013-04-24 14:01:40 +02:00
2015-04-27 23:11:29 +02:00
func ( s * Server ) getImagesJSON ( 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" ) ,
2015-04-16 14:26:33 -07:00
All : boolValue ( r , "all" ) ,
2015-04-07 18:57:54 -07:00
}
2014-01-13 14:55:31 -08:00
2015-04-17 15:18:28 -07:00
images , err := s . daemon . Repositories ( ) . Images ( & imagesConfig )
2015-04-07 18:57:54 -07:00
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
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) getInfo ( 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" )
2015-04-11 01:26:30 +08:00
2015-04-17 15:18:28 -07:00
info , err := s . daemon . SystemInfo ( )
2015-04-11 01:26:30 +08:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , info )
2013-05-07 16:33:12 -07:00
}
2013-04-18 18:56:22 +02:00
2015-04-27 23:11:29 +02:00
func ( s * Server ) getEvents ( 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
}
2015-04-17 15:18:28 -07:00
d := s . daemon
2015-04-03 15:17:49 -07:00
es := d . EventsService
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2015-05-08 12:33:33 -06:00
enc := json . NewEncoder ( ioutils . NewWriteFlusher ( w ) )
2015-04-03 15:17:49 -07:00
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
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) getImagesHistory ( 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" ]
2015-04-17 15:18:28 -07:00
history , err := s . daemon . Repositories ( ) . History ( name )
2015-04-10 18:55:07 +08:00
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
2015-04-27 23:11:29 +02:00
func ( s * Server ) getContainersChanges ( 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-05-02 03:03:35 +02:00
changes , err := s . daemon . ContainerChanges ( vars [ "name" ] )
2015-04-08 11:14:16 +02:00
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
2015-04-27 23:11:29 +02:00
func ( s * Server ) getContainersTop ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-02-21 23:15:28 +00:00
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." )
}
2015-04-09 15:13:01 -07:00
2013-06-28 15:51:58 +00:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-09 15:13:01 -07:00
2013-07-19 10:06:32 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
2014-01-16 14:58:20 -08:00
2015-04-17 15:18:28 -07:00
procList , err := s . daemon . ContainerTop ( vars [ "name" ] , r . Form . Get ( "ps_args" ) )
2015-04-09 15:13:01 -07:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , procList )
2013-06-28 15:51:58 +00:00
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) getContainersJSON ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-04-15 00:29:53 +08: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-16 14:00:18 -08:00
2015-04-07 12:34:30 -07:00
config := & daemon . ContainersConfig {
2015-04-16 14:26:33 -07:00
All : boolValue ( r , "all" ) ,
Size : boolValue ( r , "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 != "" {
2015-04-15 00:29:53 +08:00
limit , err := strconv . Atoi ( tmpLimit )
2015-04-07 12:34:30 -07:00
if err != nil {
2014-01-16 14:00:18 -08:00
return err
2013-09-04 23:41:44 +02:00
}
2015-04-15 00:29:53 +08:00
config . Limit = limit
2013-05-07 16:33:12 -07:00
}
2015-04-07 12:34:30 -07:00
2015-04-17 15:18:28 -07:00
containers , err := s . daemon . Containers ( config )
2015-04-07 12:34:30 -07:00
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-04-27 23:11:29 +02:00
func ( s * Server ) getContainersStats ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-01-07 14:43:04 -08:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-13 08:33:53 +02:00
2015-05-08 12:33:33 -06:00
return s . daemon . ContainerStats ( vars [ "name" ] , boolValue ( r , "stream" ) , ioutils . NewWriteFlusher ( w ) )
2015-01-07 14:43:04 -08:00
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) getContainersLogs ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-04-02 23:26:06 +04:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-06-18 23:11:04 +04:00
// Validate args here, because we can't return not StatusOK after job.Run() call
2015-04-16 14:26:33 -07:00
stdout , stderr := boolValue ( r , "stdout" ) , boolValue ( r , "stderr" )
2014-06-18 23:11:04 +04:00
if ! ( stdout || stderr ) {
return fmt . Errorf ( "Bad parameters: you must choose at least one stream" )
}
2014-04-02 23:26:06 +04:00
2015-04-11 23:49:14 +02:00
logsConfig := & daemon . ContainerLogsConfig {
2015-04-16 14:26:33 -07:00
Follow : boolValue ( r , "follow" ) ,
Timestamps : boolValue ( r , "timestamps" ) ,
2015-04-11 23:49:14 +02:00
Tail : r . Form . Get ( "tail" ) ,
UseStdout : stdout ,
UseStderr : stderr ,
2015-05-08 12:33:33 -06:00
OutStream : ioutils . NewWriteFlusher ( w ) ,
2014-04-02 23:26:06 +04:00
}
2015-04-17 15:18:28 -07:00
if err := s . daemon . ContainerLogs ( vars [ "name" ] , logsConfig ) ; err != nil {
2015-04-11 23:49:14 +02:00
fmt . Fprintf ( w , "Error running logs job: %s\n" , err )
2014-04-02 23:26:06 +04:00
}
2015-04-11 23:49:14 +02:00
2014-04-02 23:26:06 +04:00
return nil
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) postImagesTag ( 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
2015-04-14 10:46:29 +08:00
repo := r . Form . Get ( "repo" )
tag := r . Form . Get ( "tag" )
2015-04-16 14:26:33 -07:00
force := boolValue ( r , "force" )
2015-04-17 15:18:28 -07:00
if err := s . daemon . Repositories ( ) . Tag ( repo , tag , vars [ "name" ] , force ) ; 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
2015-04-27 23:11:29 +02:00
func ( s * Server ) postCommit ( 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-09-25 08:24:41 -07:00
if err := checkForJson ( r ) ; err != nil {
return err
}
2015-04-10 22:41:43 +02:00
cont := r . Form . Get ( "container" )
2013-12-11 17:03:48 -08:00
2015-04-16 14:26:33 -07:00
pause := boolValue ( r , "pause" )
2014-06-07 23:37:31 -07:00
if r . FormValue ( "pause" ) == "" && version . GreaterThanOrEqualTo ( "1.13" ) {
2015-04-10 22:41:43 +02:00
pause = true
}
2015-04-16 14:26:33 -07: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 22:41:43 +02: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 14:26:33 -07:00
Config : c ,
2014-06-07 23:37:31 -07:00
}
2015-04-21 14:23:48 -07:00
imgID , err := builder . Commit ( s . daemon , cont , containerCommitConfig )
2015-04-10 22:41:43 +02:00
if err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2015-04-10 22:41:43 +02:00
2015-03-28 17:39:24 +01:00
return writeJSON ( w , http . StatusCreated , & types . ContainerCommitResponse {
2015-04-10 22:41:43 +02:00
ID : imgID ,
2015-03-28 17:39:24 +01:00
} )
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
2015-04-27 23:11:29 +02:00
func ( s * Server ) postImagesCreate ( 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" )
)
2013-08-30 22:49:37 +02:00
authEncoded := r . Header . Get ( "X-Registry-Auth" )
2015-04-22 05:06:58 -07:00
authConfig := & cliconfig . 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
2015-04-22 05:06:58 -07:00
authConfig = & cliconfig . AuthConfig { }
2013-09-03 20:45:49 +02:00
}
}
2015-04-15 19:43:15 +08:00
2015-04-22 12:33:46 -07:00
var (
2015-04-25 09:10:32 +08:00
err error
2015-04-22 12:33:46 -07:00
useJSON = version . GreaterThan ( "1.0" )
2015-05-08 12:33:33 -06:00
output = ioutils . NewWriteFlusher ( w )
2015-04-22 12:33:46 -07:00
)
if useJSON {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
}
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
}
}
2015-04-15 19:43:15 +08:00
imagePullConfig := & graph . ImagePullConfig {
Parallel : version . GreaterThan ( "1.3" ) ,
MetaHeaders : metaHeaders ,
AuthConfig : authConfig ,
2015-04-25 09:10:32 +08:00
OutStream : output ,
2015-04-22 12:33:46 -07:00
Json : useJSON ,
2015-04-15 19:43:15 +08:00
}
2015-04-25 09:10:32 +08:00
err = s . daemon . Repositories ( ) . Pull ( image , tag , imagePullConfig )
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
}
2014-01-22 13:35:35 -08:00
2015-04-15 19:43:15 +08:00
src := r . Form . Get ( "fromSrc" )
imageImportConfig := & graph . ImageImportConfig {
Changes : r . Form [ "changes" ] ,
InConfig : r . Body ,
2015-04-25 09:10:32 +08:00
OutStream : output ,
2015-04-22 12:33:46 -07:00
Json : useJSON ,
2015-04-15 19:43:15 +08:00
}
2015-04-21 14:23:48 -07:00
newConfig , err := builder . BuildFromConfig ( s . daemon , & runconfig . Config { } , imageImportConfig . Changes )
2015-04-16 14:26:33 -07:00
if err != nil {
2013-05-16 12:09:06 -07:00
return err
2013-05-07 17:19:41 +02:00
}
2015-04-16 14:26:33 -07:00
imageImportConfig . ContainerConfig = newConfig
2015-04-15 19:43:15 +08:00
2015-04-25 09:10:32 +08:00
err = s . daemon . Repositories ( ) . Import ( src , repo , tag , imageImportConfig )
2015-04-22 12:33:46 -07:00
2015-04-25 09:10:32 +08:00
}
if err != nil {
if ! output . Flushed ( ) {
return err
}
2015-04-22 12:33:46 -07:00
sf := streamformatter . NewStreamFormatter ( useJSON )
2015-04-25 09:10:32 +08:00
output . 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
2015-04-25 09:10:32 +08:00
2013-05-07 16:33:12 -07:00
}
2013-05-07 17:19:41 +02:00
2015-04-27 23:11:29 +02:00
func ( s * Server ) getImagesSearch ( 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-04-22 05:06:58 -07:00
config * cliconfig . 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-04-22 05:06:58 -07:00
config = & cliconfig . 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-04-17 15:18:28 -07:00
query , err := s . daemon . RegistryService . Search ( r . Form . Get ( "term" ) , config , headers )
2015-03-31 16:21:37 -07:00
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
2015-04-27 23:11:29 +02:00
func ( s * Server ) postImagesPush ( 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
}
2015-04-22 05:06:58 -07:00
authConfig := & cliconfig . 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
2015-04-22 05:06:58 -07:00
authConfig = & cliconfig . 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
2015-04-21 11:16:25 +08:00
useJSON := version . GreaterThan ( "1.0" )
name := vars [ "name" ]
2015-05-08 12:33:33 -06:00
output := ioutils . NewWriteFlusher ( w )
2015-04-21 11:16:25 +08:00
imagePushConfig := & graph . ImagePushConfig {
MetaHeaders : metaHeaders ,
AuthConfig : authConfig ,
Tag : r . Form . Get ( "tag" ) ,
2015-04-23 10:27:34 -07:00
OutStream : output ,
2015-04-21 11:16:25 +08:00
Json : useJSON ,
}
if useJSON {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2014-02-13 19:21:27 +00:00
}
2014-01-22 17:33:29 -08:00
2015-04-21 11:16:25 +08:00
if err := s . daemon . Repositories ( ) . Push ( name , imagePushConfig ) ; err != nil {
2015-04-23 10:27:34 -07:00
if ! output . Flushed ( ) {
return err
}
2015-04-21 11:16:25 +08:00
sf := streamformatter . NewStreamFormatter ( useJSON )
2015-04-23 10:27:34 -07:00
output . Write ( sf . FormatError ( err ) )
2013-05-15 19:21:37 +00:00
}
2013-05-10 20:20:49 +02:00
return nil
2015-04-21 11:16:25 +08:00
2013-05-07 16:33:12 -07:00
}
2013-05-07 19:23:50 +02:00
2015-04-27 23:11:29 +02:00
func ( s * Server ) getImagesGet ( 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
}
2015-04-21 21:10:30 +08:00
useJSON := version . GreaterThan ( "1.0" )
if useJSON {
2013-11-14 01:05:37 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/x-tar" )
}
2015-04-21 21:10:30 +08:00
2015-05-08 12:33:33 -06:00
output := ioutils . NewWriteFlusher ( w )
2015-04-23 21:05:21 +02:00
imageExportConfig := & graph . ImageExportConfig { Outstream : output }
2014-05-01 00:26:24 -04:00
if name , ok := vars [ "name" ] ; ok {
2015-04-21 21:10:30 +08:00
imageExportConfig . Names = [ ] string { name }
2014-05-01 00:26:24 -04:00
} else {
2015-04-21 21:10:30 +08:00
imageExportConfig . Names = r . Form [ "names" ]
2014-05-01 00:26:24 -04:00
}
2015-04-21 21:10:30 +08:00
if err := s . daemon . Repositories ( ) . ImageExport ( imageExportConfig ) ; err != nil {
if ! output . Flushed ( ) {
return err
}
sf := streamformatter . NewStreamFormatter ( useJSON )
output . Write ( sf . FormatError ( err ) )
}
return nil
2013-09-02 09:06:17 -07:00
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) postImagesLoad ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-04-23 21:05:21 +02:00
return s . daemon . Repositories ( ) . Load ( r . Body , w )
2013-09-02 09:06:17 -07:00
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersCreate ( 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 (
2015-04-09 14:49:22 -07:00
warnings [ ] string
name = r . Form . Get ( "name" )
2014-01-24 23:15:40 -08:00
)
2014-09-25 08:24:41 -07:00
2015-04-10 17:05:21 -07:00
config , hostConfig , err := runconfig . DecodeContainerConfig ( r . Body )
if err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2015-04-09 14:49:22 -07:00
2015-04-17 15:18:28 -07:00
containerId , warnings , err := s . daemon . ContainerCreate ( name , config , hostConfig )
2015-04-09 14:49:22 -07:00
if err != nil {
2013-05-10 20:20:49 +02:00
return err
2013-05-07 16:33:12 -07:00
}
2015-04-09 14:49:22 -07:00
2015-02-24 11:12:47 -08:00
return writeJSON ( w , http . StatusCreated , & types . ContainerCreateResponse {
2015-04-09 14:49:22 -07:00
ID : containerId ,
Warnings : warnings ,
2015-02-24 11:12:47 -08:00
} )
2013-05-07 16:33:12 -07:00
}
2013-05-07 19:23:50 +02:00
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersRestart ( 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
}
2015-04-14 01:34:34 +02:00
2015-05-07 01:49:16 +02:00
timeout , _ := strconv . Atoi ( r . Form . Get ( "t" ) )
2015-04-14 01:34:34 +02:00
2015-04-17 15:18:28 -07:00
if err := s . daemon . ContainerRestart ( vars [ "name" ] , timeout ) ; err != nil {
2015-04-14 01:34:34 +02:00
return err
}
2013-05-09 21:42:29 +02:00
w . WriteHeader ( http . StatusNoContent )
2015-04-14 01:34:34 +02:00
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
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainerRename ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-10-05 02:47:54 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-10 01:52:55 +08:00
name := vars [ "name" ]
newName := r . Form . Get ( "name" )
2015-04-17 15:18:28 -07:00
if err := s . daemon . ContainerRename ( name , newName ) ; err != nil {
2014-10-05 02:47:54 +00:00
return err
}
w . WriteHeader ( http . StatusNoContent )
return nil
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) deleteContainers ( 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" ]
config := & daemon . ContainerRmConfig {
2015-04-16 14:26:33 -07:00
ForceRemove : boolValue ( r , "force" ) ,
RemoveVolume : boolValue ( r , "v" ) ,
RemoveLink : boolValue ( r , "link" ) ,
2015-04-08 23:38:53 +02:00
}
2015-04-17 15:18:28 -07:00
if err := s . daemon . ContainerRm ( name , config ) ; err != nil {
2015-04-10 09:53:40 -07: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 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
2015-04-27 23:11:29 +02:00
func ( s * Server ) deleteImages ( 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
name := vars [ "name" ]
2015-04-16 14:26:33 -07:00
force := boolValue ( r , "force" )
noprune := boolValue ( r , "noprune" )
2015-04-09 13:59:50 +02:00
2015-04-17 15:18:28 -07:00
list , err := s . daemon . ImageDelete ( name , force , noprune )
2015-04-09 13:59:50 +02:00
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
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersStart ( 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
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 17:39:54 -06:00
// allow a nil body for backwards compatibility
2015-04-10 17:05:21 -07: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 08:24:41 -07:00
if err := checkForJson ( r ) ; err != nil {
return err
2014-08-01 15:26:30 -07:00
}
2015-04-10 17:05:21 -07:00
c , err := runconfig . DecodeHostConfig ( r . Body )
if err != nil {
2014-08-01 15:26:30 -07:00
return err
2013-05-13 17:39:54 -06:00
}
2015-04-10 17:05:21 -07:00
hostConfig = c
2013-05-13 17:39:54 -06:00
}
2014-08-01 15:59:19 -07:00
2015-04-17 15:18:28 -07:00
if err := s . daemon . ContainerStart ( vars [ "name" ] , hostConfig ) ; 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
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersStop ( 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
}
2015-04-12 00:41:16 +02:00
2015-04-27 18:55:11 +00:00
seconds , _ := strconv . Atoi ( r . Form . Get ( "t" ) )
2015-04-12 00:41:16 +02:00
2015-04-17 15:18:28 -07:00
if err := s . daemon . ContainerStop ( vars [ "name" ] , seconds ) ; 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 )
2015-04-12 00:41:16 +02:00
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
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersWait ( 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
}
2015-04-09 23:11:11 +02:00
name := vars [ "name" ]
2015-04-17 15:18:28 -07:00
cont , err := s . daemon . Get ( name )
2015-03-25 21:01:14 -06:00
if err != nil {
return err
}
2015-04-09 23:11:11 +02:00
status , _ := cont . WaitStop ( - 1 * time . Second )
2015-03-25 21:01:14 -06:00
return writeJSON ( w , http . StatusOK , & types . ContainerWaitResponse {
2015-04-09 23:11:11 +02:00
StatusCode : status ,
2015-03-25 21:01:14 -06:00
} )
2013-05-07 16:33:12 -07:00
}
2013-05-06 11:31:22 +02:00
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersResize ( 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 {
2015-04-13 08:36:04 +02:00
return err
2015-04-09 23:51:22 +02:00
}
width , err := strconv . Atoi ( r . Form . Get ( "w" ) )
if err != nil {
2015-04-13 08:36:04 +02:00
return err
2015-04-09 23:51:22 +02:00
}
2015-05-02 03:03:35 +02:00
return s . daemon . ContainerResize ( vars [ "name" ] , height , width )
2013-05-23 19:33:28 -07:00
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersAttach ( 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-17 15:18:28 -07:00
cont , err := s . daemon . 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-16 14:26:33 -07:00
logs := boolValue ( r , "logs" )
stream := boolValue ( r , "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
2015-04-16 14:26:33 -07:00
if boolValue ( r , "stdin" ) {
2015-04-09 09:58:44 -07:00
stdin = inStream
}
2015-04-16 14:26:33 -07:00
if boolValue ( r , "stdout" ) {
2015-04-09 09:58:44 -07:00
stdout = outStream
}
2015-04-16 14:26:33 -07:00
if boolValue ( r , "stderr" ) {
2015-04-09 09:58:44 -07: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 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
2015-04-27 23:11:29 +02:00
func ( s * Server ) wsContainersAttach ( 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-17 15:18:28 -07:00
cont , err := s . daemon . Get ( vars [ "name" ] )
2015-04-06 12:19:38 -07:00
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
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) getContainersByName ( 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
}
2015-04-13 16:17:14 +02:00
name := vars [ "name" ]
2014-05-31 01:13:37 +00:00
if version . LessThan ( "1.12" ) {
2015-04-13 16:17:14 +02:00
containerJSONRaw , err := s . daemon . ContainerInspectRaw ( name )
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , containerJSONRaw )
2014-05-31 01:13:37 +00:00
}
2015-04-13 16:17:14 +02:00
containerJSON , err := s . daemon . ContainerInspect ( name )
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , containerJSON )
2013-05-07 16:33:12 -07:00
}
2013-05-02 05:07:06 +02:00
2015-04-27 23:11:29 +02:00
func ( s * Server ) getExecByID ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-11-17 15:50:09 -08:00
if vars == nil {
return fmt . Errorf ( "Missing parameter 'id'" )
}
2015-04-12 00:15:34 +02:00
2015-04-17 15:18:28 -07:00
eConfig , err := s . daemon . ContainerExecInspect ( vars [ "id" ] )
2015-04-12 00:15:34 +02:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , eConfig )
2014-11-17 15:50:09 -08:00
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) getImagesByName ( 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
}
2015-04-23 21:05:21 +02:00
name := vars [ "name" ]
2014-05-31 01:13:37 +00:00
if version . LessThan ( "1.12" ) {
2015-04-23 21:05:21 +02:00
imageInspectRaw , err := s . daemon . Repositories ( ) . LookupRaw ( name )
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , imageInspectRaw )
2014-05-31 01:13:37 +00:00
}
2015-04-23 21:05:21 +02:00
imageInspect , err := s . daemon . Repositories ( ) . Lookup ( name )
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , imageInspect )
2013-05-19 10:46:24 -07:00
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) postBuild ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-02-21 23:15:28 +00:00
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" )
2015-04-22 05:06:58 -07:00
authConfig = & cliconfig . AuthConfig { }
2014-01-03 15:13:32 -05:00
configFileEncoded = r . Header . Get ( "X-Registry-Config" )
2015-04-22 05:06:58 -07:00
configFile = & cliconfig . ConfigFile { }
2015-04-16 14:26:33 -07:00
buildConfig = builder . NewBuildConfig ( )
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
2015-04-22 05:06:58 -07:00
authConfig = & cliconfig . 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
2015-04-22 05:06:58 -07:00
configFile = & cliconfig . ConfigFile { }
2014-01-03 15:13:32 -05:00
}
}
2014-02-21 23:15:28 +00:00
if version . GreaterThanOrEqualTo ( "1.8" ) {
2015-04-16 14:26:33 -07:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
buildConfig . JSONFormat = true
2013-10-12 14:14:52 +02:00
}
2014-05-19 21:01:55 +03:00
2015-04-16 14:26:33 -07:00
if boolValue ( r , "forcerm" ) && version . GreaterThanOrEqualTo ( "1.12" ) {
buildConfig . Remove = true
2014-05-19 21:01:55 +03:00
} else if r . FormValue ( "rm" ) == "" && version . GreaterThanOrEqualTo ( "1.12" ) {
2015-04-16 14:26:33 -07:00
buildConfig . Remove = true
2014-05-16 14:47:33 +03:00
} else {
2015-04-16 14:26:33 -07:00
buildConfig . Remove = boolValue ( r , "rm" )
2014-05-16 14:47:33 +03:00
}
2015-04-16 14:26:33 -07:00
if boolValue ( r , "pull" ) && version . GreaterThanOrEqualTo ( "1.16" ) {
buildConfig . Pull = true
2014-11-21 19:51:32 +02:00
}
2015-04-16 11:11:26 -07:00
2015-05-08 12:33:33 -06:00
output := ioutils . NewWriteFlusher ( w )
2015-04-20 14:18:14 -07:00
buildConfig . Stdout = output
2015-04-16 14:26:33 -07: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
2015-04-23 09:40:23 +02:00
buildConfig . MemorySwap = int64ValueOrZero ( r , "memswap" )
buildConfig . Memory = int64ValueOrZero ( r , "memory" )
buildConfig . CpuShares = int64ValueOrZero ( r , "cpushares" )
2015-04-08 16:58:59 +08:00
buildConfig . CpuPeriod = int64ValueOrZero ( r , "cpuperiod" )
2015-04-23 09:40:23 +02:00
buildConfig . CpuQuota = int64ValueOrZero ( r , "cpuquota" )
2015-04-16 14:26:33 -07:00
buildConfig . CpuSetCpus = r . FormValue ( "cpusetcpus" )
buildConfig . CpuSetMems = r . FormValue ( "cpusetmems" )
2015-03-26 23:14:31 +00:00
buildConfig . CgroupParent = r . FormValue ( "cgroupparent" )
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-04-16 11:11:26 -07:00
logrus . Infof ( "Client disconnected, cancelling job: build" )
2015-04-16 14:26:33 -07:00
buildConfig . Cancel ( )
2015-03-10 22:10:00 +00:00
}
} ( )
}
2015-04-21 14:23:48 -07:00
if err := builder . Build ( s . daemon , buildConfig ) ; err != nil {
2015-04-20 14:18:14 -07: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 12:19:52 -08:00
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
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainersCopy ( 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-05-02 03:03:35 +02:00
data , err := s . daemon . ContainerCopy ( vars [ "name" ] , cfg . Resource )
2015-04-09 13:05:31 -07:00
if err != nil {
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
}
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 ( )
2015-05-02 03:03:35 +02:00
2015-04-09 13:05:31 -07:00
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
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainerExecCreate ( 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
}
2015-04-17 13:36:23 +08:00
name := vars [ "name" ]
2014-09-25 08:24:41 -07:00
2015-04-17 13:36:23 +08:00
execConfig := & runconfig . ExecConfig { }
if err := json . NewDecoder ( r . Body ) . Decode ( execConfig ) ; err != nil {
2014-09-15 22:56:47 +00:00
return err
}
2015-04-17 13:36:23 +08:00
execConfig . Container = name
if len ( execConfig . Cmd ) == 0 {
return fmt . Errorf ( "No exec command specified" )
}
2014-09-15 22:56:47 +00:00
// Register an instance of Exec in container.
2015-04-17 13:36:23 +08:00
id , err := s . daemon . ContainerExecCreate ( execConfig )
if err != nil {
logrus . Errorf ( "Error setting up exec command in container %s: %s" , name , err )
2014-09-15 22:56:47 +00:00
return err
}
2015-03-23 22:03:54 +01:00
return writeJSON ( w , http . StatusCreated , & types . ContainerExecCreateResponse {
2015-04-17 13:36:23 +08:00
ID : id ,
2015-03-23 22:03:54 +01:00
} )
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.
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainerExecStart ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-09-15 22:56:47 +00:00
if err := parseForm ( r ) ; err != nil {
return nil
}
var (
2015-04-17 13:36:23 +08:00
execName = vars [ "name" ]
stdin io . ReadCloser
stdout io . Writer
stderr io . Writer
2014-09-09 05:51:53 +00:00
)
2014-09-25 08:24:41 -07:00
2015-04-17 13:36:23 +08:00
execStartCheck := & types . ExecStartCheck { }
if err := json . NewDecoder ( r . Body ) . Decode ( execStartCheck ) ; err != nil {
2014-09-09 05:51:53 +00:00
return err
}
2015-04-17 13:36:23 +08:00
if ! execStartCheck . Detach {
2014-09-09 05:51:53 +00:00
// 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" )
}
2015-04-17 13:36:23 +08:00
if ! execStartCheck . 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
}
2015-04-17 13:36:23 +08:00
stdin = inStream
stdout = outStream
stderr = errStream
2014-09-09 05:51:53 +00:00
}
// Now run the user process in container.
2015-04-17 13:36:23 +08:00
if err := s . daemon . ContainerExecStart ( execName , stdin , stdout , stderr ) ; err != nil {
logrus . Errorf ( "Error starting exec command in container %s: %s" , execName , 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
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) postContainerExecResize ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-09-15 22:56:47 +00: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 {
2015-04-13 08:36:04 +02:00
return err
2015-04-09 23:51:22 +02:00
}
width , err := strconv . Atoi ( r . Form . Get ( "w" ) )
if err != nil {
2015-04-13 08:36:04 +02:00
return err
2015-04-09 23:51:22 +02:00
}
2015-04-17 15:18:28 -07:00
return s . daemon . ContainerExecResize ( vars [ "name" ] , height , width )
2014-09-15 22:56:47 +00:00
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) optionsHandler ( 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
}
2015-04-27 23:11:29 +02:00
func ( s * Server ) ping ( 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-04-27 23:11:29 +02:00
func makeHttpHandler ( 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
}
2015-04-27 23:11:29 +02:00
if err := handlerFunc ( 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
2015-04-27 23:11:29 +02:00
func createRouter ( s * Server ) * 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" : {
2015-04-17 15:18:28 -07:00
"/_ping" : s . ping ,
"/events" : s . getEvents ,
"/info" : s . getInfo ,
"/version" : s . getVersion ,
"/images/json" : s . getImagesJSON ,
"/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 16:33:12 -07:00
} ,
"POST" : {
2015-04-17 15:18:28 -07: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 16:33:12 -07:00
} ,
"DELETE" : {
2015-04-17 15:18:28 -07:00
"/containers/{name:.*}" : s . deleteContainers ,
"/images/{name:.*}" : s . deleteImages ,
2013-05-07 16:33:12 -07:00
} ,
2013-06-10 16:10:40 -09:00
"OPTIONS" : {
2015-04-17 15:18:28 -07:00
"" : s . optionsHandler ,
2013-06-10 16:10:40 -09:00
} ,
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
2015-04-17 15:18:28 -07:00
corsHeaders := s . cfg . CorsHeaders
if corsHeaders == "" && s . cfg . EnableCors {
2015-02-09 15:15:07 +08:00
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-04-27 23:11:29 +02:00
f := makeHttpHandler ( s . cfg . Logging , localMethod , localRoute , localFct , corsHeaders , version . Version ( s . cfg . Version ) )
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
}
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
}