Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion cmd/iceberg/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ package main

import (
"context"
"crypto/tls"
"errors"
"fmt"
"log"
"net/url"
"os"
"strings"

Expand Down Expand Up @@ -83,7 +85,9 @@ Options:
--description TEXT specify a description for the namespace
--location-uri TEXT specify a location URI for the namespace
--schema JSON specify table schema in json (for create table use only)
Ex: [{"name":"id","type":"int","required":false,"doc":"unique id"}]`
Ex: [{"name":"id","type":"int","required":false,"doc":"unique id"}]
--rest-config TEXT specify the REST configuration to use
Ex: sigv4-enabled=true,sigv4-region=us-east-1,sigv4-service=glue`

type Config struct {
List bool `docopt:"list"`
Expand Down Expand Up @@ -125,6 +129,7 @@ type Config struct {
Description string `docopt:"--description"`
LocationURI string `docopt:"--location-uri"`
SchemaStr string `docopt:"--schema"`
RestConfig string `docopt:"--rest-config"`
}

func main() {
Expand Down Expand Up @@ -162,6 +167,29 @@ func main() {
if len(cfg.Cred) > 0 {
opts = append(opts, rest.WithCredential(cfg.Cred))
}
if len(cfg.RestConfig) > 0 {
restCfg := config.ParseRestConfig(cfg.RestConfig)

if strings.ToLower(restCfg["sigv4-enabled"]) == "true" {
opts = append(opts, rest.WithSigV4())
}

if len(restCfg["sigv4-region"]) > 0 && len(restCfg["sigv4-service"]) > 0 {
opts = append(opts, rest.WithSigV4RegionSvc(restCfg["sigv4-region"], restCfg["sigv4-service"]))
}

if len(restCfg["auth-url"]) > 0 {
authUri, err := url.Parse(restCfg["auth-url"])
if err != nil {
log.Fatal(err)
}
opts = append(opts, rest.WithAuthURI(authUri))
}

if restCfg["tls-skip-verify"] == "true" {
opts = append(opts, rest.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}))
}
}

if len(cfg.Warehouse) > 0 {
opts = append(opts, rest.WithWarehouseLocation(cfg.Warehouse))
Expand Down Expand Up @@ -475,4 +503,7 @@ func mergeConf(fileConf *config.CatalogConfig, resConfig *Config) {
if len(resConfig.Warehouse) == 0 {
resConfig.Warehouse = fileConf.Warehouse
}
if len(resConfig.RestConfig) == 0 {
resConfig.RestConfig = fileConf.RestConfig
}
}
17 changes: 17 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import (
"os"
"path/filepath"
"strings"

"gopkg.in/yaml.v3"
)
Expand All @@ -41,6 +42,7 @@
Output string `yaml:"output"`
Credential string `yaml:"credential"`
Warehouse string `yaml:"warehouse"`
RestConfig string `yaml:"rest-config"`
}

func LoadConfig(configPath string) []byte {
Expand Down Expand Up @@ -76,6 +78,21 @@
return &res
}

func ParseRestConfig(restConfig string) map[string]string {
result := make(map[string]string)
if restConfig == "" {
return result
}

pairs := strings.Split(restConfig, ",")
for _, pair := range pairs {
if kv := strings.SplitN(pair, "=", 2); len(kv) == 2 {
result[kv[0]] = kv[1]
}
}
return result

Check failure on line 93 in config/config.go

View workflow job for this annotation

GitHub Actions / ubuntu-latest go1.23.6

return with no blank line before (nlreturn)

Check failure on line 93 in config/config.go

View workflow job for this annotation

GitHub Actions / macos-latest go1.23.6

return with no blank line before (nlreturn)

Check failure on line 93 in config/config.go

View workflow job for this annotation

GitHub Actions / windows-latest go1.23.6

return with no blank line before (nlreturn)
}

func fromConfigFiles() Config {
dir := os.Getenv("GOICEBERG_HOME")
if dir != "" {
Expand Down
44 changes: 44 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,27 @@ catalog:
Warehouse: "catalog_name",
},
},
// catalog with rest-config
{
[]byte(`
catalog:
rest-catalog:
type: rest
uri: https://glue.us-east-1.amazonaws.com/iceberg
output: json
credential: client-id:client-secret
warehouse: 123456789012
rest-config: auth-url=https://auth.example.com,sigv4-enabled=true,sigv4-region=us-east-1,sigv4-service=glue,tls-skip-verify=false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused, why don't we just use the existing properties for these that we already recognize as separate properties to pass?

i.e. rest.sigv4-enabled, rest.signing-region, rest.authorization-url, etc.. (see catalog/rest.go)

`), "rest-catalog",
&CatalogConfig{
CatalogType: "rest",
URI: "https://glue.us-east-1.amazonaws.com/iceberg",
Output: "json",
Credential: "client-id:client-secret",
Warehouse: "123456789012",
RestConfig: "auth-url=https://auth.example.com,sigv4-enabled=true,sigv4-region=us-east-1,sigv4-service=glue,tls-skip-verify=false",
},
},
}

func TestParseConfig(t *testing.T) {
Expand All @@ -87,3 +108,26 @@ func TestParseConfig(t *testing.T) {
assert.Equal(t, tt.expected, actual)
}
}

func TestRestCatalogConfig(t *testing.T) {
yamlData := []byte(`
catalog:
default:
type: rest
uri: https://glue.us-east-1.amazonaws.com/iceberg
output: json
rest-config: auth-url=https://auth.example.com,sigv4-enabled=true,sigv4-region=us-east-1,sigv4-service=glue,tls-skip-verify=false
`)

config := ParseConfig(yamlData, "default")
assert.NotNil(t, config)
restConfig := ParseRestConfig(config.RestConfig)
assert.NotEmpty(t, restConfig)

// Test RestCatalog config fields
assert.Equal(t, "https://auth.example.com", restConfig["auth-url"])
assert.Equal(t, "true", restConfig["sigv4-enabled"])
assert.Equal(t, "us-east-1", restConfig["sigv4-region"])
assert.Equal(t, "glue", restConfig["sigv4-service"])
assert.Equal(t, "false", restConfig["tls-skip-verify"])
}
Loading