You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by ze...@apache.org on 2026/08/07 18:07:34 UTC
(iceberg-go) branch main updated: fix(cli): accept case-insentive catalog types in cli (#1650)
This is an automated email from the ASF dual-hosted git repository.
zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git
The following commit(s) were added to refs/heads/main by this push:
new 80b8ab5a2 fix(cli): accept case-insentive catalog types in cli (#1650)
80b8ab5a2 is described below
commit 80b8ab5a26ebe22ffa60143ad64de3869526a07c
Author: jj.lee <63...@users.noreply.github.com>
AuthorDate: Sat Aug 8 03:07:29 2026 +0900
fix(cli): accept case-insentive catalog types in cli (#1650)
* ToLower Catalog args in cli
* consistent ToLower in registry
* added mixed-case for test
---
catalog/registry.go | 2 +-
catalog/registry_test.go | 19 ++++++
cmd/iceberg/args_test.go | 146 +++++++++++++++++++++++++++++++++++++++++++++++
cmd/iceberg/main.go | 2 +-
4 files changed, 167 insertions(+), 2 deletions(-)
diff --git a/catalog/registry.go b/catalog/registry.go
index c43f6e275..f1bd0c8af 100644
--- a/catalog/registry.go
+++ b/catalog/registry.go
@@ -158,7 +158,7 @@ func Load(ctx context.Context, name string, props iceberg.Properties) (Catalog,
}
}
- cat, ok := defaultRegistry.get(catalogType)
+ cat, ok := defaultRegistry.get(strings.ToLower(catalogType))
if !ok {
return nil, fmt.Errorf("%w: %s", ErrCatalogNotFound, catalogType)
}
diff --git a/catalog/registry_test.go b/catalog/registry_test.go
index bde8d60b5..3c5007b64 100644
--- a/catalog/registry_test.go
+++ b/catalog/registry_test.go
@@ -33,6 +33,25 @@ import (
"github.com/stretchr/testify/assert"
)
+func TestCatalogLoadTypeIsCaseInsensitive(t *testing.T) {
+ ctx := context.Background()
+ defer catalog.Unregister("foobar")
+
+ catalog.Register("foobar", catalog.RegistrarFunc(func(ctx context.Context, s string, p iceberg.Properties) (catalog.Catalog, error) {
+ return nil, nil
+ }))
+
+ for _, catalogType := range []string{"foobar", "FOOBAR", "FooBar"} {
+ c, err := catalog.Load(ctx, "not found", iceberg.Properties{"type": catalogType})
+ assert.Nil(t, c)
+ assert.NoError(t, err)
+ }
+
+ c, err := catalog.Load(ctx, "not found", iceberg.Properties{"type": "FOOBARX"})
+ assert.Nil(t, c)
+ assert.ErrorIs(t, err, catalog.ErrCatalogNotFound)
+}
+
func TestCatalogRegistry(t *testing.T) {
ctx := context.Background()
assert.ElementsMatch(t, []string{
diff --git a/cmd/iceberg/args_test.go b/cmd/iceberg/args_test.go
index 9329b1b95..9ec250d76 100644
--- a/cmd/iceberg/args_test.go
+++ b/cmd/iceberg/args_test.go
@@ -18,6 +18,9 @@
package main
import (
+ "net"
+ "net/http"
+ "net/http/httptest"
"os"
"os/exec"
"path/filepath"
@@ -382,6 +385,149 @@ func TestCLIExplicitMissingConfigFailsBeforeCatalogInit(t *testing.T) {
assert.Contains(t, string(out), path)
}
+func TestCLIAcceptsMixedCaseCatalogType(t *testing.T) {
+ if testing.Short() {
+ t.Skip("spawns a subprocess")
+ }
+
+ restMux := http.NewServeMux()
+ restMux.HandleFunc("/v1/config", func(w http.ResponseWriter, req *http.Request) {
+ require.Equal(t, http.MethodGet, req.Method)
+ w.Header().Set("Content-Type", "application/json")
+ _, _ = w.Write([]byte(`{"defaults":{},"overrides":{},"endpoints":["GET /v1/{prefix}/namespaces"]}`))
+ })
+ restMux.HandleFunc("/v1/namespaces", func(w http.ResponseWriter, req *http.Request) {
+ require.Equal(t, http.MethodGet, req.Method)
+ w.Header().Set("Content-Type", "application/json")
+ _, _ = w.Write([]byte(`{"namespaces":[]}`))
+ })
+ restSrv := httptest.NewServer(restMux)
+ defer restSrv.Close()
+
+ glueMux := http.NewServeMux()
+ glueMux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+ w.Header().Set("Content-Type", "application/x-amz-json-1.1")
+ _, _ = w.Write([]byte(`{"DatabaseList":[]}`))
+ })
+ glueSrv := httptest.NewServer(glueMux)
+ defer glueSrv.Close()
+
+ hadoopWarehouse := t.TempDir()
+
+ // A raw TCP listener stands in for a Hive metastore: the CLI only needs to
+ // get far enough to open a connection to prove "HIVE"/"Hive" were recognized
+ // as the Hive catalog type. It has no Thrift handshake, so the command still
+ // fails, but with an EOF/protocol error rather than "unrecognized catalog type".
+ hiveLn, err := net.Listen("tcp", "127.0.0.1:0")
+ require.NoError(t, err)
+ defer hiveLn.Close()
+ go func() {
+ for {
+ conn, err := hiveLn.Accept()
+ if err != nil {
+ return
+ }
+ conn.Close()
+ }
+ }()
+ hiveURI := "thrift://" + hiveLn.Addr().String()
+
+ tests := []struct {
+ name string
+ args []string
+ env []string
+ // wantErr is set for cases where the catalog type is recognized but the
+ // backend call itself is expected to fail (e.g. no real Hive metastore
+ // behind the raw TCP listener).
+ wantErr bool
+ // wantUnrecognized is set only for catalog type values that must still be
+ // rejected outright, to keep that failure mode distinguishable from
+ // wantErr above.
+ wantUnrecognized bool
+ }{
+ {
+ name: "rest lowercase",
+ args: []string{"list", "--catalog", "rest", "--uri", restSrv.URL},
+ },
+ {
+ name: "rest uppercase",
+ args: []string{"list", "--catalog", "REST", "--uri", restSrv.URL},
+ },
+ {
+ name: "rest mixed case",
+ args: []string{"list", "--catalog", "Rest", "--uri", restSrv.URL},
+ },
+ {
+ name: "glue uppercase",
+ args: []string{"list", "--catalog", "GLUE"},
+ env: []string{
+ "AWS_ENDPOINT_URL=" + glueSrv.URL,
+ "AWS_ACCESS_KEY_ID=test",
+ "AWS_SECRET_ACCESS_KEY=test",
+ "AWS_REGION=us-east-1",
+ },
+ },
+ {
+ name: "glue mixed case",
+ args: []string{"list", "--catalog", "Glue"},
+ env: []string{
+ "AWS_ENDPOINT_URL=" + glueSrv.URL,
+ "AWS_ACCESS_KEY_ID=test",
+ "AWS_SECRET_ACCESS_KEY=test",
+ "AWS_REGION=us-east-1",
+ },
+ },
+ {
+ name: "hadoop uppercase",
+ args: []string{"list", "--catalog", "HADOOP", "--warehouse", hadoopWarehouse},
+ },
+ {
+ name: "hadoop mixed case",
+ args: []string{"list", "--catalog", "Hadoop", "--warehouse", hadoopWarehouse},
+ },
+ {
+ name: "hive uppercase",
+ args: []string{"list", "--catalog", "HIVE", "--uri", hiveURI},
+ wantErr: true,
+ },
+ {
+ name: "hive mixed case",
+ args: []string{"list", "--catalog", "Hive", "--uri", hiveURI},
+ wantErr: true,
+ },
+ {
+ name: "unknown catalog type is still rejected",
+ args: []string{"list", "--catalog", "RESTX", "--uri", restSrv.URL},
+ wantUnrecognized: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cmd := exec.Command(os.Args[0], tt.args...)
+ cmd.Env = append(os.Environ(), icebergCLISubprocessEnv+"=1")
+ cmd.Env = append(cmd.Env, tt.env...)
+ out, err := cmd.CombinedOutput()
+
+ if tt.wantUnrecognized {
+ require.Error(t, err, "output: %s", out)
+ require.Contains(t, string(out), "unrecognized catalog type")
+
+ return
+ }
+
+ if tt.wantErr {
+ require.Error(t, err, "output: %s", out)
+ require.NotContains(t, string(out), "unrecognized catalog type")
+
+ return
+ }
+
+ require.NoError(t, err, "output: %s", out)
+ })
+ }
+}
+
func TestMergeConfAwsProfile(t *testing.T) {
fileCfg := &config.CatalogConfig{AwsProfile: "file-profile"}
diff --git a/cmd/iceberg/main.go b/cmd/iceberg/main.go
index fe865af05..189a7f662 100644
--- a/cmd/iceberg/main.go
+++ b/cmd/iceberg/main.go
@@ -358,7 +358,7 @@ func initCatalog(ctx context.Context, args Args) catalog.Catalog {
err error
)
- switch catalog.Type(args.Catalog) {
+ switch catalog.Type(strings.ToLower(args.Catalog)) {
case catalog.REST:
opts := []rest.Option{}
if len(args.Token) > 0 {