github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/remotes/docker/config/hosts_test.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package config 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "net/http" 24 "path/filepath" 25 "testing" 26 27 "github.com/containerd/containerd/log/logtest" 28 "github.com/containerd/containerd/remotes/docker" 29 ) 30 31 const allCaps = docker.HostCapabilityPull | docker.HostCapabilityResolve | docker.HostCapabilityPush 32 33 func TestDefaultHosts(t *testing.T) { 34 ctx := logtest.WithT(context.Background(), t) 35 resolve := ConfigureHosts(ctx, HostOptions{}) 36 37 for _, tc := range []struct { 38 host string 39 expected []docker.RegistryHost 40 }{ 41 { 42 host: "docker.io", 43 expected: []docker.RegistryHost{ 44 { 45 Scheme: "https", 46 Host: "registry-1.docker.io", 47 Path: "/v2", 48 Capabilities: allCaps, 49 }, 50 }, 51 }, 52 } { 53 hosts, err := resolve(tc.host) 54 if err != nil { 55 t.Errorf("[%s] resolve failed: %v", tc.host, err) 56 continue 57 } 58 if len(hosts) != len(tc.expected) { 59 t.Errorf("[%s] unexpected number of hosts %d, expected %d", tc.host, len(hosts), len(tc.expected)) 60 continue 61 } 62 for j := range hosts { 63 if !compareRegistryHost(hosts[j], tc.expected[j]) { 64 65 t.Errorf("[%s] [%d] unexpected host %v, expected %v", tc.host, j, hosts[j], tc.expected[j]) 66 break 67 } 68 69 } 70 71 } 72 } 73 74 func TestParseHostFile(t *testing.T) { 75 ctx := logtest.WithT(context.Background(), t) 76 77 const testtoml = ` 78 server = "https://test-default.registry" 79 ca = "/etc/path/default" 80 [header] 81 x-custom-1 = "custom header" 82 83 [host."https://mirror.registry"] 84 capabilities = ["pull"] 85 ca = "/etc/certs/mirror.pem" 86 skip_verify = false 87 [host."https://mirror.registry".header] 88 x-custom-2 = ["value1", "value2"] 89 90 [host."https://mirror-bak.registry/us"] 91 capabilities = ["pull"] 92 skip_verify = true 93 94 [host."http://mirror.registry"] 95 capabilities = ["pull"] 96 97 [host."https://test-1.registry"] 98 capabilities = ["pull", "resolve", "push"] 99 ca = ["/etc/certs/test-1-ca.pem", "/etc/certs/special.pem"] 100 client = [["/etc/certs/client.cert", "/etc/certs/client.key"],["/etc/certs/client.pem", ""]] 101 102 [host."https://test-2.registry"] 103 client = "/etc/certs/client.pem" 104 105 [host."https://test-3.registry"] 106 client = ["/etc/certs/client-1.pem", "/etc/certs/client-2.pem"] 107 ` 108 var tb, fb = true, false 109 expected := []hostConfig{ 110 { 111 scheme: "https", 112 host: "mirror.registry", 113 path: "/v2", 114 capabilities: docker.HostCapabilityPull, 115 caCerts: []string{filepath.FromSlash("/etc/certs/mirror.pem")}, 116 skipVerify: &fb, 117 header: http.Header{"x-custom-2": {"value1", "value2"}}, 118 }, 119 { 120 scheme: "https", 121 host: "mirror-bak.registry", 122 path: "/us/v2", 123 capabilities: docker.HostCapabilityPull, 124 skipVerify: &tb, 125 }, 126 { 127 scheme: "http", 128 host: "mirror.registry", 129 path: "/v2", 130 capabilities: docker.HostCapabilityPull, 131 }, 132 { 133 scheme: "https", 134 host: "test-1.registry", 135 path: "/v2", 136 capabilities: allCaps, 137 caCerts: []string{filepath.FromSlash("/etc/certs/test-1-ca.pem"), filepath.FromSlash("/etc/certs/special.pem")}, 138 clientPairs: [][2]string{ 139 {filepath.FromSlash("/etc/certs/client.cert"), filepath.FromSlash("/etc/certs/client.key")}, 140 {filepath.FromSlash("/etc/certs/client.pem"), ""}, 141 }, 142 }, 143 { 144 scheme: "https", 145 host: "test-2.registry", 146 path: "/v2", 147 capabilities: allCaps, 148 clientPairs: [][2]string{ 149 {filepath.FromSlash("/etc/certs/client.pem")}, 150 }, 151 }, 152 { 153 scheme: "https", 154 host: "test-3.registry", 155 path: "/v2", 156 capabilities: allCaps, 157 clientPairs: [][2]string{ 158 {filepath.FromSlash("/etc/certs/client-1.pem")}, 159 {filepath.FromSlash("/etc/certs/client-2.pem")}, 160 }, 161 }, 162 { 163 scheme: "https", 164 host: "test-default.registry", 165 path: "/v2", 166 capabilities: allCaps, 167 caCerts: []string{filepath.FromSlash("/etc/path/default")}, 168 header: http.Header{"x-custom-1": {"custom header"}}, 169 }, 170 } 171 hosts, err := parseHostsFile(ctx, "", []byte(testtoml)) 172 if err != nil { 173 t.Fatal(err) 174 } 175 176 defer func() { 177 if t.Failed() { 178 t.Log("HostConfigs...\nActual:\n" + printHostConfig(hosts) + "Expected:\n" + printHostConfig(expected)) 179 } 180 }() 181 182 if len(hosts) != len(expected) { 183 t.Fatalf("Unexpected number of hosts %d, expected %d", len(hosts), len(expected)) 184 } 185 186 for i := range hosts { 187 if !compareHostConfig(hosts[i], expected[i]) { 188 t.Fatalf("Mismatch at host %d", i) 189 } 190 } 191 } 192 193 func compareRegistryHost(j, k docker.RegistryHost) bool { 194 if j.Scheme != k.Scheme { 195 return false 196 } 197 if j.Host != k.Host { 198 return false 199 } 200 if j.Path != k.Path { 201 return false 202 } 203 if j.Capabilities != k.Capabilities { 204 return false 205 } 206 // Not comparing TLS configs or authorizations 207 return true 208 } 209 210 func compareHostConfig(j, k hostConfig) bool { 211 if j.scheme != k.scheme { 212 return false 213 } 214 if j.host != k.host { 215 return false 216 } 217 if j.path != k.path { 218 return false 219 } 220 if j.capabilities != k.capabilities { 221 return false 222 } 223 224 if len(j.caCerts) != len(k.caCerts) { 225 return false 226 } 227 for i := range j.caCerts { 228 if j.caCerts[i] != k.caCerts[i] { 229 return false 230 } 231 } 232 if len(j.clientPairs) != len(k.clientPairs) { 233 return false 234 } 235 for i := range j.clientPairs { 236 if j.clientPairs[i][0] != k.clientPairs[i][0] { 237 return false 238 } 239 if j.clientPairs[i][1] != k.clientPairs[i][1] { 240 return false 241 } 242 } 243 if j.skipVerify != nil && k.skipVerify != nil { 244 if *j.skipVerify != *k.skipVerify { 245 return false 246 } 247 } else if j.skipVerify != nil || k.skipVerify != nil { 248 return false 249 } 250 251 if len(j.header) != len(k.header) { 252 return false 253 } 254 for key := range j.header { 255 if len(j.header[key]) != len(k.header[key]) { 256 return false 257 } 258 for i := range j.header[key] { 259 if j.header[key][i] != k.header[key][i] { 260 return false 261 } 262 } 263 } 264 265 return true 266 } 267 268 func printHostConfig(hc []hostConfig) string { 269 b := bytes.NewBuffer(nil) 270 for i := range hc { 271 fmt.Fprintf(b, "\t[%d]\tscheme: %q\n", i, hc[i].scheme) 272 fmt.Fprintf(b, "\t\thost: %q\n", hc[i].host) 273 fmt.Fprintf(b, "\t\tpath: %q\n", hc[i].path) 274 fmt.Fprintf(b, "\t\tcaps: %03b\n", hc[i].capabilities) 275 fmt.Fprintf(b, "\t\tca: %#v\n", hc[i].caCerts) 276 fmt.Fprintf(b, "\t\tclients: %#v\n", hc[i].clientPairs) 277 if hc[i].skipVerify == nil { 278 fmt.Fprintf(b, "\t\tskip-verify: %v\n", hc[i].skipVerify) 279 } else { 280 fmt.Fprintf(b, "\t\tskip-verify: %t\n", *hc[i].skipVerify) 281 } 282 fmt.Fprintf(b, "\t\theader: %#v\n", hc[i].header) 283 } 284 return b.String() 285 }