mirror of
https://github.com/portainer/portainer.git
synced 2026-08-07 10:04:49 +00:00
fix(swarm): no image error for Docker private registry [BE-13217] (#3190)
This commit is contained in:
@@ -239,10 +239,12 @@ func deployStack(ctx context.Context, dockerCLI *command.DockerCli, filePaths []
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registries := normalizeRegistryServerAddresses(options.Registries)
|
||||||
|
|
||||||
return deployServices(
|
return deployServices(
|
||||||
ctx,
|
ctx,
|
||||||
dockerCLI.Client(),
|
dockerCLI.Client(),
|
||||||
options.Registries,
|
registries,
|
||||||
services,
|
services,
|
||||||
namespace,
|
namespace,
|
||||||
options.PullImage,
|
options.PullImage,
|
||||||
@@ -396,6 +398,23 @@ func createConfigs(ctx context.Context, apiClient client.APIClient, configs []sw
|
|||||||
return nil
|
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
|
// encodeRegistryAuth finds the registry credentials for the given image and returns
|
||||||
// the base64-encoded auth string expected by the Docker service API.
|
// the base64-encoded auth string expected by the Docker service API.
|
||||||
// Returns an empty string (no error) when no matching credentials are found.
|
// Returns an empty string (no error) when no matching credentials are found.
|
||||||
|
|||||||
@@ -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) {
|
func Test_getConfig(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user