fix(swarm): no image error for Docker private registry [BE-13217] (#3190)

This commit is contained in:
Oscar Zhou
2026-07-15 18:48:30 +12:00
committed by GitHub
parent 7edef01302
commit b485de295b
2 changed files with 77 additions and 1 deletions
+20 -1
View File
@@ -239,10 +239,12 @@ func deployStack(ctx context.Context, dockerCLI *command.DockerCli, filePaths []
return err
}
registries := normalizeRegistryServerAddresses(options.Registries)
return deployServices(
ctx,
dockerCLI.Client(),
options.Registries,
registries,
services,
namespace,
options.PullImage,
@@ -396,6 +398,23 @@ func createConfigs(ctx context.Context, apiClient client.APIClient, configs []sw
return nil
}
// normalizeRegistryServerAddresses returns a copy of registries with each ServerAddress
// rewritten so it matches the domain encodeRegistryAuth resolves for docker.io images
// (dockerregistry.IndexServer).
func normalizeRegistryServerAddresses(registries []configtypes.AuthConfig) []configtypes.AuthConfig {
normalized := make([]configtypes.AuthConfig, len(registries))
for i, r := range registries {
if r.ServerAddress == "" || r.ServerAddress == dockerregistry.DefaultNamespace {
r.ServerAddress = dockerregistry.IndexServer
}
normalized[i] = r
}
return normalized
}
// encodeRegistryAuth finds the registry credentials for the given image and returns
// the base64-encoded auth string expected by the Docker service API.
// Returns an empty string (no error) when no matching credentials are found.
+57
View File
@@ -256,6 +256,63 @@ func Test_encodeRegistryAuth(t *testing.T) {
}
}
func Test_normalizeRegistryServerAddresses(t *testing.T) {
t.Parallel()
tests := []struct {
name string
registries []configtypes.AuthConfig
expectedReturned []configtypes.AuthConfig
}{
{
name: "empty ServerAddress is normalized to IndexServer",
registries: []configtypes.AuthConfig{{ServerAddress: "", Username: "user"}},
expectedReturned: []configtypes.AuthConfig{{ServerAddress: dockerregistry.IndexServer, Username: "user"}},
},
{
name: "docker.io default namespace is normalized to IndexServer",
registries: []configtypes.AuthConfig{{ServerAddress: dockerregistry.DefaultNamespace, Username: "user"}},
expectedReturned: []configtypes.AuthConfig{{ServerAddress: dockerregistry.IndexServer, Username: "user"}},
},
{
name: "custom registry address is left untouched",
registries: []configtypes.AuthConfig{{ServerAddress: "registry.example.com", Username: "user"}},
expectedReturned: []configtypes.AuthConfig{{ServerAddress: "registry.example.com", Username: "user"}},
},
{
name: "IndexServer address is left untouched",
registries: []configtypes.AuthConfig{{ServerAddress: dockerregistry.IndexServer, Username: "user"}},
expectedReturned: []configtypes.AuthConfig{{ServerAddress: dockerregistry.IndexServer, Username: "user"}},
},
{
name: "mix of registries normalizes only the matching ones",
registries: []configtypes.AuthConfig{
{ServerAddress: "", Username: "empty"},
{ServerAddress: dockerregistry.DefaultNamespace, Username: "default-namespace"},
{ServerAddress: "registry.example.com", Username: "custom"},
},
expectedReturned: []configtypes.AuthConfig{
{ServerAddress: dockerregistry.IndexServer, Username: "empty"},
{ServerAddress: dockerregistry.IndexServer, Username: "default-namespace"},
{ServerAddress: "registry.example.com", Username: "custom"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
original := slices.Clone(tt.registries)
got := normalizeRegistryServerAddresses(tt.registries)
require.Equal(t, tt.expectedReturned, got)
require.Equal(t, original, tt.registries, "input slice must not be mutated")
})
}
}
func Test_getConfig(t *testing.T) {
dir := t.TempDir()