Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/mirror/README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ d8 mirror push <images-bundle-path> <registry> [flags]
### Arguments

- `<images-bundle-path>` - Path to the directory containing the bundle created by `d8 mirror pull`
- `<registry>` - Target registry address (format: `registry-host[:port]/path`)
- `<registry>` - Target registry address (format: `registry-host[:port]/path`, e.g. `registry.corp.local:5000/deckhouse`)

### Flags

Expand Down
2 changes: 1 addition & 1 deletion internal/mirror/cmd/pull/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func AddFlags(flagSet *pflag.FlagSet) {
&SourceRegistryRepo,
"source",
EnterpriseEditionRepo,
"Source registry to pull Deckhouse images from.",
"Source registry to pull Deckhouse images from (format: registry-host[:port]/path).",
)
flagSet.StringVar(
&SourceRegistryLogin,
Expand Down
19 changes: 15 additions & 4 deletions internal/mirror/cmd/pull/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,26 @@ func validateSourceRegistry() error {
return nil // Default is fine
}

source := pullflags.SourceRegistryRepo

// Check the "host:port" format of the user-provided repository URL.
// It must have a path after the host and port.
if _, repoPath, _ := strings.Cut(source, "/"); repoPath == "" {
return fmt.Errorf(
"--source %q is missing the repository path: expected format registry-host[:port]/path, e.g. %q",
source, strings.TrimRight(source, "/")+"/deckhouse/ee",
)
}

// We first validate that passed repository reference is correct and can be parsed
if _, err := name.NewRepository(pullflags.SourceRegistryRepo); err != nil {
return fmt.Errorf("Validate registry address: %w", err)
if _, err := name.NewRepository(source); err != nil {
return fmt.Errorf("--source %q is not a valid registry address: %w", source, err)
}

// Then we parse it as URL to validate that it contains everything we need
registryURL, err := url.ParseRequestURI("docker://" + pullflags.SourceRegistryRepo)
registryURL, err := url.ParseRequestURI("docker://" + source)
if err != nil {
return fmt.Errorf("Validate source registry parameter: %w", err)
return fmt.Errorf("Parse --source registry address %q: %w", source, err)
}

if registryURL.Host == "" {
Expand Down
14 changes: 13 additions & 1 deletion internal/mirror/cmd/pull/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,19 @@ func TestValidationValidateSourceRegistry(t *testing.T) {
name: "invalid registry format - no path",
registry: "registry.example.com",
expectError: true,
errorMsg: "no registry path",
errorMsg: "is missing the repository path",
},
{
name: "host with port but no path",
registry: "localhost:5000",
expectError: true,
errorMsg: `--source "localhost:5000" is missing the repository path: expected format registry-host[:port]/path, e.g. "localhost:5000/deckhouse/ee"`,
},
{
name: "trailing slash without path",
registry: "registry.example.com/",
expectError: true,
errorMsg: "is missing the repository path",
},
{
name: "invalid registry - malformed",
Expand Down
13 changes: 11 additions & 2 deletions internal/mirror/cmd/push/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,15 +215,24 @@ func parseAndValidateRegistryURLArg(registryArg string) error {
return errors.New("<registry> argument is empty")
}

// Check the "host:port" format of the user-provided repository URL.
// It must have a path after the host and port.
if _, repoPath, _ := strings.Cut(registry, "/"); repoPath == "" {
return fmt.Errorf(
"<registry> %q is missing the repository path (pushing to a registry root is not supported): expected format registry-host[:port]/path, e.g. %q",
registry, strings.TrimRight(registry, "/")+"/deckhouse",
)
}

// We first validate that passed repository reference is correct and can be parsed
if _, err := name.NewRepository(registry); err != nil {
return fmt.Errorf("Validate registry address: %w", err)
return fmt.Errorf("<registry> %q is not a valid registry address: %w", registry, err)
}

// Then we parse it as URL to validate that it contains everything we need
registryURL, err := url.ParseRequestURI("docker://" + registry)
if err != nil {
return fmt.Errorf("Validate registry address: %w", err)
return fmt.Errorf("Parse <registry> address %q: %w", registry, err)
}

RegistryHost = registryURL.Host
Expand Down
49 changes: 49 additions & 0 deletions internal/mirror/cmd/push/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,55 @@ func TestParseAndValidateRegistryURLArg(t *testing.T) {
name: "no repository path",
registry: "registry.example.com",
expectError: true,
errorMsg: "is missing the repository path",
},
{
name: "host with port but no repository path",
registry: "localhost:5000",
expectError: true,
errorMsg: `<registry> "localhost:5000" is missing the repository path (pushing to a registry root is not supported): expected format registry-host[:port]/path, e.g. "localhost:5000/deckhouse"`,
},
{
name: "scheme is stripped before the missing-path check",
registry: "http://localhost:5000",
expectError: true,
errorMsg: `e.g. "localhost:5000/deckhouse"`,
},
{
name: "trailing slash without repository path",
registry: "localhost:5000/",
expectError: true,
errorMsg: "is missing the repository path",
},
{
name: "uppercase repository is a character error, not a missing path",
registry: "localhost:5000/UPPER",
expectError: true,
errorMsg: "can only contain",
},
{
name: "no registry host",
registry: "/deckhouse/ee",
expectError: true,
errorMsg: "no registry host",
},
{
name: "IP with port and path",
registry: "192.168.1.1:5000/repo",
wantHost: "192.168.1.1:5000",
wantPath: "/repo",
},
{
name: "host without dot or colon stays accepted",
registry: "myregistry/repo",
wantHost: "myregistry",
wantPath: "/repo",
},
{
name: "double slash before repo stays accepted",
registry: "localhost:5000//repo",
wantHost: "localhost:5000",
wantPath: "//repo",
},
}

Expand Down
Loading