github.com/uber/kraken@v0.1.4/lib/dockerregistry/paths_test.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package dockerregistry 15 16 import ( 17 "fmt" 18 "testing" 19 20 "github.com/uber/kraken/core" 21 22 "github.com/stretchr/testify/require" 23 ) 24 25 const _testDigestHex = "ff3a5c916c92643ff77519ffa742d3ec61b7f591b6b7504599d95a4a41134e28" 26 27 func TestBlobsPath(t *testing.T) { 28 d := core.DigestFixture() 29 30 result, err := GetBlobDigest(fmt.Sprintf("/v2/blobs/sha256/%s/%s/data", d.Hex()[:2], d.Hex())) 31 require.NoError(t, err) 32 require.Equal(t, d, result) 33 } 34 35 func TestBlobsPathNoMatch(t *testing.T) { 36 testCases := []struct { 37 name string 38 input string 39 }{ 40 {"empty", ""}, 41 {"more than 2 char digest prefix", fmt.Sprintf("/v2/blobs/sha256/1234/%s/data", _testDigestHex)}, 42 {"invalid path", fmt.Sprintf("/v2/blobs/sha256/1234/%s", _testDigestHex)}, 43 {"invalid char in digest", fmt.Sprintf("/v2/blobs/sha256/3Z/%s/data", _testDigestHex)}, 44 } 45 46 for _, tc := range testCases { 47 t.Run(tc.name, func(t *testing.T) { 48 require := require.New(t) 49 _, err := GetBlobDigest(tc.input) 50 expectedErr := InvalidRegistryPathError{_blobs, tc.input} 51 require.Equal(expectedErr, err) 52 }) 53 } 54 } 55 56 func TestRepositoriesPath(t *testing.T) { 57 testCases := []struct { 58 name string 59 input string 60 repo string 61 }{ 62 {"single namespace manifest", "/v2/repositories/kraken/_manifests", "kraken"}, 63 {"single namespace upload", "/v2/repositories/kraken/_uploads", "kraken"}, 64 {"single namespace layer", "/v2/repositories/kraken/_layers", "kraken"}, 65 {"multiple namespace manifest", "/v2/repositories/namespace-foo/kraken/_manifests", "namespace-foo/kraken"}, 66 } 67 68 for _, tc := range testCases { 69 t.Run(tc.name, func(t *testing.T) { 70 require := require.New(t) 71 repo, err := GetRepo(tc.input) 72 require.NoError(err) 73 require.Equal(tc.repo, repo) 74 }) 75 } 76 } 77 78 func TestRepositoriesPathNoMatch(t *testing.T) { 79 testCases := []struct { 80 name string 81 input string 82 repo string 83 }{ 84 {"empty path", "", ""}, 85 {"no namespace", "/v2/repositories/_manifests", ""}, 86 {"no repositories prefix", "/v2/repo/kraken/_manifests", ""}, 87 {"missing _manifests", "/v2/repositories/kraken/versions", ""}, 88 } 89 90 for _, tc := range testCases { 91 t.Run(tc.name, func(t *testing.T) { 92 require := require.New(t) 93 repo, err := GetRepo(tc.input) 94 require.Equal(tc.repo, repo) 95 expectedErr := InvalidRegistryPathError{_repositories, tc.input} 96 require.Equal(expectedErr, err) 97 }) 98 } 99 } 100 101 func TestLayersPath(t *testing.T) { 102 testCases := []struct { 103 name string 104 input string 105 match bool 106 subtype PathSubType 107 }{ 108 {"empty path", "", false, _invalidPathSubType}, 109 {"missing algo", "kraken/_layers/digest5678/", false, _invalidPathSubType}, 110 {"missing layer or link", "kraken/_layers/sha256/digest5678/", false, _invalidPathSubType}, 111 {"invalid char in digest", "kraken/_layers/sha256/digestZ5678/data", false, _invalidPathSubType}, 112 {"valid data path", "kraken/_layers/sha256/digest5678/data", true, _data}, 113 {"valid link path", "kraken/_layers/sha256/digest5678/link", true, _link}, 114 } 115 116 for _, tc := range testCases { 117 t.Run(tc.name, func(t *testing.T) { 118 require := require.New(t) 119 match, subtype := matchLayersPath(tc.input) 120 require.Equal(tc.match, match) 121 require.Equal(tc.subtype, subtype) 122 }) 123 } 124 } 125 126 func TestLayersPathGetDigest(t *testing.T) { 127 d := core.DigestFixture() 128 129 testCases := []struct { 130 name string 131 input string 132 }{ 133 {"valid data path", fmt.Sprintf("kraken/_layers/sha256/%s/data", d.Hex())}, 134 {"valid link path", fmt.Sprintf("kraken/_layers/sha256/%s/link", d.Hex())}, 135 } 136 137 for _, tc := range testCases { 138 t.Run(tc.name, func(t *testing.T) { 139 result, err := GetLayerDigest(tc.input) 140 require.NoError(t, err) 141 require.Equal(t, d, result) 142 }) 143 } 144 } 145 146 func TestLayersPathGetDigestNoMatch(t *testing.T) { 147 testCases := []struct { 148 name string 149 input string 150 }{ 151 {"empty", ""}, 152 {"missing sha256", fmt.Sprintf("kraken/_layers/%s/", _testDigestHex)}, 153 {"missing data or link", fmt.Sprintf("kraken/_layers/sha256/%s/", _testDigestHex)}, 154 } 155 156 for _, tc := range testCases { 157 t.Run(tc.name, func(t *testing.T) { 158 require := require.New(t) 159 _, err := GetLayerDigest(tc.input) 160 expectedErr := InvalidRegistryPathError{_layers, tc.input} 161 require.Equal(expectedErr, err) 162 }) 163 } 164 } 165 166 func TestManifestsPathMatch(t *testing.T) { 167 testCases := []struct { 168 name string 169 input string 170 match bool 171 subtype PathSubType 172 }{ 173 {"empty", "", false, _invalidPathSubType}, 174 {"incomplete", "kraken/_manifests", false, _invalidPathSubType}, 175 {"missing link for current path", "kraken/_manifests/tags/sometag/current", false, _invalidPathSubType}, 176 {"missing link for manifest path", "kraken/_manifests/revisions/sha256/manifestdigest", false, _invalidPathSubType}, 177 {"valid manifest link", "kraken/_manifests/revisions/sha256/manifestdigest/link", true, _revisions}, 178 {"valid tag link", "kraken/_manifests/tags/sometag/current/link", true, _tags}, 179 {"valid tags", "kraken/_manifests/tags", true, _tags}, 180 } 181 182 for _, tc := range testCases { 183 t.Run(tc.name, func(t *testing.T) { 184 require := require.New(t) 185 match, subtype := matchManifestsPath(tc.input) 186 require.Equal(tc.match, match) 187 require.Equal(tc.subtype, subtype) 188 }) 189 } 190 } 191 192 func TestManifestsPathGetDigest(t *testing.T) { 193 d := core.DigestFixture() 194 195 testCases := []struct { 196 name string 197 input string 198 }{ 199 {"valid tag digest", fmt.Sprintf("kraken/_manifests/tags/sometag/index/sha256/%s/link", d.Hex())}, 200 {"valid revision digest", fmt.Sprintf("kraken/_manifests/revisions/sha256/%s/link", d.Hex())}, 201 } 202 for _, tc := range testCases { 203 t.Run(tc.name, func(t *testing.T) { 204 result, err := GetManifestDigest(tc.input) 205 require.NoError(t, err) 206 require.Equal(t, d, result) 207 }) 208 } 209 } 210 211 func TestManifestsPathGetDigestNoMatch(t *testing.T) { 212 testCases := []struct { 213 name string 214 input string 215 }{ 216 {"empty", ""}, 217 {"incomplete", "kraken/_manifests"}, 218 {"missing link", "kraken/_manifests/tags/sometag/current"}, 219 {"no digest in tag", "kraken/_manifests/sometag/link"}, 220 {"no digest in current", "kraken/_manifests/tags/sometag/current/link"}, 221 } 222 223 for _, tc := range testCases { 224 t.Run(tc.name, func(t *testing.T) { 225 require := require.New(t) 226 _, err := GetManifestDigest(tc.input) 227 expectedErr := InvalidRegistryPathError{_manifests, tc.input} 228 require.Equal(expectedErr, err) 229 }) 230 } 231 } 232 233 func TestManifestsPathGetTag(t *testing.T) { 234 testCases := []struct { 235 name string 236 input string 237 tag string 238 isCurrent bool 239 }{ 240 {"valid tag index", "kraken/_manifests/tags/sometag/index/sha256/manifestdigest/link", "sometag", false}, 241 {"valid tag current", "kraken/_manifests/tags/sometag/current/link", "sometag", true}, 242 } 243 244 for _, tc := range testCases { 245 t.Run(fmt.Sprintf("GetRepositoriesRepo %s", tc.name), func(t *testing.T) { 246 require := require.New(t) 247 tag, isCurrent, err := GetManifestTag(tc.input) 248 require.NoError(err) 249 require.Equal(tc.tag, tag) 250 require.Equal(tc.isCurrent, isCurrent) 251 }) 252 } 253 } 254 255 func TestManifestsPathGetTagNoMatch(t *testing.T) { 256 testCases := []struct { 257 name string 258 input string 259 tag string 260 isCurrent bool 261 }{ 262 {"empty", "", "", false}, 263 {"incomplete", "kraken/_manifests", "", false}, 264 {"missing link", "kraken/_manifests/tags/sometag/current", "", false}, 265 {"missing current", "kraken/_manifests/sometag/link", "", false}, 266 {"missing algo", "kraken/_manifests/revisions/manifestdigest/link", "", false}, 267 {"more than one tag in path", "kraken/_manifests/tags/sometag/sometag/index/sha256/manifestdigest/link", "", false}, 268 } 269 270 for _, tc := range testCases { 271 t.Run(tc.name, func(t *testing.T) { 272 require := require.New(t) 273 tag, isCurrent, err := GetManifestTag(tc.input) 274 require.Equal(tc.tag, tag) 275 require.Equal(tc.isCurrent, isCurrent) 276 expectedErr := InvalidRegistryPathError{_manifests, tc.input} 277 require.Equal(expectedErr, err) 278 }) 279 } 280 } 281 282 func TestUploadsPathMatch(t *testing.T) { 283 testCases := []struct { 284 name string 285 input string 286 match bool 287 subtype PathSubType 288 }{ 289 {"empty", "", false, _invalidPathSubType}, 290 {"incomplete", "kraken/_uploads", false, _invalidPathSubType}, 291 {"missing uuid", "kraken/_uploads/data", false, _invalidPathSubType}, 292 {"extra suffix after data", "kraken/_uploads/uuid/data/extra", false, _invalidPathSubType}, 293 {"extra suffix after startedat", "kraken/_uploads/uuid/startedat/extra", false, _invalidPathSubType}, 294 {"missing digest", "kraken/_uploads/uuid/hashstates", false, _invalidPathSubType}, 295 {"invalid offset", "kraken/_uploads/uuid/hashstates/sha256/a", false, _invalidPathSubType}, 296 {"valid data", "kraken/_uploads/uuid/data", true, _data}, 297 {"valid startedat", "kraken/_uploads/uuid/startedat", true, _startedat}, 298 {"valid hashstates with offset", "kraken/_uploads/uuid/hashstates/sha256/0", true, _hashstates}, 299 {"valid hashstate without offset", "kraken/_uploads/uuid/hashstates/sha256", true, _hashstates}, 300 } 301 302 for _, tc := range testCases { 303 t.Run(tc.name, func(t *testing.T) { 304 require := require.New(t) 305 match, subtype := matchUploadsPath(tc.input) 306 require.Equal(tc.match, match) 307 require.Equal(tc.subtype, subtype) 308 }) 309 } 310 } 311 312 func TestUploadsPathGetUUID(t *testing.T) { 313 testCases := []struct { 314 name string 315 input string 316 uuid string 317 }{ 318 {"valid data", "kraken/_uploads/uuid/data", "uuid"}, 319 {"valid startedat", "kraken/_uploads/uuid/startedat", "uuid"}, 320 {"valid hashstates", "kraken/_uploads/uuid/hashstates/sha256", "uuid"}, 321 {"valid hashstates with offset", "kraken/_uploads/uuid/hashstates/sha256/0", "uuid"}, 322 } 323 324 for _, tc := range testCases { 325 t.Run(tc.name, func(t *testing.T) { 326 require := require.New(t) 327 uuid, err := GetUploadUUID(tc.input) 328 require.NoError(err) 329 require.Equal(tc.uuid, uuid) 330 }) 331 } 332 } 333 334 func TestUploadsPathGetUUIDNoMatch(t *testing.T) { 335 testCases := []struct { 336 name string 337 input string 338 uuid string 339 }{ 340 {"empty", "", ""}, 341 {"incomplete", "kraken/_uploads", ""}, 342 {"missing uuid", "kraken/_uploads/data", ""}, 343 {"extra suffix after data", "kraken/_uploads/uuid/data/extra", ""}, 344 {"missing algo", "kraken/_uploads/uuid/hashstates", ""}, 345 {"invalid offset", "kraken/_uploads/uuid/hashstates/sha256/a", ""}, 346 {"multiple uuid", "kraken/_uploads/uuid/uuid/data", ""}, 347 } 348 349 for _, tc := range testCases { 350 t.Run(tc.name, func(t *testing.T) { 351 require := require.New(t) 352 uuid, err := GetUploadUUID(tc.input) 353 require.Equal(tc.uuid, uuid) 354 expectedErr := InvalidRegistryPathError{_uploads, tc.input} 355 require.Equal(expectedErr, err) 356 }) 357 } 358 } 359 360 func TestUploadsPathGetAlgoAndOffset(t *testing.T) { 361 testCases := []struct { 362 name string 363 input string 364 algo string 365 offset string 366 }{ 367 {"offest 0", "kraken/_uploads/uuid/hashstates/sha256/0", "sha256", "0"}, 368 {"offest 1234", "kraken/_uploads/uuid/hashstates/sha256/1234", "sha256", "1234"}, 369 } 370 371 for _, tc := range testCases { 372 t.Run(tc.name, func(t *testing.T) { 373 require := require.New(t) 374 algo, offset, err := GetUploadAlgoAndOffset(tc.input) 375 require.NoError(err) 376 require.Equal(tc.algo, algo) 377 require.Equal(tc.offset, offset) 378 }) 379 } 380 } 381 382 func TestUploadsPathGetAlgoAndOffsetNoMatch(t *testing.T) { 383 testCases := []struct { 384 name string 385 input string 386 algo string 387 offset string 388 }{ 389 {"empty", "", "", ""}, 390 {"missing algo", "kraken/_uploads/uuid/hashstates", "", ""}, 391 {"missing offset", "kraken/_uploads/uuid/hashstates/sha256", "", ""}, 392 {"invalid offset", "kraken/_uploads/uuid/hashstates/sha256/a", "", ""}, 393 } 394 395 for _, tc := range testCases { 396 t.Run(tc.name, func(t *testing.T) { 397 require := require.New(t) 398 algo, offset, err := GetUploadAlgoAndOffset(tc.input) 399 require.Equal(tc.algo, algo) 400 require.Equal(tc.offset, offset) 401 expectedErr := InvalidRegistryPathError{_uploads, tc.input} 402 require.Equal(expectedErr, err) 403 }) 404 } 405 }