github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/image/cache/compare_test.go (about) 1 package cache // import "github.com/docker/docker/image/cache" 2 3 import ( 4 "runtime" 5 "testing" 6 7 "github.com/docker/docker/api/types/container" 8 "github.com/docker/docker/api/types/strslice" 9 "github.com/docker/go-connections/nat" 10 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 11 "gotest.tools/v3/assert" 12 is "gotest.tools/v3/assert/cmp" 13 ) 14 15 // Just to make life easier 16 func newPortNoError(proto, port string) nat.Port { 17 p, _ := nat.NewPort(proto, port) 18 return p 19 } 20 21 func TestCompare(t *testing.T) { 22 ports1 := make(nat.PortSet) 23 ports1[newPortNoError("tcp", "1111")] = struct{}{} 24 ports1[newPortNoError("tcp", "2222")] = struct{}{} 25 ports2 := make(nat.PortSet) 26 ports2[newPortNoError("tcp", "3333")] = struct{}{} 27 ports2[newPortNoError("tcp", "4444")] = struct{}{} 28 ports3 := make(nat.PortSet) 29 ports3[newPortNoError("tcp", "1111")] = struct{}{} 30 ports3[newPortNoError("tcp", "2222")] = struct{}{} 31 ports3[newPortNoError("tcp", "5555")] = struct{}{} 32 volumes1 := make(map[string]struct{}) 33 volumes1["/test1"] = struct{}{} 34 volumes2 := make(map[string]struct{}) 35 volumes2["/test2"] = struct{}{} 36 volumes3 := make(map[string]struct{}) 37 volumes3["/test1"] = struct{}{} 38 volumes3["/test3"] = struct{}{} 39 envs1 := []string{"ENV1=value1", "ENV2=value2"} 40 envs2 := []string{"ENV1=value1", "ENV3=value3"} 41 entrypoint1 := strslice.StrSlice{"/bin/sh", "-c"} 42 entrypoint2 := strslice.StrSlice{"/bin/sh", "-d"} 43 entrypoint3 := strslice.StrSlice{"/bin/sh", "-c", "echo"} 44 cmd1 := strslice.StrSlice{"/bin/sh", "-c"} 45 cmd2 := strslice.StrSlice{"/bin/sh", "-d"} 46 cmd3 := strslice.StrSlice{"/bin/sh", "-c", "echo"} 47 labels1 := map[string]string{"LABEL1": "value1", "LABEL2": "value2"} 48 labels2 := map[string]string{"LABEL1": "value1", "LABEL2": "value3"} 49 labels3 := map[string]string{"LABEL1": "value1", "LABEL2": "value2", "LABEL3": "value3"} 50 51 sameConfigs := map[*container.Config]*container.Config{ 52 // Empty config 53 {}: {}, 54 // Does not compare hostname, domainname & image 55 { 56 Hostname: "host1", 57 Domainname: "domain1", 58 Image: "image1", 59 User: "user", 60 }: { 61 Hostname: "host2", 62 Domainname: "domain2", 63 Image: "image2", 64 User: "user", 65 }, 66 // only OpenStdin 67 {OpenStdin: false}: {OpenStdin: false}, 68 // only env 69 {Env: envs1}: {Env: envs1}, 70 // only cmd 71 {Cmd: cmd1}: {Cmd: cmd1}, 72 // only labels 73 {Labels: labels1}: {Labels: labels1}, 74 // only exposedPorts 75 {ExposedPorts: ports1}: {ExposedPorts: ports1}, 76 // only entrypoints 77 {Entrypoint: entrypoint1}: {Entrypoint: entrypoint1}, 78 // only volumes 79 {Volumes: volumes1}: {Volumes: volumes1}, 80 } 81 differentConfigs := map[*container.Config]*container.Config{ 82 nil: nil, 83 { 84 Hostname: "host1", 85 Domainname: "domain1", 86 Image: "image1", 87 User: "user1", 88 }: { 89 Hostname: "host1", 90 Domainname: "domain1", 91 Image: "image1", 92 User: "user2", 93 }, 94 // only OpenStdin 95 {OpenStdin: false}: {OpenStdin: true}, 96 {OpenStdin: true}: {OpenStdin: false}, 97 // only env 98 {Env: envs1}: {Env: envs2}, 99 // only cmd 100 {Cmd: cmd1}: {Cmd: cmd2}, 101 // not the same number of parts 102 {Cmd: cmd1}: {Cmd: cmd3}, 103 // only labels 104 {Labels: labels1}: {Labels: labels2}, 105 // not the same number of labels 106 {Labels: labels1}: {Labels: labels3}, 107 // only exposedPorts 108 {ExposedPorts: ports1}: {ExposedPorts: ports2}, 109 // not the same number of ports 110 {ExposedPorts: ports1}: {ExposedPorts: ports3}, 111 // only entrypoints 112 {Entrypoint: entrypoint1}: {Entrypoint: entrypoint2}, 113 // not the same number of parts 114 {Entrypoint: entrypoint1}: {Entrypoint: entrypoint3}, 115 // only volumes 116 {Volumes: volumes1}: {Volumes: volumes2}, 117 // not the same number of labels 118 {Volumes: volumes1}: {Volumes: volumes3}, 119 } 120 for config1, config2 := range sameConfigs { 121 if !compare(config1, config2) { 122 t.Fatalf("Compare should be true for [%v] and [%v]", config1, config2) 123 } 124 } 125 for config1, config2 := range differentConfigs { 126 if compare(config1, config2) { 127 t.Fatalf("Compare should be false for [%v] and [%v]", config1, config2) 128 } 129 } 130 } 131 132 func TestPlatformCompare(t *testing.T) { 133 for _, tc := range []struct { 134 name string 135 builder ocispec.Platform 136 image ocispec.Platform 137 expected bool 138 }{ 139 { 140 name: "same os and arch", 141 builder: ocispec.Platform{Architecture: "amd64", OS: runtime.GOOS}, 142 image: ocispec.Platform{Architecture: "amd64", OS: runtime.GOOS}, 143 expected: true, 144 }, 145 { 146 name: "same os different arch", 147 builder: ocispec.Platform{Architecture: "amd64", OS: runtime.GOOS}, 148 image: ocispec.Platform{Architecture: "arm64", OS: runtime.GOOS}, 149 expected: false, 150 }, 151 { 152 name: "same os smaller host variant", 153 builder: ocispec.Platform{Variant: "v7", Architecture: "arm", OS: runtime.GOOS}, 154 image: ocispec.Platform{Variant: "v8", Architecture: "arm", OS: runtime.GOOS}, 155 expected: false, 156 }, 157 { 158 name: "same os higher host variant", 159 builder: ocispec.Platform{Variant: "v8", Architecture: "arm", OS: runtime.GOOS}, 160 image: ocispec.Platform{Variant: "v7", Architecture: "arm", OS: runtime.GOOS}, 161 expected: true, 162 }, 163 { 164 // Test for https://github.com/moby/moby/issues/47307 165 name: "different build and revision", 166 builder: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.22621"}, 167 image: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"}, 168 expected: true, 169 }, 170 { 171 name: "different revision", 172 builder: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.1234"}, 173 image: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"}, 174 expected: true, 175 }, 176 { 177 name: "different major", 178 builder: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "11.0.17763.5329"}, 179 image: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"}, 180 expected: false, 181 }, 182 { 183 name: "different minor same osver", 184 builder: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"}, 185 image: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.1.17763.5329"}, 186 expected: false, 187 }, 188 { 189 name: "different arch same osver", 190 builder: ocispec.Platform{Architecture: "arm64", OS: "windows", OSVersion: "10.0.17763.5329"}, 191 image: ocispec.Platform{Architecture: "amd64", OS: "windows", OSVersion: "10.0.17763.5329"}, 192 expected: false, 193 }, 194 } { 195 tc := tc 196 // OSVersion comparison is only performed by containerd platform 197 // matcher if built on Windows. 198 if (tc.image.OSVersion != "" || tc.builder.OSVersion != "") && runtime.GOOS != "windows" { 199 continue 200 } 201 202 t.Run(tc.name, func(t *testing.T) { 203 assert.Check(t, is.Equal(comparePlatform(tc.builder, tc.image), tc.expected)) 204 }) 205 } 206 }