2014-03-28 22:59:29 +00:00
package server
2013-04-11 02:48:21 +00:00
import (
2015-05-07 16:49:07 +00:00
"crypto/tls"
2013-08-30 20:49:37 +00:00
"encoding/base64"
2013-04-18 01:13:43 +00:00
"encoding/json"
2013-04-19 13:24:37 +00:00
"fmt"
2013-05-08 01:06:49 +00:00
"io"
2013-06-18 18:59:56 +00:00
"net"
2013-04-11 02:48:21 +00:00
"net/http"
2013-06-18 18:59:56 +00:00
"os"
2015-04-27 21:11:29 +00:00
"runtime"
2013-04-22 16:17:47 +00:00
"strconv"
2013-04-19 13:24:37 +00:00
"strings"
2015-04-27 21:11:29 +00:00
"time"
2014-03-28 22:59:29 +00:00
2014-07-24 20:37:44 +00:00
"github.com/gorilla/mux"
2015-06-16 14:08:18 +00:00
"golang.org/x/net/websocket"
2014-05-02 21:43:51 +00:00
2015-03-26 22:22:04 +00:00
"github.com/Sirupsen/logrus"
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/api"
2015-02-24 19:12:47 +00:00
"github.com/docker/docker/api/types"
2015-04-16 19:48:04 +00:00
"github.com/docker/docker/autogen/dockerversion"
2015-04-16 18:11:26 +00:00
"github.com/docker/docker/builder"
2015-04-22 12:06:58 +00:00
"github.com/docker/docker/cliconfig"
2015-03-31 23:21:37 +00:00
"github.com/docker/docker/daemon"
2015-04-08 01:57:54 +00:00
"github.com/docker/docker/graph"
2015-05-08 18:33:33 +00:00
"github.com/docker/docker/pkg/ioutils"
2015-04-03 22:17:49 +00:00
"github.com/docker/docker/pkg/jsonmessage"
2014-07-29 00:23:38 +00:00
"github.com/docker/docker/pkg/parsers"
2015-04-03 22:17:49 +00:00
"github.com/docker/docker/pkg/parsers/filters"
2015-04-16 19:48:04 +00:00
"github.com/docker/docker/pkg/parsers/kernel"
2015-04-09 18:56:47 +00:00
"github.com/docker/docker/pkg/signal"
2015-05-20 23:48:39 +00:00
"github.com/docker/docker/pkg/sockets"
2014-09-17 15:04:56 +00:00
"github.com/docker/docker/pkg/stdcopy"
2015-03-18 02:18:41 +00:00
"github.com/docker/docker/pkg/streamformatter"
2014-07-24 22:19:50 +00:00
"github.com/docker/docker/pkg/version"
2015-04-11 00:05:21 +00:00
"github.com/docker/docker/runconfig"
2015-05-15 00:01:06 +00:00
"github.com/docker/docker/utils"
2013-04-11 02:48:21 +00:00
)
2015-04-16 19:48:04 +00:00
type ServerConfig struct {
Logging bool
EnableCors bool
CorsHeaders string
Version string
SocketGroup string
2015-05-07 16:49:07 +00:00
TLSConfig * tls . Config
2015-04-16 19:48:04 +00:00
}
2015-04-17 21:32:18 +00:00
type Server struct {
2015-04-27 21:11:29 +00:00
daemon * daemon . Daemon
cfg * ServerConfig
router * mux . Router
start chan struct { }
servers [ ] serverCloser
2015-04-17 21:32:18 +00:00
}
2015-04-27 21:11:29 +00:00
func New ( cfg * ServerConfig ) * Server {
2015-04-17 22:18:28 +00:00
srv := & Server {
cfg : cfg ,
start : make ( chan struct { } ) ,
2015-04-17 21:32:18 +00:00
}
2015-04-27 21:11:29 +00:00
r := createRouter ( srv )
2015-04-17 22:18:28 +00:00
srv . router = r
return srv
2015-04-17 21:32:18 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) Close ( ) {
for _ , srv := range s . servers {
if err := srv . Close ( ) ; err != nil {
logrus . Error ( err )
}
}
}
2015-04-17 21:32:18 +00: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 21:11:29 +00:00
srv , err := s . newServer ( protoAddrParts [ 0 ] , protoAddrParts [ 1 ] )
if err != nil {
return err
}
2015-05-28 19:15:03 +00:00
s . servers = append ( s . servers , srv ... )
2015-04-27 21:11:29 +00:00
2015-05-28 19:15:03 +00:00
for _ , s := range srv {
logrus . Infof ( "Listening for HTTP on %s (%s)" , protoAddrParts [ 0 ] , protoAddrParts [ 1 ] )
go func ( s serverCloser ) {
if err := s . Serve ( ) ; err != nil && strings . Contains ( err . Error ( ) , "use of closed network connection" ) {
err = nil
}
chErrors <- err
} ( s )
}
2015-04-17 21:32:18 +00:00
}
for i := 0 ; i < len ( protoAddrs ) ; i ++ {
err := <- chErrors
if err != nil {
return err
}
}
return nil
}
2014-11-07 20:21:19 +00: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 21:11:29 +00:00
type HttpApiFunc func ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error
2013-07-13 14:59:07 +00:00
2013-05-08 01:06:49 +00:00
func hijackServer ( w http . ResponseWriter ) ( io . ReadCloser , io . Writer , error ) {
conn , _ , err := w . ( http . Hijacker ) . Hijack ( )
2013-05-06 09:31:22 +00:00
if err != nil {
return nil , nil , err
}
// Flush the options to make sure the client sets the raw mode
2013-05-08 01:06:49 +00:00
conn . Write ( [ ] byte { } )
return conn , conn , nil
2013-05-06 09:31:22 +00:00
}
2014-12-09 19:21:21 +00: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 15:24:41 +00: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 16:52:01 +00: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 16:52:01 +00:00
if err := r . ParseForm ( ) ; err != nil && ! strings . HasPrefix ( err . Error ( ) , "mime:" ) {
return err
}
return nil
}
2013-05-23 03:07:26 +00: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 09:31:22 +00:00
func httpError ( w http . ResponseWriter , err error ) {
2015-04-20 09:49:51 +00: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 17:34:06 +00:00
errStr := strings . ToLower ( err . Error ( ) )
2015-04-23 11:37:47 +00: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-17 00:08:14 +00:00
}
2015-04-20 09:49:51 +00:00
logrus . WithFields ( logrus . Fields { "statusCode" : statusCode , "err" : err } ) . Error ( "HTTP Error" )
http . Error ( w , err . Error ( ) , statusCode )
2013-05-06 09:31:22 +00:00
}
2015-02-24 19:12:47 +00: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 21:11:29 +00:00
func ( s * Server ) postAuth ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-04-22 12:06:58 +00:00
var config * cliconfig . AuthConfig
2015-03-31 23:21:37 +00:00
err := json . NewDecoder ( r . Body ) . Decode ( & config )
r . Body . Close ( )
2013-05-07 23:33:12 +00:00
if err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2015-04-17 22:18:28 +00:00
status , err := s . daemon . RegistryService . Auth ( config )
2015-03-31 23:21:37 +00: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 23:21:37 +00:00
return writeJSON ( w , http . StatusOK , & types . AuthResponse {
Status : status ,
} )
2013-05-07 23:33:12 +00:00
}
2013-05-06 11:34:31 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) getVersion ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-04-16 19:48:04 +00:00
v := & types . Version {
2015-06-03 23:56:09 +00:00
Version : dockerversion . VERSION ,
2015-06-17 21:57:32 +00:00
ApiVersion : api . Version ,
2015-06-03 23:56:09 +00:00
GitCommit : dockerversion . GITCOMMIT ,
GoVersion : runtime . Version ( ) ,
Os : runtime . GOOS ,
Arch : runtime . GOARCH ,
2015-06-19 17:03:13 +00:00
BuildTime : dockerversion . BUILDTIME ,
2015-04-16 19:48:04 +00:00
}
2015-06-03 23:56:09 +00:00
if version . GreaterThanOrEqualTo ( "1.19" ) {
v . Experimental = utils . ExperimentalBuild ( )
}
2015-04-16 19:48:04 +00:00
if kernelVersion , err := kernel . GetKernelVersion ( ) ; err == nil {
v . KernelVersion = kernelVersion . String ( )
}
return writeJSON ( w , http . StatusOK , v )
2013-05-07 23:33:12 +00:00
}
2013-05-06 11:34:31 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersKill ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2015-04-26 16:50:25 +00:00
if err := parseForm ( r ) ; err != nil {
2013-09-12 06:50:26 +00:00
return err
}
2015-04-09 18:56:47 +00:00
var sig uint64
name := vars [ "name" ]
// If we have a signal, look at it. Otherwise, do nothing
2015-06-02 15:00:44 +00:00
if sigStr := r . Form . Get ( "signal" ) ; sigStr != "" {
2015-04-09 18:56:47 +00:00
// Check if we passed the signal as a number:
// The largest legal signal is 31, so let's parse on 5 bits
2015-06-02 15:00:44 +00:00
sigN , err := strconv . ParseUint ( sigStr , 10 , 5 )
2015-04-09 18:56:47 +00:00
if err != nil {
// The signal is not a number, treat it as a string (either like
// "KILL" or like "SIGKILL")
2015-06-02 15:00:44 +00:00
syscallSig , ok := signal . SignalMap [ strings . TrimPrefix ( sigStr , "SIG" ) ]
if ! ok {
return fmt . Errorf ( "Invalid signal: %s" , sigStr )
}
sig = uint64 ( syscallSig )
} else {
sig = sigN
2015-04-09 18:56:47 +00:00
}
if sig == 0 {
return fmt . Errorf ( "Invalid signal: %s" , sigStr )
}
2013-11-17 00:26:04 +00:00
}
2015-04-09 18:56:47 +00:00
2015-04-26 16:50:25 +00:00
if err := s . daemon . ContainerKill ( name , sig ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2015-04-09 18:56:47 +00:00
2013-05-09 19:42:29 +00:00
w . WriteHeader ( http . StatusNoContent )
2013-05-10 18:20:49 +00:00
return nil
2013-05-07 23:33:12 +00:00
}
2013-05-06 11:34:31 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersPause ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-05-21 21:06:18 +00:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-09 20:50:48 +00:00
2015-05-02 01:03:35 +00:00
if err := s . daemon . ContainerPause ( vars [ "name" ] ) ; err != nil {
2014-05-21 21:06:18 +00:00
return err
}
2015-04-09 20:50:48 +00:00
2014-05-21 21:06:18 +00:00
w . WriteHeader ( http . StatusNoContent )
2015-04-09 20:50:48 +00:00
2014-05-21 21:06:18 +00:00
return nil
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersUnpause ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-05-21 21:06:18 +00:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-09 20:50:48 +00:00
2015-05-02 01:03:35 +00:00
if err := s . daemon . ContainerUnpause ( vars [ "name" ] ) ; err != nil {
2014-05-21 21:06:18 +00:00
return err
}
2015-04-09 20:50:48 +00:00
2014-05-21 21:06:18 +00:00
w . WriteHeader ( http . StatusNoContent )
2015-04-09 20:50:48 +00:00
2014-05-21 21:06:18 +00:00
return nil
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) getContainersExport ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2015-04-12 14:04:01 +00:00
2015-04-17 22:18:28 +00:00
return s . daemon . ContainerExport ( vars [ "name" ] , w )
2013-05-07 23:33:12 +00:00
}
2013-04-24 12:01:40 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) getImagesJSON ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 16:52:01 +00:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2014-01-13 22:55:31 +00:00
2015-04-08 01:57:54 +00: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 21:26:33 +00:00
All : boolValue ( r , "all" ) ,
2015-04-08 01:57:54 +00:00
}
2014-01-13 22:55:31 +00:00
2015-04-17 22:18:28 +00:00
images , err := s . daemon . Repositories ( ) . Images ( & imagesConfig )
2015-04-08 01:57:54 +00:00
if err != nil {
2014-01-18 02:54:02 +00:00
return err
2014-01-13 22:55:31 +00:00
}
2015-05-11 21:53:52 +00:00
return writeJSON ( w , http . StatusOK , images )
2013-05-09 21:52:12 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) getInfo ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-04-17 22:18:28 +00:00
info , err := s . daemon . SystemInfo ( )
2015-04-10 17:26:30 +00:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , info )
2013-05-07 23:33:12 +00:00
}
2013-04-18 16:56:22 +00:00
2015-04-27 21:11:29 +00: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 22:17:49 +00: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 19:12:17 +00:00
2015-04-03 22:17:49 +00: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
}
2015-06-05 02:47:46 +00:00
2015-04-03 22:17:49 +00:00
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 22:18:28 +00:00
d := s . daemon
2015-04-03 22:17:49 +00:00
es := d . EventsService
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2015-05-08 18:33:33 +00:00
enc := json . NewEncoder ( ioutils . NewWriteFlusher ( w ) )
2015-04-03 22:17:49 +00: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 ( )
2015-06-05 02:47:46 +00:00
if since == - 1 {
current = nil
}
2015-04-03 22:17:49 +00:00
defer es . Evict ( l )
for _ , ev := range current {
if ev . Time < since {
continue
}
if err := sendEvent ( ev ) ; err != nil {
return err
}
}
2015-06-03 18:42:51 +00:00
var closeNotify <- chan bool
if closeNotifier , ok := w . ( http . CloseNotifier ) ; ok {
closeNotify = closeNotifier . CloseNotify ( )
}
2015-04-03 22:17:49 +00:00
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
2015-06-03 18:42:51 +00:00
case <- closeNotify :
logrus . Debug ( "Client disconnected, stop sending events" )
return nil
2015-04-03 22:17:49 +00:00
}
}
2013-07-10 12:55:05 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) getImagesHistory ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2014-01-14 00:50:10 +00:00
2015-04-10 10:55:07 +00:00
name := vars [ "name" ]
2015-04-17 22:18:28 +00:00
history , err := s . daemon . Repositories ( ) . History ( name )
2015-04-10 10:55:07 +00:00
if err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2015-04-10 10:55:07 +00:00
return writeJSON ( w , http . StatusOK , history )
2013-05-07 23:33:12 +00:00
}
2013-04-19 13:24:37 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) getContainersChanges ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2014-01-15 00:51:59 +00:00
2015-05-02 01:03:35 +00:00
changes , err := s . daemon . ContainerChanges ( vars [ "name" ] )
2015-04-08 09:14:16 +00:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , changes )
2013-05-07 23:33:12 +00:00
}
2013-04-19 13:24:37 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) getContainersTop ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-06-28 15:51:58 +00:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-09 22:13:01 +00:00
2013-07-19 10:06:32 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
2014-01-16 22:58:20 +00:00
2015-04-17 22:18:28 +00:00
procList , err := s . daemon . ContainerTop ( vars [ "name" ] , r . Form . Get ( "ps_args" ) )
2015-04-09 22:13:01 +00:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , procList )
2013-06-28 15:51:58 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) getContainersJSON ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-04-14 16:29:53 +00:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2014-01-16 22:00:18 +00:00
2015-04-07 19:34:30 +00:00
config := & daemon . ContainersConfig {
2015-04-16 21:26:33 +00:00
All : boolValue ( r , "all" ) ,
Size : boolValue ( r , "size" ) ,
2015-04-07 19:34:30 +00: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 19:34:30 +00:00
if tmpLimit := r . Form . Get ( "limit" ) ; tmpLimit != "" {
2015-04-14 16:29:53 +00:00
limit , err := strconv . Atoi ( tmpLimit )
2015-04-07 19:34:30 +00:00
if err != nil {
2014-01-16 22:00:18 +00:00
return err
2013-09-04 21:41:44 +00:00
}
2015-04-14 16:29:53 +00:00
config . Limit = limit
2013-05-07 23:33:12 +00:00
}
2015-04-07 19:34:30 +00:00
2015-04-17 22:18:28 +00:00
containers , err := s . daemon . Containers ( config )
2015-04-07 19:34:30 +00:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , containers )
2013-05-07 23:33:12 +00:00
}
2013-05-06 09:31:22 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) getContainersStats ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-01-07 22:43:04 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-13 06:33:53 +00:00
2015-06-12 15:27:39 +00:00
stream := boolValueOrDefault ( r , "stream" , true )
var out io . Writer
if ! stream {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
out = w
} else {
out = ioutils . NewWriteFlusher ( w )
}
2015-06-16 14:33:49 +00:00
var closeNotifier <- chan bool
if notifier , ok := w . ( http . CloseNotifier ) ; ok {
closeNotifier = notifier . CloseNotify ( )
}
config := & daemon . ContainerStatsConfig {
Stream : stream ,
OutStream : out ,
Stop : closeNotifier ,
}
return s . daemon . ContainerStats ( vars [ "name" ] , config )
2015-01-07 22:43:04 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) getContainersLogs ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-04-02 19:26:06 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-06-18 19:11:04 +00:00
// Validate args here, because we can't return not StatusOK after job.Run() call
2015-04-16 21:26:33 +00:00
stdout , stderr := boolValue ( r , "stdout" ) , boolValue ( r , "stderr" )
2014-06-18 19:11:04 +00:00
if ! ( stdout || stderr ) {
return fmt . Errorf ( "Bad parameters: you must choose at least one stream" )
}
2014-04-02 19:26:06 +00:00
2015-04-14 04:36:12 +00:00
var since time . Time
if r . Form . Get ( "since" ) != "" {
s , err := strconv . ParseInt ( r . Form . Get ( "since" ) , 10 , 64 )
if err != nil {
return err
}
since = time . Unix ( s , 0 )
}
2015-06-04 19:15:33 +00:00
var closeNotifier <- chan bool
if notifier , ok := w . ( http . CloseNotifier ) ; ok {
closeNotifier = notifier . CloseNotify ( )
}
2015-04-11 21:49:14 +00:00
logsConfig := & daemon . ContainerLogsConfig {
2015-04-16 21:26:33 +00:00
Follow : boolValue ( r , "follow" ) ,
Timestamps : boolValue ( r , "timestamps" ) ,
2015-04-14 04:36:12 +00:00
Since : since ,
2015-04-11 21:49:14 +00:00
Tail : r . Form . Get ( "tail" ) ,
UseStdout : stdout ,
UseStderr : stderr ,
2015-05-08 18:33:33 +00:00
OutStream : ioutils . NewWriteFlusher ( w ) ,
2015-06-04 19:15:33 +00:00
Stop : closeNotifier ,
2014-04-02 19:26:06 +00:00
}
2015-04-17 22:18:28 +00:00
if err := s . daemon . ContainerLogs ( vars [ "name" ] , logsConfig ) ; err != nil {
2015-04-11 21:49:14 +00:00
fmt . Fprintf ( w , "Error running logs job: %s\n" , err )
2014-04-02 19:26:06 +00:00
}
2015-04-11 21:49:14 +00:00
2014-04-02 19:26:06 +00:00
return nil
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) postImagesTag ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 16:52:01 +00:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2013-04-19 13:24:37 +00:00
2015-04-14 02:46:29 +00:00
repo := r . Form . Get ( "repo" )
tag := r . Form . Get ( "tag" )
2015-04-16 21:26:33 +00:00
force := boolValue ( r , "force" )
2015-05-15 00:01:06 +00:00
name := vars [ "name" ]
if err := s . daemon . Repositories ( ) . Tag ( repo , tag , name , force ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2015-05-15 00:01:06 +00:00
s . daemon . EventsService . Log ( "tag" , utils . ImageReference ( repo , tag ) , "" )
2013-05-07 23:33:12 +00:00
w . WriteHeader ( http . StatusCreated )
2013-05-10 18:20:49 +00:00
return nil
2013-05-07 23:33:12 +00:00
}
2013-04-24 14:06:03 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) postCommit ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 16:52:01 +00:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2014-09-25 15:24:41 +00:00
if err := checkForJson ( r ) ; err != nil {
return err
}
2015-06-20 10:40:37 +00:00
cname := r . Form . Get ( "container" )
2013-12-12 01:03:48 +00:00
2015-04-16 21:26:33 +00:00
pause := boolValue ( r , "pause" )
2014-06-08 06:37:31 +00:00
if r . FormValue ( "pause" ) == "" && version . GreaterThanOrEqualTo ( "1.13" ) {
2015-04-10 20:41:43 +00:00
pause = true
}
2015-04-16 21:26:33 +00:00
c , _ , err := runconfig . DecodeContainerConfig ( r . Body )
if err != nil && err != io . EOF { //Do not fail if body is empty.
return err
}
2015-06-20 10:40:37 +00:00
commitCfg := & builder . BuilderCommitConfig {
2015-04-10 20:41:43 +00:00
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 21:26:33 +00:00
Config : c ,
2014-06-08 06:37:31 +00:00
}
2015-06-20 10:40:37 +00:00
imgID , err := builder . Commit ( cname , s . daemon , commitCfg )
2015-04-10 20:41:43 +00:00
if err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2015-04-10 20:41:43 +00:00
2015-03-28 16:39:24 +00:00
return writeJSON ( w , http . StatusCreated , & types . ContainerCommitResponse {
2015-04-10 20:41:43 +00:00
ID : imgID ,
2015-03-28 16:39:24 +00:00
} )
2013-05-07 23:33:12 +00:00
}
2013-04-24 14:06:03 +00:00
2013-05-10 00:50:56 +00:00
// Creates an image from Pull or from Import
2015-04-27 21:11:29 +00:00
func ( s * Server ) postImagesCreate ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 16:52:01 +00:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2013-04-22 21:37:22 +00:00
2014-01-22 21:35:35 +00:00
var (
image = r . Form . Get ( "fromImage" )
2014-07-07 23:09:42 +00:00
repo = r . Form . Get ( "repo" )
2014-01-22 21:35:35 +00:00
tag = r . Form . Get ( "tag" )
)
2013-08-30 20:49:37 +00:00
authEncoded := r . Header . Get ( "X-Registry-Auth" )
2015-04-22 12:06:58 +00:00
authConfig := & cliconfig . AuthConfig { }
2013-08-30 20:49:37 +00:00
if authEncoded != "" {
authJson := base64 . NewDecoder ( base64 . URLEncoding , strings . NewReader ( authEncoded ) )
if err := json . NewDecoder ( authJson ) . Decode ( authConfig ) ; err != nil {
2013-09-03 18:45:49 +00:00
// for a pull it is not an error if no auth was given
2013-08-30 19:46:19 +00:00
// to increase compatibility with the existing api it is defaulting to be empty
2015-04-22 12:06:58 +00:00
authConfig = & cliconfig . AuthConfig { }
2013-09-03 18:45:49 +00:00
}
}
2015-04-15 11:43:15 +00:00
2015-04-22 19:33:46 +00:00
var (
2015-05-11 21:53:52 +00:00
err error
output = ioutils . NewWriteFlusher ( w )
2015-04-22 19:33:46 +00:00
)
2015-05-11 21:53:52 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2015-04-22 19:33:46 +00:00
2013-05-07 23:33:12 +00:00
if image != "" { //pull
2014-07-07 23:09:42 +00:00
if tag == "" {
2014-07-29 00:23:38 +00:00
image , tag = parsers . ParseRepositoryTag ( image )
2014-07-07 23:09:42 +00:00
}
2013-08-22 19:15:31 +00:00
metaHeaders := map [ string ] [ ] string { }
for k , v := range r . Header {
if strings . HasPrefix ( k , "X-Meta-" ) {
metaHeaders [ k ] = v
}
}
2015-04-15 11:43:15 +00:00
imagePullConfig := & graph . ImagePullConfig {
MetaHeaders : metaHeaders ,
AuthConfig : authConfig ,
2015-04-25 01:10:32 +00:00
OutStream : output ,
2015-04-15 11:43:15 +00:00
}
2015-04-25 01:10:32 +00:00
err = s . daemon . Repositories ( ) . Pull ( image , tag , imagePullConfig )
2013-05-07 23:33:12 +00:00
} else { //import
2014-07-07 23:09:42 +00:00
if tag == "" {
2014-07-29 00:23:38 +00:00
repo , tag = parsers . ParseRepositoryTag ( repo )
2014-07-07 23:09:42 +00:00
}
2014-01-22 21:35:35 +00:00
2015-04-15 11:43:15 +00:00
src := r . Form . Get ( "fromSrc" )
imageImportConfig := & graph . ImageImportConfig {
Changes : r . Form [ "changes" ] ,
InConfig : r . Body ,
2015-04-25 01:10:32 +00:00
OutStream : output ,
2015-04-15 11:43:15 +00:00
}
2015-05-14 00:21:02 +00:00
// 'err' MUST NOT be defined within this block, we need any error
// generated from the download to be available to the output
// stream processing below
var newConfig * runconfig . Config
newConfig , err = builder . BuildFromConfig ( s . daemon , & runconfig . Config { } , imageImportConfig . Changes )
2015-04-16 21:26:33 +00:00
if err != nil {
2013-05-16 19:09:06 +00:00
return err
2013-05-07 15:19:41 +00:00
}
2015-04-16 21:26:33 +00:00
imageImportConfig . ContainerConfig = newConfig
2015-04-15 11:43:15 +00:00
2015-04-25 01:10:32 +00:00
err = s . daemon . Repositories ( ) . Import ( src , repo , tag , imageImportConfig )
}
if err != nil {
if ! output . Flushed ( ) {
return err
}
2015-05-12 18:18:54 +00:00
sf := streamformatter . NewJSONStreamFormatter ( )
2015-04-25 01:10:32 +00:00
output . Write ( sf . FormatError ( err ) )
2013-05-07 23:33:12 +00:00
}
2014-01-22 21:35:35 +00:00
2013-05-10 18:20:49 +00:00
return nil
2015-04-25 01:10:32 +00:00
2013-05-07 23:33:12 +00:00
}
2013-05-07 15:19:41 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) getImagesSearch ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 16:52:01 +00:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2014-01-21 04:06:19 +00:00
var (
2015-04-22 12:06:58 +00:00
config * cliconfig . AuthConfig
2014-01-21 04:06:19 +00:00
authEncoded = r . Header . Get ( "X-Registry-Auth" )
2015-03-31 23:21:37 +00: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 23:21:37 +00: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 12:06:58 +00: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 23:21:37 +00:00
headers [ k ] = v
2014-01-21 04:06:19 +00:00
}
}
2015-04-17 22:18:28 +00:00
query , err := s . daemon . RegistryService . Search ( r . Form . Get ( "term" ) , config , headers )
2015-03-31 23:21:37 +00:00
if err != nil {
return err
}
return json . NewEncoder ( w ) . Encode ( query . Results )
2013-05-07 23:33:12 +00:00
}
2013-04-30 15:04:31 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) postImagesPush ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-23 01:33:29 +00:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2013-08-22 19:15:31 +00: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 12:06:58 +00:00
authConfig := & cliconfig . AuthConfig { }
2013-08-23 07:38:33 +00:00
2013-08-30 20:49:37 +00: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 12:06:58 +00:00
authConfig = & cliconfig . AuthConfig { }
2013-08-23 07:38:33 +00:00
}
} else {
2013-08-30 19:46:19 +00:00
// the old format is supported for compatibility if there was no authConfig header
2013-08-23 07:38:33 +00:00
if err := json . NewDecoder ( r . Body ) . Decode ( authConfig ) ; err != nil {
2015-03-27 15:17:50 +00:00
return fmt . Errorf ( "Bad parameters and missing X-Registry-Auth: %v" , err )
2013-08-23 07:38:33 +00:00
}
}
2013-05-15 19:21:37 +00:00
2015-04-21 03:16:25 +00:00
name := vars [ "name" ]
2015-05-08 18:33:33 +00:00
output := ioutils . NewWriteFlusher ( w )
2015-04-21 03:16:25 +00:00
imagePushConfig := & graph . ImagePushConfig {
MetaHeaders : metaHeaders ,
AuthConfig : authConfig ,
Tag : r . Form . Get ( "tag" ) ,
2015-04-23 17:27:34 +00:00
OutStream : output ,
2014-02-13 19:21:27 +00:00
}
2014-01-23 01:33:29 +00:00
2015-05-11 21:53:52 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2015-04-21 03:16:25 +00:00
if err := s . daemon . Repositories ( ) . Push ( name , imagePushConfig ) ; err != nil {
2015-04-23 17:27:34 +00:00
if ! output . Flushed ( ) {
return err
}
2015-05-12 18:18:54 +00:00
sf := streamformatter . NewJSONStreamFormatter ( )
2015-04-23 17:27:34 +00:00
output . Write ( sf . FormatError ( err ) )
2013-05-15 19:21:37 +00:00
}
2013-05-10 18:20:49 +00:00
return nil
2015-04-21 03:16:25 +00:00
2013-05-07 23:33:12 +00:00
}
2013-05-07 17:23:50 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) getImagesGet ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-01-07 20:39:15 +00:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-05-01 04:26:24 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
2015-04-21 13:10:30 +00:00
2015-05-11 21:53:52 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/x-tar" )
2015-04-21 13:10:30 +00:00
2015-05-08 18:33:33 +00:00
output := ioutils . NewWriteFlusher ( w )
2015-04-23 19:05:21 +00:00
imageExportConfig := & graph . ImageExportConfig { Outstream : output }
2014-05-01 04:26:24 +00:00
if name , ok := vars [ "name" ] ; ok {
2015-04-21 13:10:30 +00:00
imageExportConfig . Names = [ ] string { name }
2014-05-01 04:26:24 +00:00
} else {
2015-04-21 13:10:30 +00:00
imageExportConfig . Names = r . Form [ "names" ]
2014-05-01 04:26:24 +00:00
}
2015-04-21 13:10:30 +00:00
if err := s . daemon . Repositories ( ) . ImageExport ( imageExportConfig ) ; err != nil {
if ! output . Flushed ( ) {
return err
}
2015-05-12 18:18:54 +00:00
sf := streamformatter . NewJSONStreamFormatter ( )
2015-04-21 13:10:30 +00:00
output . Write ( sf . FormatError ( err ) )
}
return nil
2013-09-02 16:06:17 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) postImagesLoad ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2015-04-23 19:05:21 +00:00
return s . daemon . Repositories ( ) . Load ( r . Body , w )
2013-09-02 16:06:17 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersCreate ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-10-28 23:58:59 +00:00
if err := parseForm ( r ) ; err != nil {
2015-05-27 10:16:28 +00:00
return err
2013-10-28 23:58:59 +00:00
}
2015-02-24 19:12:47 +00:00
if err := checkForJson ( r ) ; err != nil {
return err
}
2014-01-25 07:15:40 +00:00
var (
2015-04-09 21:49:22 +00:00
warnings [ ] string
name = r . Form . Get ( "name" )
2014-01-25 07:15:40 +00:00
)
2014-09-25 15:24:41 +00:00
2015-04-11 00:05:21 +00:00
config , hostConfig , err := runconfig . DecodeContainerConfig ( r . Body )
if err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2015-06-03 19:01:53 +00:00
adjustCpuShares ( version , hostConfig )
2015-04-09 21:49:22 +00:00
2015-04-17 22:18:28 +00:00
containerId , warnings , err := s . daemon . ContainerCreate ( name , config , hostConfig )
2015-04-09 21:49:22 +00:00
if err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2015-04-09 21:49:22 +00:00
2015-02-24 19:12:47 +00:00
return writeJSON ( w , http . StatusCreated , & types . ContainerCreateResponse {
2015-04-09 21:49:22 +00:00
ID : containerId ,
Warnings : warnings ,
2015-02-24 19:12:47 +00:00
} )
2013-05-07 23:33:12 +00:00
}
2013-05-07 17:23:50 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersRestart ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 16:52:01 +00:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2015-04-13 23:34:34 +00:00
2015-05-06 23:49:16 +00:00
timeout , _ := strconv . Atoi ( r . Form . Get ( "t" ) )
2015-04-13 23:34:34 +00:00
2015-04-17 22:18:28 +00:00
if err := s . daemon . ContainerRestart ( vars [ "name" ] , timeout ) ; err != nil {
2015-04-13 23:34:34 +00:00
return err
}
2013-05-09 19:42:29 +00:00
w . WriteHeader ( http . StatusNoContent )
2015-04-13 23:34:34 +00:00
2013-05-10 18:20:49 +00:00
return nil
2013-05-07 23:33:12 +00:00
}
2013-05-06 11:34:31 +00:00
2015-04-27 21:11:29 +00: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-09 17:52:55 +00:00
name := vars [ "name" ]
newName := r . Form . Get ( "name" )
2015-04-17 22:18:28 +00: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 21:11:29 +00:00
func ( s * Server ) deleteContainers ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 16:52:01 +00:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2014-07-06 20:43:24 +00:00
2015-04-08 21:38:53 +00:00
name := vars [ "name" ]
config := & daemon . ContainerRmConfig {
2015-04-16 21:26:33 +00:00
ForceRemove : boolValue ( r , "force" ) ,
RemoveVolume : boolValue ( r , "v" ) ,
RemoveLink : boolValue ( r , "link" ) ,
2015-04-08 21:38:53 +00:00
}
2015-04-17 22:18:28 +00:00
if err := s . daemon . ContainerRm ( name , config ) ; err != nil {
2015-04-10 16:53:40 +00: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 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2015-04-08 21:38:53 +00:00
2013-05-09 19:42:29 +00:00
w . WriteHeader ( http . StatusNoContent )
2015-04-08 21:38:53 +00:00
2013-05-10 18:20:49 +00:00
return nil
2013-05-07 23:33:12 +00:00
}
2013-04-22 21:37:22 +00:00
2015-04-27 21:11:29 +00: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 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2014-01-17 02:40:33 +00:00
2015-04-09 11:59:50 +00:00
name := vars [ "name" ]
2015-04-16 21:26:33 +00:00
force := boolValue ( r , "force" )
noprune := boolValue ( r , "noprune" )
2015-04-09 11:59:50 +00:00
2015-04-17 22:18:28 +00:00
list , err := s . daemon . ImageDelete ( name , force , noprune )
2015-04-09 11:59:50 +00:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , list )
2013-05-07 23:33:12 +00:00
}
2013-04-19 13:24:37 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersStart ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-10-27 02:24:01 +00:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-06-24 18:31:56 +00:00
2014-10-01 02:06:31 +00:00
// If contentLength is -1, we can assumed chunked encoding
// or more technically that the length is unknown
2015-04-11 17:31:34 +00:00
// https://golang.org/src/pkg/net/http/request.go#L139
2014-10-01 02:06:31 +00:00
// net/http otherwise seems to swallow any headers related to chunked encoding
// including r.TransferEncoding
2013-05-13 23:39:54 +00:00
// allow a nil body for backwards compatibility
2015-04-11 00:05:21 +00:00
var hostConfig * runconfig . HostConfig
2014-10-01 02:06:31 +00:00
if r . Body != nil && ( r . ContentLength > 0 || r . ContentLength == - 1 ) {
2014-09-25 15:24:41 +00:00
if err := checkForJson ( r ) ; err != nil {
return err
2014-08-01 22:26:30 +00:00
}
2015-04-11 00:05:21 +00:00
c , err := runconfig . DecodeHostConfig ( r . Body )
if err != nil {
2014-08-01 22:26:30 +00:00
return err
2013-05-13 23:39:54 +00:00
}
2015-04-11 00:05:21 +00:00
hostConfig = c
2013-05-13 23:39:54 +00:00
}
2014-08-01 22:59:19 +00:00
2015-04-17 22:18:28 +00: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 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2013-05-09 19:42:29 +00:00
w . WriteHeader ( http . StatusNoContent )
2013-05-10 18:20:49 +00:00
return nil
2013-05-07 23:33:12 +00:00
}
2013-04-19 13:24:37 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersStop ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 16:52:01 +00:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2015-04-11 22:41:16 +00:00
2015-04-27 18:55:11 +00:00
seconds , _ := strconv . Atoi ( r . Form . Get ( "t" ) )
2015-04-11 22:41:16 +00:00
2015-04-17 22:18:28 +00: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 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2013-05-09 19:42:29 +00:00
w . WriteHeader ( http . StatusNoContent )
2015-04-11 22:41:16 +00:00
2013-05-10 18:20:49 +00:00
return nil
2013-05-07 23:33:12 +00:00
}
2013-04-19 13:24:37 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersWait ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2015-04-09 21:11:11 +00:00
2015-05-05 20:25:05 +00:00
status , err := s . daemon . ContainerWait ( vars [ "name" ] , - 1 * time . Second )
2015-03-26 03:01:14 +00:00
if err != nil {
return err
}
2015-04-09 21:11:11 +00:00
2015-03-26 03:01:14 +00:00
return writeJSON ( w , http . StatusOK , & types . ContainerWaitResponse {
2015-04-09 21:11:11 +00:00
StatusCode : status ,
2015-03-26 03:01:14 +00:00
} )
2013-05-07 23:33:12 +00:00
}
2013-05-06 09:31:22 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersResize ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-24 02:33:28 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-04-09 21:51:22 +00:00
height , err := strconv . Atoi ( r . Form . Get ( "h" ) )
if err != nil {
2015-04-13 06:36:04 +00:00
return err
2015-04-09 21:51:22 +00:00
}
width , err := strconv . Atoi ( r . Form . Get ( "w" ) )
if err != nil {
2015-04-13 06:36:04 +00:00
return err
2015-04-09 21:51:22 +00:00
}
2015-05-02 01:03:35 +00:00
return s . daemon . ContainerResize ( vars [ "name" ] , height , width )
2013-05-24 02:33:28 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersAttach ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-08 16:52:01 +00:00
if err := parseForm ( r ) ; err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2013-05-07 23:33:12 +00:00
2015-07-01 16:10:31 +00:00
cont , err := s . daemon . Get ( vars [ "name" ] )
if err != nil {
return err
}
2013-09-11 18:35:09 +00:00
inStream , outStream , err := hijackServer ( w )
2013-05-07 23:33:12 +00:00
if err != nil {
2013-05-10 18:20:49 +00:00
return err
2013-05-07 23:33:12 +00:00
}
2014-12-09 19:21:21 +00:00
defer closeStreams ( inStream , outStream )
2013-04-11 02:48:21 +00:00
2014-12-15 18:57:39 +00: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 18:35:09 +00:00
2015-05-05 20:25:05 +00:00
attachWithLogsConfig := & daemon . ContainerAttachWithLogsConfig {
InStream : inStream ,
OutStream : outStream ,
UseStdin : boolValue ( r , "stdin" ) ,
UseStdout : boolValue ( r , "stdout" ) ,
UseStderr : boolValue ( r , "stderr" ) ,
Logs : boolValue ( r , "logs" ) ,
Stream : boolValue ( r , "stream" ) ,
2013-09-11 18:35:09 +00:00
}
2015-07-01 16:10:31 +00:00
if err := s . daemon . ContainerAttachWithLogs ( cont , attachWithLogsConfig ) ; err != nil {
2014-06-17 20:07:34 +00:00
fmt . Fprintf ( outStream , "Error attaching: %s\n" , err )
2013-05-07 23:33:12 +00:00
}
2015-05-05 20:25:05 +00:00
2013-05-10 18:20:49 +00:00
return nil
2013-05-07 23:33:12 +00:00
}
2013-05-02 03:07:06 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) wsContainersAttach ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-07-05 17:55:15 +00:00
if err := parseForm ( r ) ; err != nil {
return err
}
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2015-07-01 16:10:31 +00:00
cont , err := s . daemon . Get ( vars [ "name" ] )
if err != nil {
return err
}
2013-07-13 14:59:07 +00:00
h := websocket . Handler ( func ( ws * websocket . Conn ) {
defer ws . Close ( )
2015-04-06 19:19:38 +00:00
2015-05-05 20:25:05 +00:00
wsAttachWithLogsConfig := & daemon . ContainerWsAttachWithLogsConfig {
InStream : ws ,
OutStream : ws ,
ErrStream : ws ,
Logs : boolValue ( r , "logs" ) ,
Stream : boolValue ( r , "stream" ) ,
}
2015-07-01 16:10:31 +00:00
if err := s . daemon . ContainerWsAttachWithLogs ( cont , wsAttachWithLogsConfig ) ; err != nil {
2015-03-26 22:22:04 +00:00
logrus . Errorf ( "Error attaching websocket: %s" , err )
2013-07-13 14:59:07 +00:00
}
} )
h . ServeHTTP ( w , r )
2013-07-05 17:55:15 +00:00
return nil
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) getContainersByName ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2015-04-13 14:17:14 +00:00
2015-06-02 21:37:59 +00:00
if version . LessThan ( "1.19" ) {
containerJSONRaw , err := s . daemon . ContainerInspectRaw ( vars [ "name" ] )
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , containerJSONRaw )
}
2015-05-11 21:53:52 +00:00
containerJSON , err := s . daemon . ContainerInspect ( vars [ "name" ] )
2015-04-13 14:17:14 +00:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , containerJSON )
2013-05-07 23:33:12 +00:00
}
2013-05-02 03:07:06 +00:00
2015-04-27 21:11:29 +00:00
func ( s * Server ) getExecByID ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2014-11-17 23:50:09 +00:00
if vars == nil {
return fmt . Errorf ( "Missing parameter 'id'" )
}
2015-04-11 22:15:34 +00:00
2015-04-17 22:18:28 +00:00
eConfig , err := s . daemon . ContainerExecInspect ( vars [ "id" ] )
2015-04-11 22:15:34 +00:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , eConfig )
2014-11-17 23:50:09 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) getImagesByName ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-05-09 23:28:47 +00:00
if vars == nil {
2013-05-10 18:20:49 +00:00
return fmt . Errorf ( "Missing parameter" )
2013-05-09 23:28:47 +00:00
}
2015-04-23 19:05:21 +00:00
2015-05-11 21:53:52 +00:00
imageInspect , err := s . daemon . Repositories ( ) . Lookup ( vars [ "name" ] )
2015-04-23 19:05:21 +00:00
if err != nil {
return err
}
return writeJSON ( w , http . StatusOK , imageInspect )
2013-05-19 17:46:24 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) postBuild ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-12-10 00:25:19 +00:00
var (
2015-06-23 19:58:17 +00:00
authConfigs = map [ string ] cliconfig . AuthConfig { }
authConfigsEncoded = r . Header . Get ( "X-Registry-Config" )
buildConfig = builder . NewBuildConfig ( )
2013-12-10 00:25:19 +00:00
)
2014-01-03 20:13:32 +00:00
2015-06-23 19:58:17 +00:00
if authConfigsEncoded != "" {
authConfigsJSON := base64 . NewDecoder ( base64 . URLEncoding , strings . NewReader ( authConfigsEncoded ) )
if err := json . NewDecoder ( authConfigsJSON ) . Decode ( & authConfigs ) ; err != nil {
2014-01-03 20:13:32 +00:00
// for a pull it is not an error if no auth was given
2015-06-23 19:58:17 +00:00
// to increase compatibility with the existing api it is defaulting
// to be empty.
2014-01-03 20:13:32 +00:00
}
}
2015-05-11 21:53:52 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2014-05-19 18:01:55 +00:00
2015-04-16 21:26:33 +00:00
if boolValue ( r , "forcerm" ) && version . GreaterThanOrEqualTo ( "1.12" ) {
buildConfig . Remove = true
2014-05-19 18:01:55 +00:00
} else if r . FormValue ( "rm" ) == "" && version . GreaterThanOrEqualTo ( "1.12" ) {
2015-04-16 21:26:33 +00:00
buildConfig . Remove = true
2014-05-16 11:47:33 +00:00
} else {
2015-04-16 21:26:33 +00:00
buildConfig . Remove = boolValue ( r , "rm" )
2014-05-16 11:47:33 +00:00
}
2015-04-16 21:26:33 +00:00
if boolValue ( r , "pull" ) && version . GreaterThanOrEqualTo ( "1.16" ) {
buildConfig . Pull = true
2014-11-21 17:51:32 +00:00
}
2015-04-16 18:11:26 +00:00
2015-05-08 18:33:33 +00:00
output := ioutils . NewWriteFlusher ( w )
2015-04-20 21:18:14 +00:00
buildConfig . Stdout = output
2015-04-16 21:26:33 +00: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" )
2015-06-23 19:58:17 +00:00
buildConfig . AuthConfigs = authConfigs
2015-04-23 07:40:23 +00:00
buildConfig . MemorySwap = int64ValueOrZero ( r , "memswap" )
buildConfig . Memory = int64ValueOrZero ( r , "memory" )
buildConfig . CpuShares = int64ValueOrZero ( r , "cpushares" )
2015-04-08 08:58:59 +00:00
buildConfig . CpuPeriod = int64ValueOrZero ( r , "cpuperiod" )
2015-04-23 07:40:23 +00:00
buildConfig . CpuQuota = int64ValueOrZero ( r , "cpuquota" )
2015-04-16 21:26:33 +00: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 20:19:52 +00: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 18:11:26 +00:00
logrus . Infof ( "Client disconnected, cancelling job: build" )
2015-04-16 21:26:33 +00:00
buildConfig . Cancel ( )
2015-03-10 22:10:00 +00:00
}
} ( )
}
2015-04-21 21:23:48 +00:00
if err := builder . Build ( s . daemon , buildConfig ) ; err != nil {
2015-04-20 21:18:14 +00: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 20:19:52 +00:00
return err
2013-10-12 12:14:52 +00:00
}
2015-05-12 18:18:54 +00:00
sf := streamformatter . NewJSONStreamFormatter ( )
2014-01-23 20:19:52 +00:00
w . Write ( sf . FormatError ( err ) )
2013-05-23 03:07:26 +00:00
}
return nil
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) postContainersCopy ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-07-17 04:07:41 +00:00
if vars == nil {
return fmt . Errorf ( "Missing parameter" )
}
2014-09-25 15:24:41 +00:00
if err := checkForJson ( r ) ; err != nil {
return err
}
2015-04-09 20:05:31 +00:00
cfg := types . CopyConfig { }
if err := json . NewDecoder ( r . Body ) . Decode ( & cfg ) ; err != nil {
2014-09-25 15:24:41 +00:00
return err
2013-07-17 04:07:41 +00:00
}
2015-04-09 20:05:31 +00:00
if cfg . Resource == "" {
2013-11-16 10:15:04 +00:00
return fmt . Errorf ( "Path cannot be empty" )
2013-07-17 04:07:41 +00:00
}
2014-03-16 17:48:46 +00:00
2015-05-02 01:03:35 +00:00
data , err := s . daemon . ContainerCopy ( vars [ "name" ] , cfg . Resource )
2015-04-09 20:05:31 +00:00
if err != nil {
2014-12-16 23:06:35 +00: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 20:05:31 +00: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 20:05:31 +00:00
return err
2013-07-17 04:07:41 +00:00
}
2015-04-09 20:05:31 +00:00
defer data . Close ( )
2015-05-02 01:03:35 +00:00
2015-04-09 20:05:31 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/x-tar" )
if _ , err := io . Copy ( w , data ) ; err != nil {
return err
}
2013-07-17 04:07:41 +00:00
return nil
}
2015-04-27 21:11:29 +00: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 {
2015-05-27 10:16:28 +00:00
return err
2014-09-09 05:51:53 +00:00
}
2015-04-17 05:36:23 +00:00
name := vars [ "name" ]
2014-09-25 15:24:41 +00:00
2015-04-17 05:36:23 +00: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 05:36:23 +00: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 05:36:23 +00: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 21:03:54 +00:00
return writeJSON ( w , http . StatusCreated , & types . ContainerExecCreateResponse {
2015-04-17 05:36:23 +00:00
ID : id ,
2015-03-23 21:03:54 +00: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 21:11:29 +00: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 {
2015-05-27 10:16:28 +00:00
return err
2014-09-15 22:56:47 +00:00
}
var (
2015-04-17 05:36:23 +00:00
execName = vars [ "name" ]
stdin io . ReadCloser
stdout io . Writer
stderr io . Writer
2014-09-09 05:51:53 +00:00
)
2014-09-25 15:24:41 +00:00
2015-04-17 05:36:23 +00: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 05:36:23 +00: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 19:21:21 +00:00
defer closeStreams ( inStream , outStream )
2014-09-09 05:51:53 +00:00
var errStream io . Writer
2014-12-15 18:57:39 +00: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-05-11 21:53:52 +00:00
if ! execStartCheck . Tty {
2014-09-17 15:04:56 +00:00
errStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stderr )
outStream = stdcopy . NewStdWriter ( outStream , stdcopy . Stdout )
2014-09-09 05:51:53 +00:00
}
2015-05-11 21:53:52 +00:00
2015-04-17 05:36:23 +00: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 05:36:23 +00: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 21:11:29 +00: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 21:51:22 +00:00
height , err := strconv . Atoi ( r . Form . Get ( "h" ) )
if err != nil {
2015-04-13 06:36:04 +00:00
return err
2015-04-09 21:51:22 +00:00
}
width , err := strconv . Atoi ( r . Form . Get ( "w" ) )
if err != nil {
2015-04-13 06:36:04 +00:00
return err
2015-04-09 21:51:22 +00:00
}
2015-04-17 22:18:28 +00:00
return s . daemon . ContainerExecResize ( vars [ "name" ] , height , width )
2014-09-15 22:56:47 +00:00
}
2015-04-27 21:11:29 +00:00
func ( s * Server ) optionsHandler ( version version . Version , w http . ResponseWriter , r * http . Request , vars map [ string ] string ) error {
2013-06-11 01:10:40 +00:00
w . WriteHeader ( http . StatusOK )
return nil
}
2015-02-09 07:15:07 +00:00
func writeCorsHeaders ( w http . ResponseWriter , r * http . Request , corsHeaders string ) {
2015-03-26 22:22:04 +00:00
logrus . Debugf ( "CORS header is enabled and set to: %s" , corsHeaders )
2015-02-09 07:15:07 +00:00
w . Header ( ) . Add ( "Access-Control-Allow-Origin" , corsHeaders )
2014-11-01 16:22:28 +00:00
w . Header ( ) . Add ( "Access-Control-Allow-Headers" , "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth" )
2013-06-10 02:17:35 +00:00
w . Header ( ) . Add ( "Access-Control-Allow-Methods" , "GET, POST, DELETE, PUT, OPTIONS" )
2013-06-04 01:39:00 +00:00
}
2015-04-27 21:11:29 +00: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-05-20 23:48:39 +00:00
func ( s * Server ) initTcpSocket ( addr string ) ( l net . Listener , err error ) {
2015-05-07 16:49:07 +00:00
if s . cfg . TLSConfig == nil || s . cfg . TLSConfig . ClientAuth != tls . RequireAndVerifyClientCert {
2015-05-20 23:48:39 +00:00
logrus . Warn ( "/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\" )
}
2015-05-07 16:49:07 +00:00
if l , err = sockets . NewTcpSocket ( addr , s . cfg . TLSConfig , s . start ) ; err != nil {
2015-05-20 23:48:39 +00:00
return nil , err
}
if err := allocateDaemonPort ( addr ) ; err != nil {
return nil , err
}
return
}
2015-04-27 21:11:29 +00:00
func makeHttpHandler ( logging bool , localMethod string , localRoute string , handlerFunc HttpApiFunc , corsHeaders string , dockerVersion version . Version ) http . HandlerFunc {
2013-07-13 14:59:07 +00:00
return func ( w http . ResponseWriter , r * http . Request ) {
// log the request
2015-03-26 22:22:04 +00:00
logrus . Debugf ( "Calling %s %s" , localMethod , localRoute )
2013-07-13 14:59:07 +00:00
if logging {
2015-03-26 22:22:04 +00:00
logrus . Infof ( "%s %s" , r . Method , r . RequestURI )
2013-07-13 14:59:07 +00:00
}
if strings . Contains ( r . Header . Get ( "User-Agent" ) , "Docker-Client/" ) {
userAgent := strings . Split ( r . Header . Get ( "User-Agent" ) , "/" )
2015-06-04 17:29:57 +00:00
// v1.20 onwards includes the GOOS of the client after the version
// such as Docker/1.7.0 (linux)
if len ( userAgent ) == 2 && strings . Contains ( userAgent [ 1 ] , " " ) {
userAgent [ 1 ] = strings . Split ( userAgent [ 1 ] , " " ) [ 0 ]
}
2014-04-01 22:46:52 +00:00
if len ( userAgent ) == 2 && ! dockerVersion . Equal ( version . Version ( userAgent [ 1 ] ) ) {
2015-03-26 22:22:04 +00:00
logrus . Debugf ( "Warning: client and server don't have the same version (client: %s, server: %s)" , userAgent [ 1 ] , dockerVersion )
2013-07-13 14:59:07 +00: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 == "" {
2015-06-17 21:57:32 +00:00
version = api . Version
2013-07-13 14:59:07 +00:00
}
2015-02-09 07:15:07 +00:00
if corsHeaders != "" {
writeCorsHeaders ( w , r , corsHeaders )
2013-07-13 14:59:07 +00:00
}
2015-06-17 21:57:32 +00:00
if version . GreaterThan ( api . Version ) {
http . Error ( w , fmt . Errorf ( "client is newer than server (client API version: %s, server API version: %s)" , version , api . Version ) . Error ( ) , http . StatusBadRequest )
return
}
if version . LessThan ( api . MinVersion ) {
http . Error ( w , fmt . Errorf ( "client is too old, minimum supported API version is %s, please upgrade your client to a newer version" , api . MinVersion ) . Error ( ) , http . StatusBadRequest )
2013-07-13 14:59:07 +00:00
return
}
2015-06-04 13:30:14 +00:00
w . Header ( ) . Set ( "Server" , "Docker/" + dockerversion . VERSION + " (" + runtime . GOOS + ")" )
2015-04-27 21:11:29 +00:00
if err := handlerFunc ( version , w , r , mux . Vars ( r ) ) ; err != nil {
2015-03-26 22:22:04 +00:00
logrus . Errorf ( "Handler for %s %s returned error: %s" , localMethod , localRoute , err )
2013-07-13 14:59:07 +00:00
httpError ( w , err )
}
}
}
2015-02-09 07:15:07 +00:00
// we keep enableCors just for legacy usage, need to be removed in the future
2015-04-27 21:11:29 +00:00
func createRouter ( s * Server ) * mux . Router {
2013-05-07 23:33:12 +00:00
r := mux . NewRouter ( )
2013-11-19 19:25:17 +00:00
if os . Getenv ( "DEBUG" ) != "" {
2015-04-01 08:56:30 +00:00
ProfilerSetup ( r , "/debug/" )
2013-11-19 19:25:17 +00:00
}
2013-07-13 17:19:38 +00:00
m := map [ string ] map [ string ] HttpApiFunc {
2013-05-07 23:33:12 +00:00
"GET" : {
2015-04-17 22:18:28 +00: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 23:33:12 +00:00
} ,
"POST" : {
2015-04-17 22:18:28 +00: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 23:33:12 +00:00
} ,
"DELETE" : {
2015-04-17 22:18:28 +00:00
"/containers/{name:.*}" : s . deleteContainers ,
"/images/{name:.*}" : s . deleteImages ,
2013-05-07 23:33:12 +00:00
} ,
2013-06-11 01:10:40 +00:00
"OPTIONS" : {
2015-04-17 22:18:28 +00:00
"" : s . optionsHandler ,
2013-06-11 01:10:40 +00:00
} ,
2013-05-07 23:33:12 +00:00
}
2015-02-09 07:15:07 +00: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 22:18:28 +00:00
corsHeaders := s . cfg . CorsHeaders
if corsHeaders == "" && s . cfg . EnableCors {
2015-02-09 07:15:07 +00:00
corsHeaders = "*"
}
2013-05-07 23:33:12 +00:00
for method , routes := range m {
for route , fct := range routes {
2015-03-26 22:22:04 +00:00
logrus . Debugf ( "Registering %s, %s" , method , route )
2013-05-07 23:33:12 +00:00
// NOTE: scope issue, make sure the variables are local and won't be changed
localRoute := route
localFct := fct
2013-07-13 14:59:07 +00:00
localMethod := method
2013-06-21 01:18:36 +00:00
2013-07-13 14:59:07 +00:00
// build the handler function
2015-04-27 21:11:29 +00:00
f := makeHttpHandler ( s . cfg . Logging , localMethod , localRoute , localFct , corsHeaders , version . Version ( s . cfg . Version ) )
2013-06-11 01:10:40 +00:00
2013-07-13 14:59:07 +00:00
// add the new route
2013-06-11 01:10:40 +00: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 15:46:41 +00:00
}
2013-05-07 23:33:12 +00:00
}
2013-07-05 17:55:15 +00:00
2015-01-13 21:41:34 +00:00
return r
2013-06-10 22:02:40 +00:00
}