2014-12-01 12:16:49 -05:00
package main
import (
"bytes"
2018-04-19 18:30:59 -04:00
"context"
2015-07-02 02:57:44 -04:00
"encoding/json"
2014-12-01 12:16:49 -05:00
"fmt"
2021-08-24 06:10:50 -04:00
"io"
2015-04-20 17:03:56 -04:00
"net/http"
2017-11-13 17:53:56 -05:00
"os"
"strings"
2019-09-09 17:06:12 -04:00
"testing"
2016-01-15 11:57:23 -05:00
"time"
2015-04-18 12:46:47 -04:00
2017-05-23 23:56:26 -04:00
"github.com/docker/docker/api/types"
2018-05-04 17:15:00 -04:00
"github.com/docker/docker/api/types/versions"
2017-05-23 23:56:26 -04:00
"github.com/docker/docker/client"
2016-12-30 12:23:00 -05:00
"github.com/docker/docker/integration-cli/checker"
2019-08-29 16:52:40 -04:00
"github.com/docker/docker/testutil/request"
2020-02-07 08:39:24 -05:00
"gotest.tools/v3/assert"
2022-04-05 05:43:06 -04:00
is "gotest.tools/v3/assert/cmp"
2020-02-07 08:39:24 -05:00
"gotest.tools/v3/poll"
2014-12-01 12:16:49 -05:00
)
// Regression test for #9414
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecAPICreateNoCmd ( c * testing . T ) {
2014-12-01 12:16:49 -05:00
name := "exec_test"
2015-07-14 02:35:36 -04:00
dockerCmd ( c , "run" , "-d" , "-t" , "--name" , name , "busybox" , "/bin/sh" )
2014-12-01 12:16:49 -05:00
2017-05-23 23:56:26 -04:00
res , body , err := request . Post ( fmt . Sprintf ( "/containers/%s/exec" , name ) , request . JSONBody ( map [ string ] interface { } { "Cmd" : nil } ) )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2018-05-04 17:15:00 -04:00
if versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.32" ) {
2019-04-04 09:23:19 -04:00
assert . Equal ( c , res . StatusCode , http . StatusInternalServerError )
2018-05-04 17:15:00 -04:00
} else {
2019-04-04 09:23:19 -04:00
assert . Equal ( c , res . StatusCode , http . StatusBadRequest )
2018-05-04 17:15:00 -04:00
}
2017-05-23 23:56:26 -04:00
b , err := request . ReadBody ( body )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
assert . Assert ( c , strings . Contains ( getErrorMessage ( c , b ) , "No exec command specified" ) , "Expected message when creating exec command with no Cmd specified" )
2014-12-01 12:16:49 -05:00
}
2015-07-02 02:57:44 -04:00
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecAPICreateNoValidContentType ( c * testing . T ) {
2015-07-02 02:57:44 -04:00
name := "exec_test"
dockerCmd ( c , "run" , "-d" , "-t" , "--name" , name , "busybox" , "/bin/sh" )
jsonData := bytes . NewBuffer ( nil )
if err := json . NewEncoder ( jsonData ) . Encode ( map [ string ] interface { } { "Cmd" : nil } ) ; err != nil {
c . Fatalf ( "Can not encode data to json %s" , err )
}
2021-08-24 06:10:50 -04:00
res , body , err := request . Post ( fmt . Sprintf ( "/containers/%s/exec" , name ) , request . RawContent ( io . NopCloser ( jsonData ) ) , request . ContentType ( "test/plain" ) )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2018-05-04 17:15:00 -04:00
if versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.32" ) {
2019-04-04 09:23:19 -04:00
assert . Equal ( c , res . StatusCode , http . StatusInternalServerError )
2018-05-04 17:15:00 -04:00
} else {
2019-04-04 09:23:19 -04:00
assert . Equal ( c , res . StatusCode , http . StatusBadRequest )
2018-05-04 17:15:00 -04:00
}
2017-08-21 18:50:40 -04:00
b , err := request . ReadBody ( body )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2022-04-05 05:43:06 -04:00
assert . Assert ( c , is . Contains ( getErrorMessage ( c , b ) , "unsupported Content-Type header (test/plain): must be 'application/json'" ) )
2015-07-02 02:57:44 -04:00
}
2015-09-11 22:50:21 -04:00
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecAPICreateContainerPaused ( c * testing . T ) {
2016-01-28 19:26:06 -05:00
// Not relevant on Windows as Windows containers cannot be paused
2015-11-19 00:51:26 -05:00
testRequires ( c , DaemonIsLinux )
name := "exec_create_test"
dockerCmd ( c , "run" , "-d" , "-t" , "--name" , name , "busybox" , "/bin/sh" )
dockerCmd ( c , "pause" , name )
2017-05-23 23:56:26 -04:00
2019-01-03 16:49:00 -05:00
cli , err := client . NewClientWithOpts ( client . FromEnv )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2017-05-23 23:56:26 -04:00
defer cli . Close ( )
config := types . ExecConfig {
Cmd : [ ] string { "true" } ,
}
_ , err = cli . ContainerExecCreate ( context . Background ( ) , name , config )
2019-04-04 09:23:19 -04:00
assert . ErrorContains ( c , err , "Container " + name + " is paused, unpause the container before exec" , "Expected message when creating exec command with Container %s is paused" , name )
2015-11-19 00:51:26 -05:00
}
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecAPIStart ( c * testing . T ) {
2017-01-16 23:45:27 -05:00
testRequires ( c , DaemonIsLinux ) // Uses pause/unpause but bits may be salvageable to Windows to Windows CI
2015-09-11 22:50:21 -04:00
dockerCmd ( c , "run" , "-d" , "--name" , "test" , "busybox" , "top" )
2015-10-23 12:48:55 -04:00
id := createExec ( c , "test" )
2016-01-15 11:57:23 -05:00
startExec ( c , id , http . StatusOK )
2015-09-11 22:50:21 -04:00
2016-10-17 13:49:36 -04:00
var execJSON struct { PID int }
inspectExec ( c , id , & execJSON )
2019-04-04 09:23:19 -04:00
assert . Assert ( c , execJSON . PID > 1 )
2016-10-17 13:49:36 -04:00
2015-10-23 12:48:55 -04:00
id = createExec ( c , "test" )
2015-09-11 22:50:21 -04:00
dockerCmd ( c , "stop" , "test" )
2016-01-15 11:57:23 -05:00
startExec ( c , id , http . StatusNotFound )
2015-09-11 22:50:21 -04:00
dockerCmd ( c , "start" , "test" )
2016-01-15 11:57:23 -05:00
startExec ( c , id , http . StatusNotFound )
2015-09-11 22:50:21 -04:00
// make sure exec is created before pausing
2015-10-23 12:48:55 -04:00
id = createExec ( c , "test" )
2015-09-11 22:50:21 -04:00
dockerCmd ( c , "pause" , "test" )
2016-01-15 11:57:23 -05:00
startExec ( c , id , http . StatusConflict )
2015-09-11 22:50:21 -04:00
dockerCmd ( c , "unpause" , "test" )
2016-01-15 11:57:23 -05:00
startExec ( c , id , http . StatusOK )
2015-09-11 22:50:21 -04:00
}
2015-10-23 12:48:55 -04:00
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecAPIStartEnsureHeaders ( c * testing . T ) {
2016-12-05 10:58:53 -05:00
testRequires ( c , DaemonIsLinux )
dockerCmd ( c , "run" , "-d" , "--name" , "test" , "busybox" , "top" )
id := createExec ( c , "test" )
2017-03-06 10:35:27 -05:00
resp , _ , err := request . Post ( fmt . Sprintf ( "/exec/%s/start" , id ) , request . RawString ( ` { "Detach": true} ` ) , request . JSON )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
assert . Assert ( c , resp . Header . Get ( "Server" ) != "" )
2016-12-05 10:58:53 -05:00
}
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecAPIStartBackwardsCompatible ( c * testing . T ) {
2016-10-31 13:15:43 -04:00
testRequires ( c , DaemonIsLinux ) // Windows only supports 1.25 or later
2016-01-28 19:26:06 -05:00
runSleepingContainer ( c , "-d" , "--name" , "test" )
2015-10-23 12:48:55 -04:00
id := createExec ( c , "test" )
2017-03-06 10:35:27 -05:00
resp , body , err := request . Post ( fmt . Sprintf ( "/v1.20/exec/%s/start" , id ) , request . RawString ( ` { "Detach": true} ` ) , request . ContentType ( "text/plain" ) )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2015-10-23 12:48:55 -04:00
2017-08-21 18:50:40 -04:00
b , err := request . ReadBody ( body )
2019-09-09 17:08:22 -04:00
comment := fmt . Sprintf ( "response body: %s" , b )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err , comment )
assert . Equal ( c , resp . StatusCode , http . StatusOK , comment )
2015-10-23 12:48:55 -04:00
}
2016-01-15 11:57:23 -05:00
// #19362
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecAPIStartMultipleTimesError ( c * testing . T ) {
2016-01-28 19:26:06 -05:00
runSleepingContainer ( c , "-d" , "--name" , "test" )
2016-01-15 11:57:23 -05:00
execID := createExec ( c , "test" )
startExec ( c , execID , http . StatusOK )
2017-01-21 06:35:54 -05:00
waitForExec ( c , execID )
2016-01-15 11:57:23 -05:00
startExec ( c , execID , http . StatusConflict )
}
2016-02-24 21:04:44 -05:00
// #20638
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecAPIStartWithDetach ( c * testing . T ) {
2016-02-24 21:04:44 -05:00
name := "foo"
2016-02-25 08:27:22 -05:00
runSleepingContainer ( c , "-d" , "-t" , "--name" , name )
2017-05-23 23:56:26 -04:00
config := types . ExecConfig {
Cmd : [ ] string { "true" } ,
AttachStderr : true ,
2016-02-24 21:04:44 -05:00
}
2019-01-03 16:49:00 -05:00
cli , err := client . NewClientWithOpts ( client . FromEnv )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2017-05-23 23:56:26 -04:00
defer cli . Close ( )
createResp , err := cli . ContainerExecCreate ( context . Background ( ) , name , config )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2016-02-24 21:04:44 -05:00
2017-03-06 10:35:27 -05:00
_ , body , err := request . Post ( fmt . Sprintf ( "/exec/%s/start" , createResp . ID ) , request . RawString ( ` { "Detach": true} ` ) , request . JSON )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2016-02-24 21:04:44 -05:00
2017-05-23 23:56:26 -04:00
b , err := request . ReadBody ( body )
2019-09-09 17:08:22 -04:00
comment := fmt . Sprintf ( "response body: %s" , b )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err , comment )
2016-02-24 21:04:44 -05:00
2017-03-06 10:35:27 -05:00
resp , _ , err := request . Get ( "/_ping" )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2016-02-24 21:04:44 -05:00
if resp . StatusCode != http . StatusOK {
c . Fatal ( "daemon is down, it should alive" )
}
}
2017-01-21 06:35:54 -05:00
// #30311
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecAPIStartValidCommand ( c * testing . T ) {
2017-01-21 06:35:54 -05:00
name := "exec_test"
dockerCmd ( c , "run" , "-d" , "-t" , "--name" , name , "busybox" , "/bin/sh" )
id := createExecCmd ( c , name , "true" )
startExec ( c , id , http . StatusOK )
waitForExec ( c , id )
var inspectJSON struct { ExecIDs [ ] string }
inspectContainer ( c , name , & inspectJSON )
2019-04-04 09:23:19 -04:00
assert . Assert ( c , inspectJSON . ExecIDs == nil )
2017-01-21 06:35:54 -05:00
}
// #30311
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecAPIStartInvalidCommand ( c * testing . T ) {
2017-01-21 06:35:54 -05:00
name := "exec_test"
dockerCmd ( c , "run" , "-d" , "-t" , "--name" , name , "busybox" , "/bin/sh" )
id := createExecCmd ( c , name , "invalid" )
2018-05-04 17:15:00 -04:00
if versions . LessThan ( testEnv . DaemonAPIVersion ( ) , "1.32" ) {
startExec ( c , id , http . StatusNotFound )
} else {
startExec ( c , id , http . StatusBadRequest )
}
2017-01-21 06:35:54 -05:00
waitForExec ( c , id )
var inspectJSON struct { ExecIDs [ ] string }
inspectContainer ( c , name , & inspectJSON )
2019-04-04 09:23:19 -04:00
assert . Assert ( c , inspectJSON . ExecIDs == nil )
2017-01-21 06:35:54 -05:00
}
2019-09-09 17:05:55 -04:00
func ( s * DockerSuite ) TestExecStateCleanup ( c * testing . T ) {
2018-12-24 07:25:53 -05:00
testRequires ( c , DaemonIsLinux , testEnv . IsLocalDaemon )
2017-11-13 17:53:56 -05:00
// This test checks accidental regressions. Not part of stable API.
name := "exec_cleanup"
cid , _ := dockerCmd ( c , "run" , "-d" , "-t" , "--name" , name , "busybox" , "/bin/sh" )
cid = strings . TrimSpace ( cid )
stateDir := "/var/run/docker/containerd/" + cid
2019-09-09 17:08:22 -04:00
checkReadDir := func ( c * testing . T ) ( interface { } , string ) {
2021-08-24 06:10:50 -04:00
fi , err := os . ReadDir ( stateDir )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2019-09-09 17:09:27 -04:00
return len ( fi ) , ""
2017-11-13 17:53:56 -05:00
}
2021-08-24 06:10:50 -04:00
fi , err := os . ReadDir ( stateDir )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
assert . Assert ( c , len ( fi ) > 1 )
2017-11-13 17:53:56 -05:00
id := createExecCmd ( c , name , "ls" )
startExec ( c , id , http . StatusOK )
waitForExec ( c , id )
2019-08-26 11:51:40 -04:00
poll . WaitOn ( c , pollCheck ( c , checkReadDir , checker . Equals ( len ( fi ) ) ) , poll . WithTimeout ( 5 * time . Second ) )
2017-11-13 17:53:56 -05:00
id = createExecCmd ( c , name , "invalid" )
startExec ( c , id , http . StatusBadRequest )
waitForExec ( c , id )
2019-08-26 11:51:40 -04:00
poll . WaitOn ( c , pollCheck ( c , checkReadDir , checker . Equals ( len ( fi ) ) ) , poll . WithTimeout ( 5 * time . Second ) )
2017-11-13 17:53:56 -05:00
dockerCmd ( c , "stop" , name )
_ , err = os . Stat ( stateDir )
2019-04-04 09:23:19 -04:00
assert . ErrorContains ( c , err , "" )
assert . Assert ( c , os . IsNotExist ( err ) )
2017-11-13 17:53:56 -05:00
}
2019-09-09 17:05:55 -04:00
func createExec ( c * testing . T , name string ) string {
2017-01-21 06:35:54 -05:00
return createExecCmd ( c , name , "true" )
}
2019-09-09 17:05:55 -04:00
func createExecCmd ( c * testing . T , name string , cmd string ) string {
2017-03-06 10:35:27 -05:00
_ , reader , err := request . Post ( fmt . Sprintf ( "/containers/%s/exec" , name ) , request . JSONBody ( map [ string ] interface { } { "Cmd" : [ ] string { cmd } } ) )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2021-08-24 06:10:50 -04:00
b , err := io . ReadAll ( reader )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2017-02-28 11:12:30 -05:00
defer reader . Close ( )
2015-10-23 12:48:55 -04:00
createResp := struct {
ID string ` json:"Id" `
} { }
2019-04-04 09:23:19 -04:00
assert . NilError ( c , json . Unmarshal ( b , & createResp ) , string ( b ) )
2015-10-23 12:48:55 -04:00
return createResp . ID
}
2016-01-15 11:57:23 -05:00
2019-09-09 17:05:55 -04:00
func startExec ( c * testing . T , id string , code int ) {
2017-03-06 10:35:27 -05:00
resp , body , err := request . Post ( fmt . Sprintf ( "/exec/%s/start" , id ) , request . RawString ( ` { "Detach": true} ` ) , request . JSON )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2016-01-15 11:57:23 -05:00
2017-08-21 18:50:40 -04:00
b , err := request . ReadBody ( body )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err , "response body: %s" , b )
assert . Equal ( c , resp . StatusCode , code , "response body: %s" , b )
2016-01-15 11:57:23 -05:00
}
2019-09-09 17:05:55 -04:00
func inspectExec ( c * testing . T , id string , out interface { } ) {
2017-03-06 10:35:27 -05:00
resp , body , err := request . Get ( fmt . Sprintf ( "/exec/%s/json" , id ) )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2016-01-15 11:57:23 -05:00
defer body . Close ( )
2019-04-04 09:23:19 -04:00
assert . Equal ( c , resp . StatusCode , http . StatusOK )
2016-01-15 11:57:23 -05:00
err = json . NewDecoder ( body ) . Decode ( out )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2016-01-15 11:57:23 -05:00
}
2017-01-21 06:35:54 -05:00
2019-09-09 17:05:55 -04:00
func waitForExec ( c * testing . T , id string ) {
2017-01-21 06:35:54 -05:00
timeout := time . After ( 60 * time . Second )
var execJSON struct { Running bool }
for {
select {
case <- timeout :
c . Fatal ( "timeout waiting for exec to start" )
default :
}
inspectExec ( c , id , & execJSON )
if ! execJSON . Running {
break
}
}
}
2019-09-09 17:05:55 -04:00
func inspectContainer ( c * testing . T , id string , out interface { } ) {
2021-02-11 15:45:16 -05:00
resp , body , err := request . Get ( "/containers/" + id + "/json" )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2017-01-21 06:35:54 -05:00
defer body . Close ( )
2019-04-04 09:23:19 -04:00
assert . Equal ( c , resp . StatusCode , http . StatusOK )
2017-01-21 06:35:54 -05:00
err = json . NewDecoder ( body ) . Decode ( out )
2019-04-04 09:23:19 -04:00
assert . NilError ( c , err )
2017-01-21 06:35:54 -05:00
}