docker_utils.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "io/ioutil"
  6. "net/http"
  7. "net/http/httptest"
  8. "os"
  9. "os/exec"
  10. "path"
  11. "path/filepath"
  12. "strconv"
  13. "strings"
  14. "testing"
  15. )
  16. func deleteContainer(container string) error {
  17. container = strings.Replace(container, "\n", " ", -1)
  18. container = strings.Trim(container, " ")
  19. killArgs := fmt.Sprintf("kill %v", container)
  20. killSplitArgs := strings.Split(killArgs, " ")
  21. killCmd := exec.Command(dockerBinary, killSplitArgs...)
  22. runCommand(killCmd)
  23. rmArgs := fmt.Sprintf("rm %v", container)
  24. rmSplitArgs := strings.Split(rmArgs, " ")
  25. rmCmd := exec.Command(dockerBinary, rmSplitArgs...)
  26. exitCode, err := runCommand(rmCmd)
  27. // set error manually if not set
  28. if exitCode != 0 && err == nil {
  29. err = fmt.Errorf("failed to remove container: `docker rm` exit is non-zero")
  30. }
  31. return err
  32. }
  33. func getAllContainers() (string, error) {
  34. getContainersCmd := exec.Command(dockerBinary, "ps", "-q", "-a")
  35. out, exitCode, err := runCommandWithOutput(getContainersCmd)
  36. if exitCode != 0 && err == nil {
  37. err = fmt.Errorf("failed to get a list of containers: %v\n", out)
  38. }
  39. return out, err
  40. }
  41. func deleteAllContainers() error {
  42. containers, err := getAllContainers()
  43. if err != nil {
  44. fmt.Println(containers)
  45. return err
  46. }
  47. if err = deleteContainer(containers); err != nil {
  48. return err
  49. }
  50. return nil
  51. }
  52. func deleteImages(images string) error {
  53. rmiCmd := exec.Command(dockerBinary, "rmi", images)
  54. exitCode, err := runCommand(rmiCmd)
  55. // set error manually if not set
  56. if exitCode != 0 && err == nil {
  57. err = fmt.Errorf("failed to remove image: `docker rmi` exit is non-zero")
  58. }
  59. return err
  60. }
  61. func imageExists(image string) error {
  62. inspectCmd := exec.Command(dockerBinary, "inspect", image)
  63. exitCode, err := runCommand(inspectCmd)
  64. if exitCode != 0 && err == nil {
  65. err = fmt.Errorf("couldn't find image '%s'", image)
  66. }
  67. return err
  68. }
  69. func pullImageIfNotExist(image string) (err error) {
  70. if err := imageExists(image); err != nil {
  71. pullCmd := exec.Command(dockerBinary, "pull", image)
  72. _, exitCode, err := runCommandWithOutput(pullCmd)
  73. if err != nil || exitCode != 0 {
  74. err = fmt.Errorf("image '%s' wasn't found locally and it couldn't be pulled: %s", image, err)
  75. }
  76. }
  77. return
  78. }
  79. // deprecated, use dockerCmd instead
  80. func cmd(t *testing.T, args ...string) (string, int, error) {
  81. return dockerCmd(t, args...)
  82. }
  83. func dockerCmd(t *testing.T, args ...string) (string, int, error) {
  84. out, status, err := runCommandWithOutput(exec.Command(dockerBinary, args...))
  85. errorOut(err, t, fmt.Sprintf("'%s' failed with errors: %v (%v)", strings.Join(args, " "), err, out))
  86. return out, status, err
  87. }
  88. // execute a docker command in a directory
  89. func dockerCmdInDir(t *testing.T, path string, args ...string) (string, int, error) {
  90. dockerCommand := exec.Command(dockerBinary, args...)
  91. dockerCommand.Dir = path
  92. out, status, err := runCommandWithOutput(dockerCommand)
  93. errorOut(err, t, fmt.Sprintf("'%s' failed with errors: %v (%v)", strings.Join(args, " "), err, out))
  94. return out, status, err
  95. }
  96. func findContainerIp(t *testing.T, id string) string {
  97. cmd := exec.Command(dockerBinary, "inspect", "--format='{{ .NetworkSettings.IPAddress }}'", id)
  98. out, _, err := runCommandWithOutput(cmd)
  99. if err != nil {
  100. t.Fatal(err, out)
  101. }
  102. return strings.Trim(out, " \r\n'")
  103. }
  104. func getContainerCount() (int, error) {
  105. const containers = "Containers:"
  106. cmd := exec.Command(dockerBinary, "info")
  107. out, _, err := runCommandWithOutput(cmd)
  108. if err != nil {
  109. return 0, err
  110. }
  111. lines := strings.Split(out, "\n")
  112. for _, line := range lines {
  113. if strings.Contains(line, containers) {
  114. output := stripTrailingCharacters(line)
  115. output = strings.TrimLeft(output, containers)
  116. output = strings.Trim(output, " ")
  117. containerCount, err := strconv.Atoi(output)
  118. if err != nil {
  119. return 0, err
  120. }
  121. return containerCount, nil
  122. }
  123. }
  124. return 0, fmt.Errorf("couldn't find the Container count in the output")
  125. }
  126. type FakeContext struct {
  127. Dir string
  128. }
  129. func (f *FakeContext) Add(file, content string) error {
  130. filepath := path.Join(f.Dir, file)
  131. dirpath := path.Dir(filepath)
  132. if dirpath != "." {
  133. if err := os.MkdirAll(dirpath, 0755); err != nil {
  134. return err
  135. }
  136. }
  137. return ioutil.WriteFile(filepath, []byte(content), 0644)
  138. }
  139. func (f *FakeContext) Delete(file string) error {
  140. filepath := path.Join(f.Dir, file)
  141. return os.RemoveAll(filepath)
  142. }
  143. func (f *FakeContext) Close() error {
  144. return os.RemoveAll(f.Dir)
  145. }
  146. func fakeContext(dockerfile string, files map[string]string) (*FakeContext, error) {
  147. tmp, err := ioutil.TempDir("", "fake-context")
  148. if err != nil {
  149. return nil, err
  150. }
  151. ctx := &FakeContext{tmp}
  152. for file, content := range files {
  153. if err := ctx.Add(file, content); err != nil {
  154. ctx.Close()
  155. return nil, err
  156. }
  157. }
  158. if err := ctx.Add("Dockerfile", dockerfile); err != nil {
  159. ctx.Close()
  160. return nil, err
  161. }
  162. return ctx, nil
  163. }
  164. type FakeStorage struct {
  165. *FakeContext
  166. *httptest.Server
  167. }
  168. func (f *FakeStorage) Close() error {
  169. f.Server.Close()
  170. return f.FakeContext.Close()
  171. }
  172. func fakeStorage(files map[string]string) (*FakeStorage, error) {
  173. tmp, err := ioutil.TempDir("", "fake-storage")
  174. if err != nil {
  175. return nil, err
  176. }
  177. ctx := &FakeContext{tmp}
  178. for file, content := range files {
  179. if err := ctx.Add(file, content); err != nil {
  180. ctx.Close()
  181. return nil, err
  182. }
  183. }
  184. handler := http.FileServer(http.Dir(ctx.Dir))
  185. server := httptest.NewServer(handler)
  186. return &FakeStorage{
  187. FakeContext: ctx,
  188. Server: server,
  189. }, nil
  190. }
  191. func inspectField(name, field string) (string, error) {
  192. format := fmt.Sprintf("{{.%s}}", field)
  193. inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name)
  194. out, exitCode, err := runCommandWithOutput(inspectCmd)
  195. if err != nil || exitCode != 0 {
  196. return "", fmt.Errorf("failed to inspect %s: %s", name, out)
  197. }
  198. return strings.TrimSpace(out), nil
  199. }
  200. func inspectFieldJSON(name, field string) (string, error) {
  201. format := fmt.Sprintf("{{json .%s}}", field)
  202. inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name)
  203. out, exitCode, err := runCommandWithOutput(inspectCmd)
  204. if err != nil || exitCode != 0 {
  205. return "", fmt.Errorf("failed to inspect %s: %s", name, out)
  206. }
  207. return strings.TrimSpace(out), nil
  208. }
  209. func getIDByName(name string) (string, error) {
  210. return inspectField(name, "Id")
  211. }
  212. func buildImageWithOut(name, dockerfile string, useCache bool) (string, string, error) {
  213. args := []string{"build", "-t", name}
  214. if !useCache {
  215. args = append(args, "--no-cache")
  216. }
  217. args = append(args, "-")
  218. buildCmd := exec.Command(dockerBinary, args...)
  219. buildCmd.Stdin = strings.NewReader(dockerfile)
  220. out, exitCode, err := runCommandWithOutput(buildCmd)
  221. if err != nil || exitCode != 0 {
  222. return "", out, fmt.Errorf("failed to build the image: %s", out)
  223. }
  224. id, err := getIDByName(name)
  225. if err != nil {
  226. return "", out, err
  227. }
  228. return id, out, nil
  229. }
  230. func buildImage(name, dockerfile string, useCache bool) (string, error) {
  231. id, _, err := buildImageWithOut(name, dockerfile, useCache)
  232. return id, err
  233. }
  234. func buildImageFromContext(name string, ctx *FakeContext, useCache bool) (string, error) {
  235. args := []string{"build", "-t", name}
  236. if !useCache {
  237. args = append(args, "--no-cache")
  238. }
  239. args = append(args, ".")
  240. buildCmd := exec.Command(dockerBinary, args...)
  241. buildCmd.Dir = ctx.Dir
  242. out, exitCode, err := runCommandWithOutput(buildCmd)
  243. if err != nil || exitCode != 0 {
  244. return "", fmt.Errorf("failed to build the image: %s", out)
  245. }
  246. return getIDByName(name)
  247. }
  248. func buildImageFromPath(name, path string, useCache bool) (string, error) {
  249. args := []string{"build", "-t", name}
  250. if !useCache {
  251. args = append(args, "--no-cache")
  252. }
  253. args = append(args, path)
  254. buildCmd := exec.Command(dockerBinary, args...)
  255. out, exitCode, err := runCommandWithOutput(buildCmd)
  256. if err != nil || exitCode != 0 {
  257. return "", fmt.Errorf("failed to build the image: %s", out)
  258. }
  259. return getIDByName(name)
  260. }
  261. type FakeGIT struct {
  262. *httptest.Server
  263. Root string
  264. RepoURL string
  265. }
  266. func (g *FakeGIT) Close() {
  267. g.Server.Close()
  268. os.RemoveAll(g.Root)
  269. }
  270. func fakeGIT(name string, files map[string]string) (*FakeGIT, error) {
  271. tmp, err := ioutil.TempDir("", "fake-git-repo")
  272. if err != nil {
  273. return nil, err
  274. }
  275. ctx := &FakeContext{tmp}
  276. for file, content := range files {
  277. if err := ctx.Add(file, content); err != nil {
  278. ctx.Close()
  279. return nil, err
  280. }
  281. }
  282. defer ctx.Close()
  283. curdir, err := os.Getwd()
  284. if err != nil {
  285. return nil, err
  286. }
  287. defer os.Chdir(curdir)
  288. if output, err := exec.Command("git", "init", ctx.Dir).CombinedOutput(); err != nil {
  289. return nil, fmt.Errorf("Error trying to init repo: %s (%s)", err, output)
  290. }
  291. err = os.Chdir(ctx.Dir)
  292. if err != nil {
  293. return nil, err
  294. }
  295. if output, err := exec.Command("git", "add", "*").CombinedOutput(); err != nil {
  296. return nil, fmt.Errorf("Error trying to add files to repo: %s (%s)", err, output)
  297. }
  298. if output, err := exec.Command("git", "commit", "-a", "-m", "Initial commit").CombinedOutput(); err != nil {
  299. return nil, fmt.Errorf("Error trying to commit to repo: %s (%s)", err, output)
  300. }
  301. root, err := ioutil.TempDir("", "docker-test-git-repo")
  302. if err != nil {
  303. return nil, err
  304. }
  305. repoPath := filepath.Join(root, name+".git")
  306. if output, err := exec.Command("git", "clone", "--bare", ctx.Dir, repoPath).CombinedOutput(); err != nil {
  307. os.RemoveAll(root)
  308. return nil, fmt.Errorf("Error trying to clone --bare: %s (%s)", err, output)
  309. }
  310. err = os.Chdir(repoPath)
  311. if err != nil {
  312. os.RemoveAll(root)
  313. return nil, err
  314. }
  315. if output, err := exec.Command("git", "update-server-info").CombinedOutput(); err != nil {
  316. os.RemoveAll(root)
  317. return nil, fmt.Errorf("Error trying to git update-server-info: %s (%s)", err, output)
  318. }
  319. err = os.Chdir(curdir)
  320. if err != nil {
  321. os.RemoveAll(root)
  322. return nil, err
  323. }
  324. handler := http.FileServer(http.Dir(root))
  325. server := httptest.NewServer(handler)
  326. return &FakeGIT{
  327. Server: server,
  328. Root: root,
  329. RepoURL: fmt.Sprintf("%s/%s.git", server.URL, name),
  330. }, nil
  331. }
  332. // Write `content` to the file at path `dst`, creating it if necessary,
  333. // as well as any missing directories.
  334. // The file is truncated if it already exists.
  335. // Call t.Fatal() at the first error.
  336. func writeFile(dst, content string, t *testing.T) {
  337. // Create subdirectories if necessary
  338. if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) {
  339. t.Fatal(err)
  340. }
  341. f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
  342. if err != nil {
  343. t.Fatal(err)
  344. }
  345. // Write content (truncate if it exists)
  346. if _, err := io.Copy(f, strings.NewReader(content)); err != nil {
  347. t.Fatal(err)
  348. }
  349. }
  350. // Return the contents of file at path `src`.
  351. // Call t.Fatal() at the first error (including if the file doesn't exist)
  352. func readFile(src string, t *testing.T) (content string) {
  353. f, err := os.Open(src)
  354. if err != nil {
  355. t.Fatal(err)
  356. }
  357. data, err := ioutil.ReadAll(f)
  358. if err != nil {
  359. t.Fatal(err)
  360. }
  361. return string(data)
  362. }