package registry import ( "testing" cerrdefs "github.com/containerd/errdefs" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestValidateMirror(t *testing.T) { tests := []struct { input string output string expectedErr string }{ // Valid cases { input: "http://mirror-1.example.com", output: "http://mirror-1.example.com/", }, { input: "http://mirror-1.example.com/", output: "http://mirror-1.example.com/", }, { input: "https://mirror-1.example.com", output: "https://mirror-1.example.com/", }, { input: "https://mirror-1.example.com/", output: "https://mirror-1.example.com/", }, { input: "http://localhost", output: "http://localhost/", }, { input: "https://localhost", output: "https://localhost/", }, { input: "http://localhost:5000", output: "http://localhost:5000/", }, { input: "https://localhost:5000", output: "https://localhost:5000/", }, { input: "http://127.0.0.1", output: "http://127.0.0.1/", }, { input: "https://127.0.0.1", output: "https://127.0.0.1/", }, { input: "http://127.0.0.1:5000", output: "http://127.0.0.1:5000/", }, { input: "https://127.0.0.1:5000", output: "https://127.0.0.1:5000/", }, { input: "http://mirror-1.example.com/v1/", output: "http://mirror-1.example.com/v1/", }, { input: "https://mirror-1.example.com/v1/", output: "https://mirror-1.example.com/v1/", }, // Invalid cases { input: "!invalid!://%as%", expectedErr: `invalid mirror: "!invalid!://%as%" is not a valid URI: parse "!invalid!://%as%": first path segment in URL cannot contain colon`, }, { input: "mirror-1.example.com", expectedErr: `invalid mirror: no scheme specified for "mirror-1.example.com": must use either 'https://' or 'http://'`, }, { input: "mirror-1.example.com:5000", expectedErr: `invalid mirror: no scheme specified for "mirror-1.example.com:5000": must use either 'https://' or 'http://'`, }, { input: "ftp://mirror-1.example.com", expectedErr: `invalid mirror: unsupported scheme "ftp" in "ftp://mirror-1.example.com": must use either 'https://' or 'http://'`, }, { input: "http://mirror-1.example.com/?q=foo", expectedErr: `invalid mirror: query or fragment at end of the URI "http://mirror-1.example.com/?q=foo"`, }, { input: "http://mirror-1.example.com/v1/?q=foo", expectedErr: `invalid mirror: query or fragment at end of the URI "http://mirror-1.example.com/v1/?q=foo"`, }, { input: "http://mirror-1.example.com/v1/?q=foo#frag", expectedErr: `invalid mirror: query or fragment at end of the URI "http://mirror-1.example.com/v1/?q=foo#frag"`, }, { input: "http://mirror-1.example.com?q=foo", expectedErr: `invalid mirror: query or fragment at end of the URI "http://mirror-1.example.com?q=foo"`, }, { input: "https://mirror-1.example.com#frag", expectedErr: `invalid mirror: query or fragment at end of the URI "https://mirror-1.example.com#frag"`, }, { input: "https://mirror-1.example.com/#frag", expectedErr: `invalid mirror: query or fragment at end of the URI "https://mirror-1.example.com/#frag"`, }, { input: "http://foo:bar@mirror-1.example.com/", expectedErr: `invalid mirror: username/password not allowed in URI "http://foo:xxxxx@mirror-1.example.com/"`, }, { input: "https://mirror-1.example.com/v1/#frag", expectedErr: `invalid mirror: query or fragment at end of the URI "https://mirror-1.example.com/v1/#frag"`, }, { input: "https://mirror-1.example.com?q", expectedErr: `invalid mirror: query or fragment at end of the URI "https://mirror-1.example.com?q"`, }, } for _, tc := range tests { t.Run(tc.input, func(t *testing.T) { out, err := ValidateMirror(tc.input) if tc.expectedErr != "" { assert.Error(t, err, tc.expectedErr) } else { assert.NilError(t, err) } assert.Check(t, is.Equal(out, tc.output)) }) } } func TestLoadInsecureRegistries(t *testing.T) { testCases := []struct { registries []string index string err string }{ { registries: []string{"127.0.0.1"}, index: "127.0.0.1", }, { registries: []string{"127.0.0.1:8080"}, index: "127.0.0.1:8080", }, { registries: []string{"2001:db8::1"}, index: "2001:db8::1", }, { registries: []string{"[2001:db8::1]:80"}, index: "[2001:db8::1]:80", }, { registries: []string{"http://myregistry.example.com"}, index: "myregistry.example.com", }, { registries: []string{"https://myregistry.example.com"}, index: "myregistry.example.com", }, { registries: []string{"HTTP://myregistry.example.com"}, index: "myregistry.example.com", }, { registries: []string{"svn://myregistry.example.com"}, err: "insecure registry svn://myregistry.example.com should not contain '://'", }, { registries: []string{"-invalid-registry"}, err: "Cannot begin or end with a hyphen", }, { registries: []string{`mytest-.com`}, err: `insecure registry mytest-.com is not valid: invalid host "mytest-.com"`, }, { registries: []string{`1200:0000:AB00:1234:0000:2552:7777:1313:8080`}, err: `insecure registry 1200:0000:AB00:1234:0000:2552:7777:1313:8080 is not valid: invalid host "1200:0000:AB00:1234:0000:2552:7777:1313:8080"`, }, { registries: []string{`myregistry.example.com:500000`}, err: `insecure registry myregistry.example.com:500000 is not valid: invalid port "500000"`, }, { registries: []string{`"myregistry.example.com"`}, err: `insecure registry "myregistry.example.com" is not valid: invalid host "\"myregistry.example.com\""`, }, { registries: []string{`"myregistry.example.com:5000"`}, err: `insecure registry "myregistry.example.com:5000" is not valid: invalid host "\"myregistry.example.com"`, }, } for _, testCase := range testCases { config := &serviceConfig{} err := config.loadInsecureRegistries(testCase.registries) if testCase.err == "" { if err != nil { t.Fatalf("expect no error, got '%s'", err) } match := false for index := range config.IndexConfigs { if index == testCase.index { match = true } } if !match { t.Fatalf("expect index configs to contain '%s', got %+v", testCase.index, config.IndexConfigs) } } else { if err == nil { t.Fatalf("expect error '%s', got no error", testCase.err) } assert.ErrorContains(t, err, testCase.err) assert.Check(t, cerrdefs.IsInvalidArgument(err)) } } } func TestNewServiceConfig(t *testing.T) { tests := []struct { doc string opts ServiceOptions errStr string }{ { doc: "empty config", }, { doc: "invalid mirror", opts: ServiceOptions{ Mirrors: []string{"example.com:5000"}, }, errStr: `invalid mirror: no scheme specified for "example.com:5000": must use either 'https://' or 'http://'`, }, { doc: "valid mirror", opts: ServiceOptions{ Mirrors: []string{"https://example.com:5000"}, }, }, { doc: "invalid insecure registry", opts: ServiceOptions{ InsecureRegistries: []string{"[fe80::]/64"}, }, errStr: `insecure registry [fe80::]/64 is not valid: invalid host "[fe80::]/64"`, }, { doc: "valid insecure registry", opts: ServiceOptions{ InsecureRegistries: []string{"102.10.8.1/24"}, }, }, } for _, tc := range tests { t.Run(tc.doc, func(t *testing.T) { _, err := newServiceConfig(tc.opts) if tc.errStr != "" { assert.Check(t, is.Error(err, tc.errStr)) assert.Check(t, cerrdefs.IsInvalidArgument(err)) } else { assert.Check(t, err) } }) } } func TestValidateIndexName(t *testing.T) { valid := []struct { index string expect string }{ { index: "index.docker.io", expect: "docker.io", }, { index: "example.com", expect: "example.com", }, { index: "127.0.0.1:8080", expect: "127.0.0.1:8080", }, { index: "mytest-1.com", expect: "mytest-1.com", }, { index: "mirror-1.example.com/v1/?q=foo", expect: "mirror-1.example.com/v1/?q=foo", }, } for _, testCase := range valid { result, err := ValidateIndexName(testCase.index) if assert.Check(t, err) { assert.Check(t, is.Equal(testCase.expect, result)) } } } func TestValidateIndexNameWithError(t *testing.T) { invalid := []struct { index string err string }{ { index: "docker.io-", err: "invalid index name (docker.io-). Cannot begin or end with a hyphen", }, { index: "-example.com", err: "invalid index name (-example.com). Cannot begin or end with a hyphen", }, { index: "mirror-1.example.com/v1/?q=foo-", err: "invalid index name (mirror-1.example.com/v1/?q=foo-). Cannot begin or end with a hyphen", }, } for _, testCase := range invalid { _, err := ValidateIndexName(testCase.index) assert.Check(t, is.Error(err, testCase.err)) assert.Check(t, cerrdefs.IsInvalidArgument(err)) } }