Implemented a FakeStorage alternative that supports spinning
up a remote container on DOCKER_TEST_HOST to serve files over
an offline-compiled Go static web server image so that tests which
use URLs in Dockerfile can build them over at the daemon side.
`fakeStorage` function now automatically chooses if it should
use a local httptest.Server or a remote container.
This fixes the following tests when running against a remote
daemon:
- `TestBuildCacheADD`
- `TestBuildCopyWildcardNoFind`
- `TestBuildCopyWildcardCache`
- `TestBuildADDRemoteFileWithCache`
- `TestBuildADDRemoteFileWithoutCache`
- `TestBuildADDRemoteFileMTime`
- `TestBuildADDLocalAndRemoteFilesWithCache`
- `TestBuildADDLocalAndRemoteFilesWithoutCache`
- `TestBuildFromURLWithF`
- `TestBuildApiDockerFileRemote`
Signed-off-by: Ahmet Alp Balkan <ahmetalpbalkan@gmail.com>
| ... | ... |
@@ -364,7 +364,7 @@ RUN find /tmp/`, |
| 364 | 364 |
} |
| 365 | 365 |
defer server.Close() |
| 366 | 366 |
|
| 367 |
- buf, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL+"/testD", nil, "application/json")
|
|
| 367 |
+ buf, err := sockRequestRaw("POST", "/build?dockerfile=baz&remote="+server.URL()+"/testD", nil, "application/json")
|
|
| 368 | 368 |
if err != nil {
|
| 369 | 369 |
t.Fatalf("Build failed: %s", err)
|
| 370 | 370 |
} |
| ... | ... |
@@ -8,14 +8,12 @@ import ( |
| 8 | 8 |
"io/ioutil" |
| 9 | 9 |
"os" |
| 10 | 10 |
"os/exec" |
| 11 |
- "path" |
|
| 12 | 11 |
"path/filepath" |
| 13 | 12 |
"reflect" |
| 14 | 13 |
"regexp" |
| 15 | 14 |
"runtime" |
| 16 | 15 |
"strconv" |
| 17 | 16 |
"strings" |
| 18 |
- "syscall" |
|
| 19 | 17 |
"testing" |
| 20 | 18 |
"text/template" |
| 21 | 19 |
"time" |
| ... | ... |
@@ -645,9 +643,10 @@ func TestBuildCacheADD(t *testing.T) {
|
| 645 | 645 |
t.Fatal(err) |
| 646 | 646 |
} |
| 647 | 647 |
defer server.Close() |
| 648 |
+ |
|
| 648 | 649 |
if _, err := buildImage(name, |
| 649 | 650 |
fmt.Sprintf(`FROM scratch |
| 650 |
- ADD %s/robots.txt /`, server.URL), |
|
| 651 |
+ ADD %s/robots.txt /`, server.URL()), |
|
| 651 | 652 |
true); err != nil {
|
| 652 | 653 |
t.Fatal(err) |
| 653 | 654 |
} |
| ... | ... |
@@ -657,7 +656,7 @@ func TestBuildCacheADD(t *testing.T) {
|
| 657 | 657 |
deleteImages(name) |
| 658 | 658 |
_, out, err := buildImageWithOut(name, |
| 659 | 659 |
fmt.Sprintf(`FROM scratch |
| 660 |
- ADD %s/index.html /`, server.URL), |
|
| 660 |
+ ADD %s/index.html /`, server.URL()), |
|
| 661 | 661 |
true) |
| 662 | 662 |
if err != nil {
|
| 663 | 663 |
t.Fatal(err) |
| ... | ... |
@@ -797,7 +796,7 @@ RUN [ $(ls -l /exists/test_file4 | awk '{print $3":"$4}') = 'root:root' ]
|
| 797 | 797 |
RUN [ $(ls -l /exists/robots.txt | awk '{print $3":"$4}') = 'root:root' ]
|
| 798 | 798 |
|
| 799 | 799 |
RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio' ]
|
| 800 |
-`, server.URL), |
|
| 800 |
+`, server.URL()), |
|
| 801 | 801 |
map[string]string{
|
| 802 | 802 |
"test_file1": "test1", |
| 803 | 803 |
"test_file2": "test2", |
| ... | ... |
@@ -1084,6 +1083,7 @@ func TestBuildCopyWildcard(t *testing.T) {
|
| 1084 | 1084 |
t.Fatal(err) |
| 1085 | 1085 |
} |
| 1086 | 1086 |
defer server.Close() |
| 1087 |
+ |
|
| 1087 | 1088 |
ctx, err := fakeContext(fmt.Sprintf(`FROM busybox |
| 1088 | 1089 |
COPY file*.txt /tmp/ |
| 1089 | 1090 |
RUN ls /tmp/file1.txt /tmp/file2.txt |
| ... | ... |
@@ -1093,7 +1093,7 @@ func TestBuildCopyWildcard(t *testing.T) {
|
| 1093 | 1093 |
RUN mkdir /tmp2 |
| 1094 | 1094 |
ADD dir/*dir %s/robots.txt /tmp2/ |
| 1095 | 1095 |
RUN ls /tmp2/nest_nest_file /tmp2/robots.txt |
| 1096 |
- `, server.URL), |
|
| 1096 |
+ `, server.URL()), |
|
| 1097 | 1097 |
map[string]string{
|
| 1098 | 1098 |
"file1.txt": "test1", |
| 1099 | 1099 |
"file2.txt": "test2", |
| ... | ... |
@@ -2831,10 +2831,11 @@ func TestBuildADDRemoteFileWithCache(t *testing.T) {
|
| 2831 | 2831 |
t.Fatal(err) |
| 2832 | 2832 |
} |
| 2833 | 2833 |
defer server.Close() |
| 2834 |
+ |
|
| 2834 | 2835 |
id1, err := buildImage(name, |
| 2835 | 2836 |
fmt.Sprintf(`FROM scratch |
| 2836 | 2837 |
MAINTAINER dockerio |
| 2837 |
- ADD %s/baz /usr/lib/baz/quux`, server.URL), |
|
| 2838 |
+ ADD %s/baz /usr/lib/baz/quux`, server.URL()), |
|
| 2838 | 2839 |
true) |
| 2839 | 2840 |
if err != nil {
|
| 2840 | 2841 |
t.Fatal(err) |
| ... | ... |
@@ -2842,7 +2843,7 @@ func TestBuildADDRemoteFileWithCache(t *testing.T) {
|
| 2842 | 2842 |
id2, err := buildImage(name, |
| 2843 | 2843 |
fmt.Sprintf(`FROM scratch |
| 2844 | 2844 |
MAINTAINER dockerio |
| 2845 |
- ADD %s/baz /usr/lib/baz/quux`, server.URL), |
|
| 2845 |
+ ADD %s/baz /usr/lib/baz/quux`, server.URL()), |
|
| 2846 | 2846 |
true) |
| 2847 | 2847 |
if err != nil {
|
| 2848 | 2848 |
t.Fatal(err) |
| ... | ... |
@@ -2864,10 +2865,11 @@ func TestBuildADDRemoteFileWithoutCache(t *testing.T) {
|
| 2864 | 2864 |
t.Fatal(err) |
| 2865 | 2865 |
} |
| 2866 | 2866 |
defer server.Close() |
| 2867 |
+ |
|
| 2867 | 2868 |
id1, err := buildImage(name, |
| 2868 | 2869 |
fmt.Sprintf(`FROM scratch |
| 2869 | 2870 |
MAINTAINER dockerio |
| 2870 |
- ADD %s/baz /usr/lib/baz/quux`, server.URL), |
|
| 2871 |
+ ADD %s/baz /usr/lib/baz/quux`, server.URL()), |
|
| 2871 | 2872 |
true) |
| 2872 | 2873 |
if err != nil {
|
| 2873 | 2874 |
t.Fatal(err) |
| ... | ... |
@@ -2875,7 +2877,7 @@ func TestBuildADDRemoteFileWithoutCache(t *testing.T) {
|
| 2875 | 2875 |
id2, err := buildImage(name2, |
| 2876 | 2876 |
fmt.Sprintf(`FROM scratch |
| 2877 | 2877 |
MAINTAINER dockerio |
| 2878 |
- ADD %s/baz /usr/lib/baz/quux`, server.URL), |
|
| 2878 |
+ ADD %s/baz /usr/lib/baz/quux`, server.URL()), |
|
| 2879 | 2879 |
false) |
| 2880 | 2880 |
if err != nil {
|
| 2881 | 2881 |
t.Fatal(err) |
| ... | ... |
@@ -2894,7 +2896,8 @@ func TestBuildADDRemoteFileMTime(t *testing.T) {
|
| 2894 | 2894 |
|
| 2895 | 2895 |
defer deleteImages(name, name2, name3, name4) |
| 2896 | 2896 |
|
| 2897 |
- server, err := fakeStorage(map[string]string{"baz": "hello"})
|
|
| 2897 |
+ files := map[string]string{"baz": "hello"}
|
|
| 2898 |
+ server, err := fakeStorage(files) |
|
| 2898 | 2899 |
if err != nil {
|
| 2899 | 2900 |
t.Fatal(err) |
| 2900 | 2901 |
} |
| ... | ... |
@@ -2902,7 +2905,7 @@ func TestBuildADDRemoteFileMTime(t *testing.T) {
|
| 2902 | 2902 |
|
| 2903 | 2903 |
ctx, err := fakeContext(fmt.Sprintf(`FROM scratch |
| 2904 | 2904 |
MAINTAINER dockerio |
| 2905 |
- ADD %s/baz /usr/lib/baz/quux`, server.URL), nil) |
|
| 2905 |
+ ADD %s/baz /usr/lib/baz/quux`, server.URL()), nil) |
|
| 2906 | 2906 |
if err != nil {
|
| 2907 | 2907 |
t.Fatal(err) |
| 2908 | 2908 |
} |
| ... | ... |
@@ -2921,15 +2924,26 @@ func TestBuildADDRemoteFileMTime(t *testing.T) {
|
| 2921 | 2921 |
t.Fatal("The cache should have been used but wasn't - #1")
|
| 2922 | 2922 |
} |
| 2923 | 2923 |
|
| 2924 |
- // Now set baz's times to anything else and redo the build |
|
| 2924 |
+ // Now create a different server withsame contents (causes different mtim) |
|
| 2925 | 2925 |
// This time the cache should not be used |
| 2926 |
- bazPath := path.Join(server.FakeContext.Dir, "baz") |
|
| 2927 |
- err = syscall.UtimesNano(bazPath, make([]syscall.Timespec, 2)) |
|
| 2926 |
+ |
|
| 2927 |
+ // allow some time for clock to pass as mtime precision is only 1s |
|
| 2928 |
+ time.Sleep(2 * time.Second) |
|
| 2929 |
+ |
|
| 2930 |
+ server2, err := fakeStorage(files) |
|
| 2928 | 2931 |
if err != nil {
|
| 2929 |
- t.Fatalf("Error setting mtime on %q: %v", bazPath, err)
|
|
| 2932 |
+ t.Fatal(err) |
|
| 2930 | 2933 |
} |
| 2934 |
+ defer server2.Close() |
|
| 2931 | 2935 |
|
| 2932 |
- id3, err := buildImageFromContext(name3, ctx, true) |
|
| 2936 |
+ ctx2, err := fakeContext(fmt.Sprintf(`FROM scratch |
|
| 2937 |
+ MAINTAINER dockerio |
|
| 2938 |
+ ADD %s/baz /usr/lib/baz/quux`, server2.URL()), nil) |
|
| 2939 |
+ if err != nil {
|
|
| 2940 |
+ t.Fatal(err) |
|
| 2941 |
+ } |
|
| 2942 |
+ defer ctx2.Close() |
|
| 2943 |
+ id3, err := buildImageFromContext(name3, ctx2, true) |
|
| 2933 | 2944 |
if err != nil {
|
| 2934 | 2945 |
t.Fatal(err) |
| 2935 | 2946 |
} |
| ... | ... |
@@ -2938,7 +2952,7 @@ func TestBuildADDRemoteFileMTime(t *testing.T) {
|
| 2938 | 2938 |
} |
| 2939 | 2939 |
|
| 2940 | 2940 |
// And for good measure do it again and make sure cache is used this time |
| 2941 |
- id4, err := buildImageFromContext(name4, ctx, true) |
|
| 2941 |
+ id4, err := buildImageFromContext(name4, ctx2, true) |
|
| 2942 | 2942 |
if err != nil {
|
| 2943 | 2943 |
t.Fatal(err) |
| 2944 | 2944 |
} |
| ... | ... |
@@ -2958,10 +2972,11 @@ func TestBuildADDLocalAndRemoteFilesWithCache(t *testing.T) {
|
| 2958 | 2958 |
t.Fatal(err) |
| 2959 | 2959 |
} |
| 2960 | 2960 |
defer server.Close() |
| 2961 |
+ |
|
| 2961 | 2962 |
ctx, err := fakeContext(fmt.Sprintf(`FROM scratch |
| 2962 | 2963 |
MAINTAINER dockerio |
| 2963 | 2964 |
ADD foo /usr/lib/bla/bar |
| 2964 |
- ADD %s/baz /usr/lib/baz/quux`, server.URL), |
|
| 2965 |
+ ADD %s/baz /usr/lib/baz/quux`, server.URL()), |
|
| 2965 | 2966 |
map[string]string{
|
| 2966 | 2967 |
"foo": "hello world", |
| 2967 | 2968 |
}) |
| ... | ... |
@@ -3047,10 +3062,11 @@ func TestBuildADDLocalAndRemoteFilesWithoutCache(t *testing.T) {
|
| 3047 | 3047 |
t.Fatal(err) |
| 3048 | 3048 |
} |
| 3049 | 3049 |
defer server.Close() |
| 3050 |
+ |
|
| 3050 | 3051 |
ctx, err := fakeContext(fmt.Sprintf(`FROM scratch |
| 3051 | 3052 |
MAINTAINER dockerio |
| 3052 | 3053 |
ADD foo /usr/lib/bla/bar |
| 3053 |
- ADD %s/baz /usr/lib/baz/quux`, server.URL), |
|
| 3054 |
+ ADD %s/baz /usr/lib/baz/quux`, server.URL()), |
|
| 3054 | 3055 |
map[string]string{
|
| 3055 | 3056 |
"foo": "hello world", |
| 3056 | 3057 |
}) |
| ... | ... |
@@ -4773,7 +4789,7 @@ RUN echo from Dockerfile`, |
| 4773 | 4773 |
|
| 4774 | 4774 |
// Make sure that -f is ignored and that we don't use the Dockerfile |
| 4775 | 4775 |
// that's in the current dir |
| 4776 |
- out, _, err := dockerCmdInDir(t, ctx.Dir, "build", "-f", "baz", "-t", "test1", server.URL+"/baz") |
|
| 4776 |
+ out, _, err := dockerCmdInDir(t, ctx.Dir, "build", "-f", "baz", "-t", "test1", server.URL()+"/baz") |
|
| 4777 | 4777 |
if err != nil {
|
| 4778 | 4778 |
t.Fatalf("Failed to build: %s\n%s", out, err)
|
| 4779 | 4779 |
} |
| ... | ... |
@@ -581,17 +581,42 @@ func fakeContext(dockerfile string, files map[string]string) (*FakeContext, erro |
| 581 | 581 |
return ctx, nil |
| 582 | 582 |
} |
| 583 | 583 |
|
| 584 |
-type FakeStorage struct {
|
|
| 584 |
+// FakeStorage is a static file server. It might be running locally or remotely |
|
| 585 |
+// on test host. |
|
| 586 |
+type FakeStorage interface {
|
|
| 587 |
+ Close() error |
|
| 588 |
+ URL() string |
|
| 589 |
+ CtxDir() string |
|
| 590 |
+} |
|
| 591 |
+ |
|
| 592 |
+// fakeStorage returns either a local or remote (at daemon machine) file server |
|
| 593 |
+func fakeStorage(files map[string]string) (FakeStorage, error) {
|
|
| 594 |
+ if isLocalDaemon {
|
|
| 595 |
+ return newLocalFakeStorage(files) |
|
| 596 |
+ } |
|
| 597 |
+ return newRemoteFileServer(files) |
|
| 598 |
+} |
|
| 599 |
+ |
|
| 600 |
+// localFileStorage is a file storage on the running machine |
|
| 601 |
+type localFileStorage struct {
|
|
| 585 | 602 |
*FakeContext |
| 586 | 603 |
*httptest.Server |
| 587 | 604 |
} |
| 588 | 605 |
|
| 589 |
-func (f *FakeStorage) Close() error {
|
|
| 590 |
- f.Server.Close() |
|
| 591 |
- return f.FakeContext.Close() |
|
| 606 |
+func (s *localFileStorage) URL() string {
|
|
| 607 |
+ return s.Server.URL |
|
| 608 |
+} |
|
| 609 |
+ |
|
| 610 |
+func (s *localFileStorage) CtxDir() string {
|
|
| 611 |
+ return s.FakeContext.Dir |
|
| 612 |
+} |
|
| 613 |
+ |
|
| 614 |
+func (s *localFileStorage) Close() error {
|
|
| 615 |
+ defer s.Server.Close() |
|
| 616 |
+ return s.FakeContext.Close() |
|
| 592 | 617 |
} |
| 593 | 618 |
|
| 594 |
-func fakeStorage(files map[string]string) (*FakeStorage, error) {
|
|
| 619 |
+func newLocalFakeStorage(files map[string]string) (*localFileStorage, error) {
|
|
| 595 | 620 |
tmp, err := ioutil.TempDir("", "fake-storage")
|
| 596 | 621 |
if err != nil {
|
| 597 | 622 |
return nil, err |
| ... | ... |
@@ -605,42 +630,101 @@ func fakeStorage(files map[string]string) (*FakeStorage, error) {
|
| 605 | 605 |
} |
| 606 | 606 |
handler := http.FileServer(http.Dir(ctx.Dir)) |
| 607 | 607 |
server := httptest.NewServer(handler) |
| 608 |
- return &FakeStorage{
|
|
| 608 |
+ return &localFileStorage{
|
|
| 609 | 609 |
FakeContext: ctx, |
| 610 | 610 |
Server: server, |
| 611 | 611 |
}, nil |
| 612 | 612 |
} |
| 613 | 613 |
|
| 614 |
-func inspectField(name, field string) (string, error) {
|
|
| 615 |
- format := fmt.Sprintf("{{.%s}}", field)
|
|
| 616 |
- inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) |
|
| 617 |
- out, exitCode, err := runCommandWithOutput(inspectCmd) |
|
| 618 |
- if err != nil || exitCode != 0 {
|
|
| 619 |
- return "", fmt.Errorf("failed to inspect %s: %s", name, out)
|
|
| 614 |
+// remoteFileServer is a containerized static file server started on the remote |
|
| 615 |
+// testing machine to be used in URL-accepting docker build functionality. |
|
| 616 |
+type remoteFileServer struct {
|
|
| 617 |
+ host string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712 |
|
| 618 |
+ container string |
|
| 619 |
+ image string |
|
| 620 |
+ ctx *FakeContext |
|
| 621 |
+} |
|
| 622 |
+ |
|
| 623 |
+func (f *remoteFileServer) URL() string {
|
|
| 624 |
+ u := url.URL{
|
|
| 625 |
+ Scheme: "http", |
|
| 626 |
+ Host: f.host} |
|
| 627 |
+ return u.String() |
|
| 628 |
+} |
|
| 629 |
+ |
|
| 630 |
+func (f *remoteFileServer) CtxDir() string {
|
|
| 631 |
+ return f.ctx.Dir |
|
| 632 |
+} |
|
| 633 |
+ |
|
| 634 |
+func (f *remoteFileServer) Close() error {
|
|
| 635 |
+ defer func() {
|
|
| 636 |
+ if f.ctx != nil {
|
|
| 637 |
+ f.ctx.Close() |
|
| 638 |
+ } |
|
| 639 |
+ if f.image != "" {
|
|
| 640 |
+ deleteImages(f.image) |
|
| 641 |
+ } |
|
| 642 |
+ }() |
|
| 643 |
+ if f.container == "" {
|
|
| 644 |
+ return nil |
|
| 620 | 645 |
} |
| 621 |
- return strings.TrimSpace(out), nil |
|
| 646 |
+ return deleteContainer(f.container) |
|
| 622 | 647 |
} |
| 623 | 648 |
|
| 624 |
-func inspectFieldJSON(name, field string) (string, error) {
|
|
| 625 |
- format := fmt.Sprintf("{{json .%s}}", field)
|
|
| 626 |
- inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) |
|
| 627 |
- out, exitCode, err := runCommandWithOutput(inspectCmd) |
|
| 628 |
- if err != nil || exitCode != 0 {
|
|
| 629 |
- return "", fmt.Errorf("failed to inspect %s: %s", name, out)
|
|
| 649 |
+func newRemoteFileServer(files map[string]string) (*remoteFileServer, error) {
|
|
| 650 |
+ var ( |
|
| 651 |
+ image = fmt.Sprintf("fileserver-img-%s", strings.ToLower(makeRandomString(10)))
|
|
| 652 |
+ container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(makeRandomString(10)))
|
|
| 653 |
+ ) |
|
| 654 |
+ |
|
| 655 |
+ // Build the image |
|
| 656 |
+ ctx, err := fakeContext(`FROM httpserver |
|
| 657 |
+COPY . /static`, files) |
|
| 658 |
+ if _, err := buildImageFromContext(image, ctx, false); err != nil {
|
|
| 659 |
+ return nil, fmt.Errorf("failed building file storage container image: %v", err)
|
|
| 630 | 660 |
} |
| 631 |
- return strings.TrimSpace(out), nil |
|
| 661 |
+ |
|
| 662 |
+ // Start the container |
|
| 663 |
+ runCmd := exec.Command(dockerBinary, "run", "-d", "-P", "--name", container, image) |
|
| 664 |
+ if out, ec, err := runCommandWithOutput(runCmd); err != nil {
|
|
| 665 |
+ return nil, fmt.Errorf("failed to start file storage container. ec=%v\nout=%s\nerr=%v", ec, out, err)
|
|
| 666 |
+ } |
|
| 667 |
+ |
|
| 668 |
+ // Find out the system assigned port |
|
| 669 |
+ out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "port", container, "80/tcp")) |
|
| 670 |
+ if err != nil {
|
|
| 671 |
+ return nil, fmt.Errorf("failed to find container port: err=%v\nout=%s", err, out)
|
|
| 672 |
+ } |
|
| 673 |
+ |
|
| 674 |
+ return &remoteFileServer{
|
|
| 675 |
+ container: container, |
|
| 676 |
+ image: image, |
|
| 677 |
+ host: strings.Trim(out, "\n"), |
|
| 678 |
+ ctx: ctx}, nil |
|
| 632 | 679 |
} |
| 633 | 680 |
|
| 634 |
-func inspectFieldMap(name, path, field string) (string, error) {
|
|
| 635 |
- format := fmt.Sprintf("{{index .%s %q}}", path, field)
|
|
| 681 |
+func inspectFilter(name, filter string) (string, error) {
|
|
| 682 |
+ format := fmt.Sprintf("{{%s}}", filter)
|
|
| 636 | 683 |
inspectCmd := exec.Command(dockerBinary, "inspect", "-f", format, name) |
| 637 | 684 |
out, exitCode, err := runCommandWithOutput(inspectCmd) |
| 638 | 685 |
if err != nil || exitCode != 0 {
|
| 639 |
- return "", fmt.Errorf("failed to inspect %s: %s", name, out)
|
|
| 686 |
+ return "", fmt.Errorf("failed to inspect container %s: %s", name, out)
|
|
| 640 | 687 |
} |
| 641 | 688 |
return strings.TrimSpace(out), nil |
| 642 | 689 |
} |
| 643 | 690 |
|
| 691 |
+func inspectField(name, field string) (string, error) {
|
|
| 692 |
+ return inspectFilter(name, fmt.Sprintf(".%s", field))
|
|
| 693 |
+} |
|
| 694 |
+ |
|
| 695 |
+func inspectFieldJSON(name, field string) (string, error) {
|
|
| 696 |
+ return inspectFilter(name, fmt.Sprintf("json .%s", field))
|
|
| 697 |
+} |
|
| 698 |
+ |
|
| 699 |
+func inspectFieldMap(name, path, field string) (string, error) {
|
|
| 700 |
+ return inspectFilter(name, fmt.Sprintf("index .%s %q", path, field))
|
|
| 701 |
+} |
|
| 702 |
+ |
|
| 644 | 703 |
func getIDByName(name string) (string, error) {
|
| 645 | 704 |
return inspectField(name, "Id") |
| 646 | 705 |
} |
| 647 | 706 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,15 @@ |
| 0 |
+#!/bin/bash |
|
| 1 |
+set -e |
|
| 2 |
+ |
|
| 3 |
+# Build a Go static web server on top of busybox image |
|
| 4 |
+# and compile it for target daemon |
|
| 5 |
+ |
|
| 6 |
+dir="$DEST/httpserver" |
|
| 7 |
+mkdir -p "$dir" |
|
| 8 |
+( |
|
| 9 |
+ cd "$dir" |
|
| 10 |
+ GOOS=linux GOARCH=amd64 go build -o httpserver github.com/docker/docker/contrib/httpserver |
|
| 11 |
+ cp ../../../../contrib/httpserver/Dockerfile . |
|
| 12 |
+ docker build -qt httpserver . > /dev/null |
|
| 13 |
+) |
|
| 14 |
+rm -rf "$dir" |