2015-05-14 16:44:29 -04:00
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
2016-11-14 08:37:08 -05:00
"runtime"
2015-05-14 16:44:29 -04:00
"strings"
2016-12-30 12:23:00 -05:00
"github.com/docker/docker/integration-cli/checker"
2015-05-14 16:44:29 -04:00
"github.com/docker/docker/pkg/archive"
"github.com/go-check/check"
)
2015-07-22 08:59:24 -04:00
type fileType uint32
2015-05-14 16:44:29 -04:00
const (
2015-07-22 08:59:24 -04:00
ftRegular fileType = iota
ftDir
ftSymlink
2015-05-14 16:44:29 -04:00
)
2015-07-22 08:59:24 -04:00
type fileData struct {
filetype fileType
2015-05-14 16:44:29 -04:00
path string
contents string
2016-11-14 08:37:08 -05:00
uid int
gid int
mode int
2015-05-14 16:44:29 -04:00
}
2015-07-22 08:59:24 -04:00
func ( fd fileData ) creationCommand ( ) string {
2015-05-14 16:44:29 -04:00
var command string
switch fd . filetype {
2015-07-22 08:59:24 -04:00
case ftRegular :
2015-05-14 16:44:29 -04:00
// Don't overwrite the file if it already exists!
command = fmt . Sprintf ( "if [ ! -f %s ]; then echo %q > %s; fi" , fd . path , fd . contents , fd . path )
2015-07-22 08:59:24 -04:00
case ftDir :
2015-05-14 16:44:29 -04:00
command = fmt . Sprintf ( "mkdir -p %s" , fd . path )
2015-07-22 08:59:24 -04:00
case ftSymlink :
2015-05-14 16:44:29 -04:00
command = fmt . Sprintf ( "ln -fs %s %s" , fd . contents , fd . path )
}
return command
}
2015-07-22 08:59:24 -04:00
func mkFilesCommand ( fds [ ] fileData ) string {
2015-05-14 16:44:29 -04:00
commands := make ( [ ] string , len ( fds ) )
for i , fd := range fds {
commands [ i ] = fd . creationCommand ( )
}
return strings . Join ( commands , " && " )
}
2015-07-22 08:59:24 -04:00
var defaultFileData = [ ] fileData {
2016-11-14 08:37:08 -05:00
{ ftRegular , "file1" , "file1" , 0 , 0 , 0666 } ,
{ ftRegular , "file2" , "file2" , 0 , 0 , 0666 } ,
{ ftRegular , "file3" , "file3" , 0 , 0 , 0666 } ,
{ ftRegular , "file4" , "file4" , 0 , 0 , 0666 } ,
{ ftRegular , "file5" , "file5" , 0 , 0 , 0666 } ,
{ ftRegular , "file6" , "file6" , 0 , 0 , 0666 } ,
{ ftRegular , "file7" , "file7" , 0 , 0 , 0666 } ,
{ ftDir , "dir1" , "" , 0 , 0 , 0777 } ,
{ ftRegular , "dir1/file1-1" , "file1-1" , 0 , 0 , 0666 } ,
{ ftRegular , "dir1/file1-2" , "file1-2" , 0 , 0 , 0666 } ,
{ ftDir , "dir2" , "" , 0 , 0 , 0666 } ,
{ ftRegular , "dir2/file2-1" , "file2-1" , 0 , 0 , 0666 } ,
{ ftRegular , "dir2/file2-2" , "file2-2" , 0 , 0 , 0666 } ,
{ ftDir , "dir3" , "" , 0 , 0 , 0666 } ,
{ ftRegular , "dir3/file3-1" , "file3-1" , 0 , 0 , 0666 } ,
{ ftRegular , "dir3/file3-2" , "file3-2" , 0 , 0 , 0666 } ,
{ ftDir , "dir4" , "" , 0 , 0 , 0666 } ,
{ ftRegular , "dir4/file3-1" , "file4-1" , 0 , 0 , 0666 } ,
{ ftRegular , "dir4/file3-2" , "file4-2" , 0 , 0 , 0666 } ,
{ ftDir , "dir5" , "" , 0 , 0 , 0666 } ,
{ ftSymlink , "symlinkToFile1" , "file1" , 0 , 0 , 0666 } ,
{ ftSymlink , "symlinkToDir1" , "dir1" , 0 , 0 , 0666 } ,
{ ftSymlink , "brokenSymlinkToFileX" , "fileX" , 0 , 0 , 0666 } ,
{ ftSymlink , "brokenSymlinkToDirX" , "dirX" , 0 , 0 , 0666 } ,
{ ftSymlink , "symlinkToAbsDir" , "/root" , 0 , 0 , 0666 } ,
{ ftDir , "permdirtest" , "" , 2 , 2 , 0700 } ,
{ ftRegular , "permdirtest/permtest" , "perm_test" , 65534 , 65534 , 0400 } ,
2015-05-14 16:44:29 -04:00
}
func defaultMkContentCommand ( ) string {
return mkFilesCommand ( defaultFileData )
}
func makeTestContentInDir ( c * check . C , dir string ) {
for _ , fd := range defaultFileData {
path := filepath . Join ( dir , filepath . FromSlash ( fd . path ) )
switch fd . filetype {
2015-07-22 08:59:24 -04:00
case ftRegular :
2016-11-14 08:37:08 -05:00
c . Assert ( ioutil . WriteFile ( path , [ ] byte ( fd . contents + "\n" ) , os . FileMode ( fd . mode ) ) , checker . IsNil )
2015-07-22 08:59:24 -04:00
case ftDir :
2016-11-14 08:37:08 -05:00
c . Assert ( os . Mkdir ( path , os . FileMode ( fd . mode ) ) , checker . IsNil )
2015-07-22 08:59:24 -04:00
case ftSymlink :
2015-10-22 21:19:33 -04:00
c . Assert ( os . Symlink ( fd . contents , path ) , checker . IsNil )
2015-05-14 16:44:29 -04:00
}
2016-11-14 08:37:08 -05:00
if fd . filetype != ftSymlink && runtime . GOOS != "windows" {
c . Assert ( os . Chown ( path , fd . uid , fd . gid ) , checker . IsNil )
}
2015-05-14 16:44:29 -04:00
}
}
type testContainerOptions struct {
addContent bool
readOnly bool
volumes [ ] string
workDir string
command string
}
func makeTestContainer ( c * check . C , options testContainerOptions ) ( containerID string ) {
if options . addContent {
mkContentCmd := defaultMkContentCommand ( )
if options . command == "" {
options . command = mkContentCmd
} else {
options . command = fmt . Sprintf ( "%s && %s" , defaultMkContentCommand ( ) , options . command )
}
}
if options . command == "" {
options . command = "#(nop)"
}
args := [ ] string { "run" , "-d" }
for _ , volume := range options . volumes {
args = append ( args , "-v" , volume )
}
if options . workDir != "" {
args = append ( args , "-w" , options . workDir )
}
if options . readOnly {
args = append ( args , "--read-only" )
}
args = append ( args , "busybox" , "/bin/sh" , "-c" , options . command )
2015-10-22 21:19:33 -04:00
out , _ := dockerCmd ( c , args ... )
2015-05-14 16:44:29 -04:00
containerID = strings . TrimSpace ( out )
2015-10-22 21:19:33 -04:00
out , _ = dockerCmd ( c , "wait" , containerID )
2015-05-14 16:44:29 -04:00
2015-10-22 21:19:33 -04:00
exitCode := strings . TrimSpace ( out )
if exitCode != "0" {
out , _ = dockerCmd ( c , "logs" , containerID )
2015-05-14 16:44:29 -04:00
}
2015-10-22 21:19:33 -04:00
c . Assert ( exitCode , checker . Equals , "0" , check . Commentf ( "failed to make test container: %s" , out ) )
2015-05-14 16:44:29 -04:00
return
}
func makeCatFileCommand ( path string ) string {
return fmt . Sprintf ( "if [ -f %s ]; then cat %s; fi" , path , path )
}
func cpPath ( pathElements ... string ) string {
localizedPathElements := make ( [ ] string , len ( pathElements ) )
for i , path := range pathElements {
localizedPathElements [ i ] = filepath . FromSlash ( path )
}
return strings . Join ( localizedPathElements , string ( filepath . Separator ) )
}
func cpPathTrailingSep ( pathElements ... string ) string {
return fmt . Sprintf ( "%s%c" , cpPath ( pathElements ... ) , filepath . Separator )
}
func containerCpPath ( containerID string , pathElements ... string ) string {
joined := strings . Join ( pathElements , "/" )
return fmt . Sprintf ( "%s:%s" , containerID , joined )
}
func containerCpPathTrailingSep ( containerID string , pathElements ... string ) string {
return fmt . Sprintf ( "%s/" , containerCpPath ( containerID , pathElements ... ) )
}
2016-11-14 08:37:08 -05:00
func runDockerCp ( c * check . C , src , dst string , params [ ] string ) ( err error ) {
c . Logf ( "running `docker cp %s %s %s`" , strings . Join ( params , " " ) , src , dst )
args := [ ] string { "cp" }
for _ , param := range params {
args = append ( args , param )
}
2015-05-14 16:44:29 -04:00
2016-11-14 08:37:08 -05:00
args = append ( args , src , dst )
2015-05-14 16:44:29 -04:00
out , _ , err := runCommandWithOutput ( exec . Command ( dockerBinary , args ... ) )
if err != nil {
err = fmt . Errorf ( "error executing `docker cp` command: %s: %s" , err , out )
}
return
}
2015-10-22 21:19:33 -04:00
func startContainerGetOutput ( c * check . C , containerID string ) ( out string , err error ) {
c . Logf ( "running `docker start -a %s`" , containerID )
2015-05-14 16:44:29 -04:00
2015-10-22 21:19:33 -04:00
args := [ ] string { "start" , "-a" , containerID }
2015-05-14 16:44:29 -04:00
out , _ , err = runCommandWithOutput ( exec . Command ( dockerBinary , args ... ) )
if err != nil {
err = fmt . Errorf ( "error executing `docker start` command: %s: %s" , err , out )
}
return
}
func getTestDir ( c * check . C , label string ) ( tmpDir string ) {
var err error
2015-10-22 21:19:33 -04:00
tmpDir , err = ioutil . TempDir ( "" , label )
// unable to make temporary directory
c . Assert ( err , checker . IsNil )
2015-05-14 16:44:29 -04:00
return
}
func isCpNotExist ( err error ) bool {
return strings . Contains ( err . Error ( ) , "no such file or directory" ) || strings . Contains ( err . Error ( ) , "cannot find the file specified" )
}
func isCpDirNotExist ( err error ) bool {
return strings . Contains ( err . Error ( ) , archive . ErrDirNotExists . Error ( ) )
}
func isCpNotDir ( err error ) bool {
return strings . Contains ( err . Error ( ) , archive . ErrNotDirectory . Error ( ) ) || strings . Contains ( err . Error ( ) , "filename, directory name, or volume label syntax is incorrect" )
}
func isCpCannotCopyDir ( err error ) bool {
return strings . Contains ( err . Error ( ) , archive . ErrCannotCopyDir . Error ( ) )
}
func isCpCannotCopyReadOnly ( err error ) bool {
return strings . Contains ( err . Error ( ) , "marked read-only" )
}
func isCannotOverwriteNonDirWithDir ( err error ) bool {
return strings . Contains ( err . Error ( ) , "cannot overwrite non-directory" )
}
func fileContentEquals ( c * check . C , filename , contents string ) ( err error ) {
c . Logf ( "checking that file %q contains %q\n" , filename , contents )
fileBytes , err := ioutil . ReadFile ( filename )
if err != nil {
return
}
expectedBytes , err := ioutil . ReadAll ( strings . NewReader ( contents ) )
if err != nil {
return
}
if ! bytes . Equal ( fileBytes , expectedBytes ) {
err = fmt . Errorf ( "file content not equal - expected %q, got %q" , string ( expectedBytes ) , string ( fileBytes ) )
}
return
}
2015-07-24 17:12:55 -04:00
func symlinkTargetEquals ( c * check . C , symlink , expectedTarget string ) ( err error ) {
c . Logf ( "checking that the symlink %q points to %q\n" , symlink , expectedTarget )
actualTarget , err := os . Readlink ( symlink )
if err != nil {
2015-10-22 21:19:33 -04:00
return
2015-07-24 17:12:55 -04:00
}
if actualTarget != expectedTarget {
2015-10-22 21:19:33 -04:00
err = fmt . Errorf ( "symlink target points to %q not %q" , actualTarget , expectedTarget )
2015-07-24 17:12:55 -04:00
}
2015-10-22 21:19:33 -04:00
return
2015-07-24 17:12:55 -04:00
}
2015-10-22 21:19:33 -04:00
func containerStartOutputEquals ( c * check . C , containerID , contents string ) ( err error ) {
c . Logf ( "checking that container %q start output contains %q\n" , containerID , contents )
2015-05-14 16:44:29 -04:00
2015-10-22 21:19:33 -04:00
out , err := startContainerGetOutput ( c , containerID )
2015-05-14 16:44:29 -04:00
if err != nil {
2015-10-22 21:19:33 -04:00
return
2015-05-14 16:44:29 -04:00
}
if out != contents {
err = fmt . Errorf ( "output contents not equal - expected %q, got %q" , contents , out )
}
return
}
func defaultVolumes ( tmpDir string ) [ ] string {
2016-12-16 09:13:23 -05:00
if SameHostDaemon ( ) {
2015-05-14 16:44:29 -04:00
return [ ] string {
"/vol1" ,
fmt . Sprintf ( "%s:/vol2" , tmpDir ) ,
fmt . Sprintf ( "%s:/vol3" , filepath . Join ( tmpDir , "vol3" ) ) ,
fmt . Sprintf ( "%s:/vol_ro:ro" , filepath . Join ( tmpDir , "vol_ro" ) ) ,
}
}
// Can't bind-mount volumes with separate host daemon.
return [ ] string { "/vol1" , "/vol2" , "/vol3" , "/vol_ro:/vol_ro:ro" }
}