github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration-cli/docker_cli_cp_utils_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "runtime" 11 "strings" 12 "testing" 13 14 "github.com/Prakhar-Agarwal-byte/moby/integration-cli/cli" 15 "github.com/Prakhar-Agarwal-byte/moby/pkg/archive" 16 "gotest.tools/v3/assert" 17 ) 18 19 type fileType uint32 20 21 const ( 22 ftRegular fileType = iota 23 ftDir 24 ftSymlink 25 ) 26 27 type fileData struct { 28 filetype fileType 29 path string 30 contents string 31 uid int 32 gid int 33 mode int 34 } 35 36 func (fd fileData) creationCommand() string { 37 var command string 38 39 switch fd.filetype { 40 case ftRegular: 41 // Don't overwrite the file if it already exists! 42 command = fmt.Sprintf("if [ ! -f %s ]; then echo %q > %s; fi", fd.path, fd.contents, fd.path) 43 case ftDir: 44 command = fmt.Sprintf("mkdir -p %s", fd.path) 45 case ftSymlink: 46 command = fmt.Sprintf("ln -fs %s %s", fd.contents, fd.path) 47 } 48 49 return command 50 } 51 52 func mkFilesCommand(fds []fileData) string { 53 commands := make([]string, len(fds)) 54 55 for i, fd := range fds { 56 commands[i] = fd.creationCommand() 57 } 58 59 return strings.Join(commands, " && ") 60 } 61 62 var defaultFileData = []fileData{ 63 {ftRegular, "file1", "file1", 0, 0, 0o666}, 64 {ftRegular, "file2", "file2", 0, 0, 0o666}, 65 {ftRegular, "file3", "file3", 0, 0, 0o666}, 66 {ftRegular, "file4", "file4", 0, 0, 0o666}, 67 {ftRegular, "file5", "file5", 0, 0, 0o666}, 68 {ftRegular, "file6", "file6", 0, 0, 0o666}, 69 {ftRegular, "file7", "file7", 0, 0, 0o666}, 70 {ftDir, "dir1", "", 0, 0, 0o777}, 71 {ftRegular, "dir1/file1-1", "file1-1", 0, 0, 0o666}, 72 {ftRegular, "dir1/file1-2", "file1-2", 0, 0, 0o666}, 73 {ftDir, "dir2", "", 0, 0, 0o666}, 74 {ftRegular, "dir2/file2-1", "file2-1", 0, 0, 0o666}, 75 {ftRegular, "dir2/file2-2", "file2-2", 0, 0, 0o666}, 76 {ftDir, "dir3", "", 0, 0, 0o666}, 77 {ftRegular, "dir3/file3-1", "file3-1", 0, 0, 0o666}, 78 {ftRegular, "dir3/file3-2", "file3-2", 0, 0, 0o666}, 79 {ftDir, "dir4", "", 0, 0, 0o666}, 80 {ftRegular, "dir4/file3-1", "file4-1", 0, 0, 0o666}, 81 {ftRegular, "dir4/file3-2", "file4-2", 0, 0, 0o666}, 82 {ftDir, "dir5", "", 0, 0, 0o666}, 83 {ftSymlink, "symlinkToFile1", "file1", 0, 0, 0o666}, 84 {ftSymlink, "symlinkToDir1", "dir1", 0, 0, 0o666}, 85 {ftSymlink, "brokenSymlinkToFileX", "fileX", 0, 0, 0o666}, 86 {ftSymlink, "brokenSymlinkToDirX", "dirX", 0, 0, 0o666}, 87 {ftSymlink, "symlinkToAbsDir", "/root", 0, 0, 0o666}, 88 {ftDir, "permdirtest", "", 2, 2, 0o700}, 89 {ftRegular, "permdirtest/permtest", "perm_test", 65534, 65534, 0o400}, 90 } 91 92 func defaultMkContentCommand() string { 93 return mkFilesCommand(defaultFileData) 94 } 95 96 func makeTestContentInDir(c *testing.T, dir string) { 97 c.Helper() 98 for _, fd := range defaultFileData { 99 path := filepath.Join(dir, filepath.FromSlash(fd.path)) 100 switch fd.filetype { 101 case ftRegular: 102 assert.NilError(c, os.WriteFile(path, []byte(fd.contents+"\n"), os.FileMode(fd.mode))) 103 case ftDir: 104 assert.NilError(c, os.Mkdir(path, os.FileMode(fd.mode))) 105 case ftSymlink: 106 assert.NilError(c, os.Symlink(fd.contents, path)) 107 } 108 109 if fd.filetype != ftSymlink && runtime.GOOS != "windows" { 110 assert.NilError(c, os.Chown(path, fd.uid, fd.gid)) 111 } 112 } 113 } 114 115 type testContainerOptions struct { 116 addContent bool 117 readOnly bool 118 volumes []string 119 workDir string 120 command string 121 } 122 123 func makeTestContainer(c *testing.T, options testContainerOptions) (containerID string) { 124 c.Helper() 125 if options.addContent { 126 mkContentCmd := defaultMkContentCommand() 127 if options.command == "" { 128 options.command = mkContentCmd 129 } else { 130 options.command = fmt.Sprintf("%s && %s", defaultMkContentCommand(), options.command) 131 } 132 } 133 134 if options.command == "" { 135 options.command = "#(nop)" 136 } 137 138 args := []string{"run", "-d"} 139 140 for _, volume := range options.volumes { 141 args = append(args, "-v", volume) 142 } 143 144 if options.workDir != "" { 145 args = append(args, "-w", options.workDir) 146 } 147 148 if options.readOnly { 149 args = append(args, "--read-only") 150 } 151 152 args = append(args, "busybox", "/bin/sh", "-c", options.command) 153 154 out := cli.DockerCmd(c, args...).Combined() 155 156 containerID = strings.TrimSpace(out) 157 158 out = cli.DockerCmd(c, "wait", containerID).Combined() 159 160 exitCode := strings.TrimSpace(out) 161 if exitCode != "0" { 162 out = cli.DockerCmd(c, "logs", containerID).Combined() 163 } 164 assert.Equal(c, exitCode, "0", "failed to make test container: %s", out) 165 166 return 167 } 168 169 func makeCatFileCommand(path string) string { 170 return fmt.Sprintf("if [ -f %s ]; then cat %s; fi", path, path) 171 } 172 173 func cpPath(pathElements ...string) string { 174 localizedPathElements := make([]string, len(pathElements)) 175 for i, path := range pathElements { 176 localizedPathElements[i] = filepath.FromSlash(path) 177 } 178 return strings.Join(localizedPathElements, string(filepath.Separator)) 179 } 180 181 func cpPathTrailingSep(pathElements ...string) string { 182 return fmt.Sprintf("%s%c", cpPath(pathElements...), filepath.Separator) 183 } 184 185 func containerCpPath(containerID string, pathElements ...string) string { 186 joined := strings.Join(pathElements, "/") 187 return fmt.Sprintf("%s:%s", containerID, joined) 188 } 189 190 func containerCpPathTrailingSep(containerID string, pathElements ...string) string { 191 return fmt.Sprintf("%s/", containerCpPath(containerID, pathElements...)) 192 } 193 194 func runDockerCp(c *testing.T, src, dst string) error { 195 c.Helper() 196 197 args := []string{"cp", src, dst} 198 if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, args...)); err != nil { 199 return fmt.Errorf("error executing `docker cp` command: %s: %s", err, out) 200 } 201 return nil 202 } 203 204 func startContainerGetOutput(c *testing.T, containerID string) (out string, err error) { 205 c.Helper() 206 207 args := []string{"start", "-a", containerID} 208 209 out, _, err = runCommandWithOutput(exec.Command(dockerBinary, args...)) 210 if err != nil { 211 err = fmt.Errorf("error executing `docker start` command: %s: %s", err, out) 212 } 213 214 return 215 } 216 217 func getTestDir(c *testing.T, label string) (tmpDir string) { 218 c.Helper() 219 var err error 220 221 tmpDir, err = os.MkdirTemp("", label) 222 // unable to make temporary directory 223 assert.NilError(c, err) 224 225 return 226 } 227 228 func isCpDirNotExist(err error) bool { 229 return strings.Contains(err.Error(), archive.ErrDirNotExists.Error()) 230 } 231 232 func isCpCannotCopyDir(err error) bool { 233 return strings.Contains(err.Error(), archive.ErrCannotCopyDir.Error()) 234 } 235 236 func fileContentEquals(c *testing.T, filename, contents string) error { 237 c.Helper() 238 239 fileBytes, err := os.ReadFile(filename) 240 if err != nil { 241 return err 242 } 243 244 expectedBytes, err := io.ReadAll(strings.NewReader(contents)) 245 if err != nil { 246 return err 247 } 248 249 if !bytes.Equal(fileBytes, expectedBytes) { 250 return fmt.Errorf("file content not equal - expected %q, got %q", string(expectedBytes), string(fileBytes)) 251 } 252 253 return nil 254 } 255 256 func symlinkTargetEquals(c *testing.T, symlink, expectedTarget string) error { 257 c.Helper() 258 259 actualTarget, err := os.Readlink(symlink) 260 if err != nil { 261 return err 262 } 263 264 if actualTarget != expectedTarget { 265 return fmt.Errorf("symlink target points to %q not %q", actualTarget, expectedTarget) 266 } 267 268 return nil 269 } 270 271 func containerStartOutputEquals(c *testing.T, containerID, contents string) error { 272 c.Helper() 273 274 out, err := startContainerGetOutput(c, containerID) 275 if err != nil { 276 return err 277 } 278 279 if out != contents { 280 return fmt.Errorf("output contents not equal - expected %q, got %q", contents, out) 281 } 282 283 return nil 284 } 285 286 func defaultVolumes(tmpDir string) []string { 287 if testEnv.IsLocalDaemon() { 288 return []string{ 289 "/vol1", 290 fmt.Sprintf("%s:/vol2", tmpDir), 291 fmt.Sprintf("%s:/vol3", filepath.Join(tmpDir, "vol3")), 292 fmt.Sprintf("%s:/vol_ro:ro", filepath.Join(tmpDir, "vol_ro")), 293 } 294 } 295 296 // Can't bind-mount volumes with separate host daemon. 297 return []string{"/vol1", "/vol2", "/vol3", "/vol_ro:/vol_ro:ro"} 298 }