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