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  }