github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/build/docker_builder_test.go (about) 1 package build 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "strings" 8 "testing" 9 10 "github.com/docker/docker/api/types" 11 "github.com/opencontainers/go-digest" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 15 "github.com/tilt-dev/tilt/internal/container" 16 "github.com/tilt-dev/tilt/internal/docker" 17 "github.com/tilt-dev/tilt/internal/testutils" 18 ) 19 20 func TestDigestAsTag(t *testing.T) { 21 dig := digest.Digest("sha256:cc5f4c463f81c55183d8d737ba2f0d30b3e6f3670dbe2da68f0aac168e93fbb1") 22 tag, err := digestAsTag(dig) 23 if err != nil { 24 t.Fatal(err) 25 } 26 27 expected := "tilt-cc5f4c463f81c551" 28 if tag != expected { 29 t.Errorf("Expected %s, actual: %s", expected, tag) 30 } 31 } 32 33 func TestDigestMatchesRef(t *testing.T) { 34 dig := digest.Digest("sha256:cc5f4c463f81c55183d8d737ba2f0d30b3e6f3670dbe2da68f0aac168e93fbb1") 35 tag, err := digestAsTag(dig) 36 if err != nil { 37 t.Fatal(err) 38 } 39 40 ref, _ := container.ParseNamedTagged("windmill.build/image:" + tag) 41 if !digestMatchesRef(ref, dig) { 42 t.Errorf("Expected digest %s to match ref %s", dig, ref) 43 } 44 } 45 46 func TestDigestNotMatchesRef(t *testing.T) { 47 dig := digest.Digest("sha256:cc5f4c463f81c55183d8d737ba2f0d30b3e6f3670dbe2da68f0aac168e93fbb1") 48 ref, _ := container.ParseNamedTagged("windmill.build/image:tilt-deadbeef") 49 if digestMatchesRef(ref, dig) { 50 t.Errorf("Expected digest %s to not match ref %s", dig, ref) 51 } 52 } 53 54 func TestDigestAsTagToShort(t *testing.T) { 55 dig := digest.Digest("sha256:cc") 56 _, err := digestAsTag(dig) 57 expected := "too short" 58 if err == nil || !strings.Contains(err.Error(), expected) { 59 t.Errorf("expected error %q, actual: %v", expected, err) 60 } 61 } 62 63 func TestDigestFromSingleStepOutput(t *testing.T) { 64 f := newFakeDockerBuildFixture(t) 65 66 input := docker.ExampleBuildOutput1 67 expected := digest.Digest("sha256:11cd0b38bc3ceb958ffb2f9bd70be3fb317ce7d255c8a4c3f4af30e298aa1aab") 68 actual, _, err := f.b.getDigestFromBuildOutput(f.ctx, bytes.NewBuffer([]byte(input))) 69 if err != nil { 70 t.Fatal(err) 71 } 72 if actual != expected { 73 t.Errorf("Expected %s, got %s", expected, actual) 74 } 75 } 76 77 func TestDigestFromOutputV1_23(t *testing.T) { 78 f := newFakeDockerBuildFixture(t) 79 80 input := docker.ExampleBuildOutputV1_23 81 expected := digest.Digest("sha256:11cd0eb38bc3ceb958ffb2f9bd70be3fb317ce7d255c8a4c3f4af30e298aa1aab") 82 f.fakeDocker.Images["11cd0b38bc3c"] = types.ImageInspect{ID: string(expected)} 83 actual, _, err := f.b.getDigestFromBuildOutput(f.ctx, bytes.NewBuffer([]byte(input))) 84 if err != nil { 85 t.Fatal(err) 86 } 87 if actual != expected { 88 t.Errorf("Expected %s, got %s", expected, actual) 89 } 90 } 91 92 func TestDumpImageDeployRef(t *testing.T) { 93 f := newFakeDockerBuildFixture(t) 94 95 digest := digest.Digest("sha256:11cd0eb38bc3ceb958ffb2f9bd70be3fb317ce7d255c8a4c3f4af30e298aa1aab") 96 f.fakeDocker.Images["example-image:dev"] = types.ImageInspect{ID: string(digest)} 97 ref, err := f.b.DumpImageDeployRef(f.ctx, "example-image:dev") 98 require.NoError(t, err) 99 assert.Equal(t, "docker.io/library/example-image:tilt-11cd0eb38bc3ceb9", ref.String()) 100 } 101 102 func makeDockerBuildErrorOutput(s string) string { 103 b := &bytes.Buffer{} 104 err := json.NewEncoder(b).Encode(s) 105 if err != nil { 106 panic(err) 107 } 108 109 return fmt.Sprintf(`{"errorDetail":{"message":%s},"error":%s}`, b.String(), b.String()) 110 } 111 112 func TestCleanUpBuildKitErrors(t *testing.T) { 113 for _, tc := range []struct { 114 buildKitError string 115 expectedTiltError string 116 }{ 117 // actual error currently emitted by buildkit when a `RUN` fails 118 { 119 //nolint 120 buildKitError: "failed to solve with frontend dockerfile.v0: failed to build LLB: executor failed running [/bin/sh -c go install github.com/tilt-dev/servantes/vigoda]: runc did not terminate sucessfully", 121 expectedTiltError: "executor failed running [/bin/sh -c go install github.com/tilt-dev/servantes/vigoda]", 122 }, 123 //nolint 124 // artificial error - in case docker for some reason doesn't have "executor failed running", don't trim "runc did not terminate sucessfully" 125 { 126 //nolint 127 buildKitError: "failed to solve with frontend dockerfile.v0: failed to build LLB: [/bin/sh -c go install github.com/tilt-dev/servantes/vigoda]: runc did not terminate sucessfully", 128 expectedTiltError: "[/bin/sh -c go install github.com/tilt-dev/servantes/vigoda]: runc did not terminate sucessfully", 129 }, 130 // actual error currently emitted by buildkit when an `ADD` file is missing 131 { 132 buildKitError: `failed to solve with frontend dockerfile.v0: failed to build LLB: failed to compute cache key: "/foo.txt" not found: not found`, 133 expectedTiltError: `"/foo.txt" not found`, 134 }, 135 // artificial error - in case docker fails to emit the double "not found", don't trim the one at the end 136 // output in this case could do without the "failed to compute cache key", but this test is just ensuring we 137 // err on the side of caution, rather than that we're emitting an optimal message for an artificial error 138 { 139 buildKitError: `failed to solve with frontend dockerfile.v0: failed to build LLB: failed to compute cache key: "/foo.txt": not found`, 140 expectedTiltError: `failed to compute cache key: "/foo.txt": not found`, 141 }, 142 // artificial error - in case docker doesn't say "not found" at all 143 { 144 buildKitError: `failed to solve with frontend dockerfile.v0: failed to build LLB: failed to compute cache key: "/foo.txt"`, 145 expectedTiltError: `failed to compute cache key: "/foo.txt"`, 146 }, 147 // actual error - trying to ADD a file from a directory that doesn't exist locally 148 { 149 buildKitError: `failed to compute cache key: failed to walk /var/lib/docker/tmp/buildkit-mount818620576/base/src: lstat /var/lib/docker/tmp/buildkit-mount818620576/base/src: no such file or directory`, 150 expectedTiltError: `base/src: no such file or directory`, 151 }, 152 // check an unanticipated error that still has the annoying preamble 153 { 154 buildKitError: "failed to solve with frontend dockerfile.v0: failed to build LLB: who knows, some made up explosion", 155 expectedTiltError: "who knows, some made up explosion", 156 }, 157 { 158 // Error message when using 159 // # syntax=docker/dockerfile:experimental 160 //nolint 161 buildKitError: "failed to solve with frontend dockerfile.v0: failed to solve with frontend gateway.v0: rpc error: code = Unknown desc = failed to build LLB: executor failed running [/bin/sh -c pip install python-dateutil]: runc did not terminate sucessfully", 162 expectedTiltError: "executor failed running [/bin/sh -c pip install python-dateutil]", 163 }, 164 } { 165 t.Run(tc.expectedTiltError, func(t *testing.T) { 166 f := newFakeDockerBuildFixture(t) 167 168 ctx, _, _ := testutils.CtxAndAnalyticsForTest() 169 s := makeDockerBuildErrorOutput(tc.buildKitError) 170 _, _, err := f.b.getDigestFromBuildOutput(ctx, strings.NewReader(s)) 171 require.NotNil(t, err) 172 require.Equal(t, fmt.Sprintf("ImageBuild: %s", tc.expectedTiltError), err.Error()) 173 }) 174 } 175 }