diff --git a/cmd/compose/compose.go b/cmd/compose/compose.go index 7fb0991efd1..83d84fde4d1 100644 --- a/cmd/compose/compose.go +++ b/cmd/compose/compose.go @@ -348,9 +348,7 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, b Compatibility: o.Compatibility, ProjectOptionsFns: po, LoadListeners: []api.LoadListener{metricsListener}, - OCI: api.OCIOptions{ - InsecureRegistries: o.insecureRegistries, - }, + OCI: o.ociOptions(), } project, err := backend.LoadProject(ctx, loadOpts) @@ -369,10 +367,21 @@ func (o *ProjectOptions) remoteLoaders(dockerCli command.Cli) []loader.ResourceL return nil } git := remote.NewGitRemoteLoader(dockerCli, o.Offline) - oci := remote.NewOCIRemoteLoader(dockerCli, o.Offline, api.OCIOptions{}) + oci := remote.NewOCIRemoteLoader(dockerCli, o.Offline, o.ociOptions()) return []loader.ResourceLoader{git, oci} } +// ociOptions builds the OCI loader configuration from the project options. +// Both the primary project load and the loaders returned by remoteLoaders +// must use this so the --insecure-registry flag is honored on every path +// that pulls an OCI compose artifact (e.g. the interpolation-variable +// re-load that `up` runs via ToModel). See docker/compose#13824. +func (o *ProjectOptions) ociOptions() api.OCIOptions { + return api.OCIOptions{ + InsecureRegistries: o.insecureRegistries, + } +} + func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) { opts := []cli.ProjectOptionsFn{ cli.WithWorkingDirectory(o.ProjectDir), diff --git a/cmd/compose/compose_remote_loaders_test.go b/cmd/compose/compose_remote_loaders_test.go new file mode 100644 index 00000000000..7593f95ef61 --- /dev/null +++ b/cmd/compose/compose_remote_loaders_test.go @@ -0,0 +1,67 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package compose + +import ( + "testing" + + "go.uber.org/mock/gomock" + "gotest.tools/v3/assert" + + "github.com/docker/compose/v5/pkg/mocks" +) + +// insecureRegistriesLoader is implemented by the OCI remote loader; used to +// observe which registries a loader will contact over plain HTTP. +type insecureRegistriesLoader interface { + InsecureRegistries() []string +} + +// TestRemoteLoaders_PropagatesInsecureRegistries guards docker/compose#13824: +// the OCI loader returned by remoteLoaders must carry the --insecure-registry +// values. This loader backs the ToModel re-load that `up` runs (via +// checksForRemoteStack) to prompt for interpolation variables; dropping the +// flag here made `docker compose -f oci://localhost:5000/... up` fail against +// an insecure registry unless --yes was passed. +func TestRemoteLoaders_PropagatesInsecureRegistries(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + // remoteLoaders only stores dockerCli in the loaders; it never calls it, + // so a bare mock with no expectations is sufficient. + dockerCli := mocks.NewMockCli(ctrl) + + o := &ProjectOptions{insecureRegistries: []string{"localhost:5000"}} + + var got []string + found := false + for _, l := range o.remoteLoaders(dockerCli) { + if oci, ok := l.(insecureRegistriesLoader); ok { + found = true + got = oci.InsecureRegistries() + } + } + + assert.Assert(t, found, "remoteLoaders returned no OCI loader") + assert.DeepEqual(t, []string{"localhost:5000"}, got) +} + +// TestOCIOptions_PropagatesInsecureRegistries covers the shared helper that +// both OCI load paths (LoadProject and remoteLoaders) rely on. +func TestOCIOptions_PropagatesInsecureRegistries(t *testing.T) { + o := &ProjectOptions{insecureRegistries: []string{"localhost:5000", "registry.test:443"}} + assert.DeepEqual(t, []string{"localhost:5000", "registry.test:443"}, o.ociOptions().InsecureRegistries) +} diff --git a/pkg/remote/oci.go b/pkg/remote/oci.go index 7b663d770a8..93a44d44b1f 100644 --- a/pkg/remote/oci.go +++ b/pkg/remote/oci.go @@ -113,6 +113,14 @@ func (g *ociRemoteLoader) Accept(path string) bool { return strings.HasPrefix(path, OciPrefix) } +// InsecureRegistries returns the registry hosts this loader contacts over +// plain HTTP. Exposed so callers/tests can verify the --insecure-registry +// flag is propagated into the loader on every OCI load path +// (docker/compose#13824). +func (g *ociRemoteLoader) InsecureRegistries() []string { + return g.insecureRegistries +} + //nolint:gocyclo func (g *ociRemoteLoader) Load(ctx context.Context, path string) (string, error) { enabled, err := ociRemoteLoaderEnabled()