From ac1b4339f0f214548fbce582af257fdad6891426 Mon Sep 17 00:00:00 2001 From: Roger Peppe Date: Wed, 18 Oct 2023 16:33:59 +0100 Subject: [PATCH] cmd/cue/cmd: wire up modmux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gives the cue command the capability to specify multiple registries. Signed-off-by: Roger Peppe Change-Id: Iccf78185044ff8a19bce27b42926534ae27c6e8e Reviewed-on: https://review-eu.gerrithub.io/c/cue-lang/cue/+/1170886 TryBot-Result: CUEcueckoo Unity-Result: CUE porcuepine Reviewed-by: Daniel Martí --- cmd/cue/cmd/registry.go | 86 ++---------- cmd/cue/cmd/registry_test.go | 77 ----------- cmd/cue/cmd/script_test.go | 29 +++- .../script/registry_invalid_env.txtar | 2 +- .../cmd/testdata/script/registry_mux.txtar | 129 ++++++++++++++++++ 5 files changed, 162 insertions(+), 161 deletions(-) delete mode 100644 cmd/cue/cmd/registry_test.go create mode 100644 cmd/cue/cmd/testdata/script/registry_mux.txtar diff --git a/cmd/cue/cmd/registry.go b/cmd/cue/cmd/registry.go index 4808ed56396..1ebdd9f6e81 100644 --- a/cmd/cue/cmd/registry.go +++ b/cmd/cue/cmd/registry.go @@ -2,16 +2,14 @@ package cmd import ( "fmt" - "net" "os" - "strings" "cuelabs.dev/go/oci/ociregistry" "cuelabs.dev/go/oci/ociregistry/ociclient" - "cuelabs.dev/go/oci/ociregistry/ocifilter" - "cuelabs.dev/go/oci/ociregistry/ociref" "cuelang.org/go/internal/cueexperiment" + "cuelang.org/go/internal/mod/modmux" + "cuelang.org/go/internal/mod/modresolve" ) func getRegistry() (ociregistry.Interface, error) { @@ -23,79 +21,13 @@ func getRegistry() (ociregistry.Interface, error) { } return nil, nil } - if env == "" { - env = "registry.cuelabs.dev" - } - - host, prefix, insecure, err := parseRegistry(env) - if err != nil { - return nil, err - } - r, err := ociclient.New(host, &ociclient.Options{ - Insecure: insecure, - }) - if err != nil { - return nil, fmt.Errorf("cannot make OCI client: %v", err) - } - if prefix != "" { - r = ocifilter.Sub(r, prefix) - } - return r, nil -} - -func parseRegistry(env string) (hostPort, prefix string, insecure bool, err error) { - var suffix string - if i := strings.LastIndex(env, "+"); i > 0 { - suffix = env[i:] - env = env[:i] - } - var r ociref.Reference - if !strings.Contains(env, "/") { - // OCI references don't allow a host name on its own without a repo, - // but we do. - r.Host = env - if !ociref.IsValidHost(r.Host) { - return "", "", false, fmt.Errorf("$CUE_REGISTRY %q is not a valid host name", r.Host) - } - } else { - var err error - r, err = ociref.Parse(env) - if err != nil { - return "", "", false, fmt.Errorf("cannot parse $CUE_REGISTRY: %v", err) - } - if r.Tag != "" || r.Digest != "" { - return "", "", false, fmt.Errorf("$CUE_REGISTRY %q cannot have an associated tag or digest", env) - } - } - if suffix == "" { - if isInsecureHost(r.Host) { - suffix = "+insecure" - } else { - suffix = "+secure" - } - } - switch suffix { - case "+insecure": - insecure = true - case "+secure": - default: - return "", "", false, fmt.Errorf("unknown suffix (%q) to CUE_REGISTRY (need +insecure or +secure)", suffix) - } - return r.Host, r.Repository, insecure, nil -} - -func isInsecureHost(hostPort string) bool { - host, _, err := net.SplitHostPort(hostPort) + resolver, err := modresolve.ParseCUERegistry(env, "registry.cuelabs.dev") if err != nil { - host = hostPort - } - switch host { - case "localhost", - "127.0.0.1", - "::1": - return true + return nil, fmt.Errorf("bad value for $CUE_REGISTRY: %v", err) } - // TODO other clients have logic for RFC1918 too, amongst other - // things. Maybe we should do that too. - return false + return modmux.New(resolver, func(host string, insecure bool) (ociregistry.Interface, error) { + return ociclient.New(host, &ociclient.Options{ + Insecure: insecure, + }) + }), nil } diff --git a/cmd/cue/cmd/registry_test.go b/cmd/cue/cmd/registry_test.go deleted file mode 100644 index e6161251f89..00000000000 --- a/cmd/cue/cmd/registry_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package cmd - -import ( - "testing" - - "github.com/go-quicktest/qt" - - "cuelang.org/go/internal/tdtest" -) - -type registryTest struct { - registry string - wantHost string - wantPrefix string - wantInsecure bool - wantError string -} - -var parseRegistryTests = []registryTest{{ - registry: "registry.cuelabs.dev", - wantHost: "registry.cuelabs.dev", -}, { - registry: "registry.cuelabs.dev+insecure", - wantHost: "registry.cuelabs.dev", - wantInsecure: true, -}, { - registry: "foo.com/bar/baz", - wantHost: "foo.com", - wantPrefix: "bar/baz", - wantInsecure: false, -}, { - registry: "localhost:8080/blah", - wantHost: "localhost:8080", - wantPrefix: "blah", - wantInsecure: true, -}, { - registry: "localhost/blah", - wantError: `cannot parse \$CUE_REGISTRY: reference does not contain host name`, -}, { - registry: "127.0.0.1/blah", - wantHost: "127.0.0.1", - wantPrefix: "blah", - wantInsecure: true, -}, { - registry: "localhost:1324", - wantHost: "localhost:1324", - wantInsecure: true, -}, { - registry: "foo.com/bar:1324", - wantError: `\$CUE_REGISTRY "foo.com/bar:1324" cannot have an associated tag or digest`, -}, { - registry: "foo.com/bar@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", - wantError: `\$CUE_REGISTRY "foo.com/bar@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae" cannot have an associated tag or digest`, -}, { - registry: "foo.com/bar:blah@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", - wantError: `\$CUE_REGISTRY "foo.com/bar:blah@sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae" cannot have an associated tag or digest`, -}, { - registry: "foo.com/bar+baz", - wantError: `unknown suffix \("\+baz"\) to CUE_REGISTRY \(need \+insecure or \+secure\)`, -}, { - registry: "badhost", - wantError: `\$CUE_REGISTRY "badhost" is not a valid host name`, -}} - -func TestParseRegistry(t *testing.T) { - tdtest.Run(t, parseRegistryTests, func(t *tdtest.T, test *registryTest) { - host, prefix, insecure, err := parseRegistry(test.registry) - if test.wantError != "" { - qt.Assert(t, qt.ErrorMatches(err, test.wantError)) - return - } - qt.Assert(t, qt.IsNil(err)) - t.Equal(host, test.wantHost) - t.Equal(prefix, test.wantPrefix) - t.Equal(insecure, test.wantInsecure) - }) -} diff --git a/cmd/cue/cmd/script_test.go b/cmd/cue/cmd/script_test.go index 285d945d400..a0df127fb46 100644 --- a/cmd/cue/cmd/script_test.go +++ b/cmd/cue/cmd/script_test.go @@ -108,12 +108,25 @@ func TestScript(t *testing.T) { "GONOSUMDB=*", // GOPROXY is a private proxy homeEnvName()+"="+home, ) - registryDir := filepath.Join(e.WorkDir, "_registry") - if info, err := os.Stat(registryDir); err == nil && info.IsDir() { + entries, err := os.ReadDir(e.WorkDir) + if err != nil { + return fmt.Errorf("cannot read workdir: %v", err) + } + hasRegistry := false + for _, entry := range entries { + if !entry.IsDir() { + continue + } + regID, ok := strings.CutPrefix(entry.Name(), "_registry") + if !ok { + continue + } // There's a _registry directory. Start a fake registry server to serve // the modules in it. + hasRegistry = true + registryDir := filepath.Join(e.WorkDir, entry.Name()) prefix := "" - if data, err := os.ReadFile(filepath.Join(e.WorkDir, "_registry_prefix")); err == nil { + if data, err := os.ReadFile(filepath.Join(e.WorkDir, "_registry"+regID+"_prefix")); err == nil { prefix = strings.TrimSpace(string(data)) } reg, err := registrytest.New(os.DirFS(registryDir), prefix) @@ -124,14 +137,18 @@ func TestScript(t *testing.T) { prefix = "/" + prefix } e.Vars = append(e.Vars, - "CUE_REGISTRY="+reg.Host()+prefix+"+insecure", + "CUE_REGISTRY"+regID+"="+reg.Host()+prefix+"+insecure", // This enables some tests to construct their own malformed // CUE_REGISTRY values that still refer to the test registry. - "DEBUG_REGISTRY_HOST="+reg.Host(), - "CUE_EXPERIMENT=modules", + "DEBUG_REGISTRY"+regID+"_HOST="+reg.Host(), ) e.Defer(reg.Close) } + if hasRegistry { + e.Vars = append(e.Vars, + "CUE_EXPERIMENT=modules", + ) + } return nil }, Condition: cuetest.Condition, diff --git a/cmd/cue/cmd/testdata/script/registry_invalid_env.txtar b/cmd/cue/cmd/testdata/script/registry_invalid_env.txtar index 633f540c576..1654eb9378f 100644 --- a/cmd/cue/cmd/testdata/script/registry_invalid_env.txtar +++ b/cmd/cue/cmd/testdata/script/registry_invalid_env.txtar @@ -4,7 +4,7 @@ env CUE_REGISTRY=malformed!registry@url cmp stderr expect-stderr -- expect-stderr -- -$CUE_REGISTRY "malformed!registry@url" is not a valid host name +bad value for $CUE_REGISTRY: invalid registry "malformed!registry@url": invalid host name "malformed!registry@url" in registry -- main.cue -- package main import "example.com/e" diff --git a/cmd/cue/cmd/testdata/script/registry_mux.txtar b/cmd/cue/cmd/testdata/script/registry_mux.txtar new file mode 100644 index 00000000000..7fdb5a30a30 --- /dev/null +++ b/cmd/cue/cmd/testdata/script/registry_mux.txtar @@ -0,0 +1,129 @@ +env CUE_REGISTRY=${CUE_REGISTRY1},baz.org=$CUE_REGISTRY2 +exec cue eval . +cmp stdout expect-stdout +-- expect-stdout -- +main: "main" +"foo.com/bar/hello@v0": "v0.2.3" +"bar.com@v0": "v0.5.0" +"baz.org@v0": "v0.10.1 in registry2" +"example.com@v0": "v0.0.1" +-- cue.mod/module.cue -- +module: "main.org" + +deps: "example.com@v0": v: "v0.0.1" + +-- main.cue -- +package main +import "example.com@v0:main" + +main + +-- _registry1/example.com_v0.0.1/cue.mod/module.cue -- +module: "example.com@v0" +deps: { + "foo.com/bar/hello@v0": v: "v0.2.3" + "bar.com@v0": v: "v0.5.0" +} + +-- _registry1/example.com_v0.0.1/top.cue -- +package main + +// Note: import without a major version takes +// the major version from the module.cue file. +import a "foo.com/bar/hello" +a +main: "main" +"example.com@v0": "v0.0.1" + +-- _registry1/foo.com_bar_hello_v0.2.3/cue.mod/module.cue -- +module: "foo.com/bar/hello@v0" +deps: { + "bar.com@v0": v: "v0.0.2" + "baz.org@v0": v: "v0.10.1" +} + +-- _registry1/foo.com_bar_hello_v0.2.3/x.cue -- +package hello +import ( + a "bar.com/bar@v0" + b "baz.org@v0:baz" +) +"foo.com/bar/hello@v0": "v0.2.3" +a +b + + +-- _registry1/bar.com_v0.0.2/cue.mod/module.cue -- +module: "bar.com@v0" +deps: "baz.org@v0": v: "v0.0.2" + +-- _registry1/bar.com_v0.0.2/bar/x.cue -- +package bar +import a "baz.org@v0:baz" +"bar.com@v0": "v0.0.2" +a + + +-- _registry1/bar.com_v0.5.0/cue.mod/module.cue -- +module: "bar.com@v0" +deps: "baz.org@v0": v: "v0.5.0" + +-- _registry1/bar.com_v0.5.0/bar/x.cue -- +package bar +import a "baz.org@v0:baz" +"bar.com@v0": "v0.5.0" +a + + +-- _registry1/baz.org_v0.0.2/cue.mod/module.cue -- +module: "baz.org@v0" + +-- _registry1/baz.org_v0.0.2/baz.cue -- +package baz +"baz.org@v0": "v0.0.2" + +-- _registry1/baz.org_v0.1.2/cue.mod/module.cue -- +module: "baz.org@v0" + +-- _registry1/baz.org_v0.1.2/baz.cue -- +package baz +"baz.org@v0": "v0.1.2" + + +-- _registry1/baz.org_v0.5.0/cue.mod/module.cue -- +module: "baz.org@v0" + +-- _registry1/baz.org_v0.5.0/baz.cue -- +package baz +"baz.org@v0": "v0.5.0" + +-- _registry1/baz.org_v0.10.1/cue.mod/module.cue -- +module: "baz.org@v0" + +-- _registry1/baz.org_v0.10.1/baz.cue -- +package baz +"baz.org@v0": "v0.10.1" + +-- _registry2/baz.org_v0.0.2/cue.mod/module.cue -- +module: "baz.org@v0" + +-- _registry2/baz.org_v0.0.2/baz.cue -- +package baz +"baz.org@v0": "v0.0.2" + +-- _registry2/baz.org_v0.1.2/cue.mod/module.cue -- +module: "baz.org@v0" + +-- _registry2/baz.org_v0.5.0/cue.mod/module.cue -- +module: "baz.org@v0" + +-- _registry2/baz.org_v0.5.0/baz.cue -- +package baz +"baz.org@v0": "v0.5.0 in registry2" + +-- _registry2/baz.org_v0.10.1/cue.mod/module.cue -- +module: "baz.org@v0" + +-- _registry2/baz.org_v0.10.1/baz.cue -- +package baz +"baz.org@v0": "v0.10.1 in registry2"