tanmayrauth commented on code in PR #1493:
URL: https://github.com/apache/iceberg-go/pull/1493#discussion_r3608919911


##########
encryption/kms_registry.go:
##########
@@ -0,0 +1,120 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 encryption
+
+import (
+       "errors"
+       "fmt"
+       "maps"
+       "slices"
+       "sync"
+)
+
+// Catalog/table property keys used to select and configure a 
KeyManagementClient.
+const (
+       // KMSTypeKey selects a built-in KeyManagementClient implementation
+       // registered via [RegisterKMS] (e.g. "memory").
+       KMSTypeKey = "encryption.kms-type"
+
+       // KMSImplKey selects a custom KeyManagementClient implementation by a
+       // name registered via [RegisterKMS]. It is an alternative to
+       // [KMSTypeKey] for vendors that ship their own KMS integration under a
+       // distinct name; the two properties share the same registry and
+       // resolution mechanism.
+       KMSImplKey = "encryption.kms-impl"

Review Comment:
   kms-type and kms-impl resolve the same registry by the same short-name 
lookup, so they're functionally identical — and that's novel here: nothing else 
in the repo uses a -impl property, and the precedent (catalog.Load reads a 
single "type"; the io scheme registry) is one short key. In Iceberg generally 
-impl means a fully-qualified implementation, distinct from the short -type 
alias, so someone migrating a config that sets 
encryption.kms-impl=com.acme.AcmeKmsClient gets ErrKMSTypeNotFound because Go 
looks it up as a registered short name. Since nothing consumes this yet, either 
drop kms-impl and keep only kms-type (matching the repo's single-key 
precedent), or keep both and make the doc explicit that kms-impl is a 
registered short name, not an FQCN — the "a custom implementation" wording at 
34-35 currently reads like the Java FQCN meaning.



##########
encryption/kms_registry.go:
##########
@@ -0,0 +1,120 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 encryption
+
+import (
+       "errors"
+       "fmt"
+       "maps"
+       "slices"
+       "sync"
+)
+
+// Catalog/table property keys used to select and configure a 
KeyManagementClient.
+const (
+       // KMSTypeKey selects a built-in KeyManagementClient implementation
+       // registered via [RegisterKMS] (e.g. "memory").
+       KMSTypeKey = "encryption.kms-type"
+
+       // KMSImplKey selects a custom KeyManagementClient implementation by a
+       // name registered via [RegisterKMS]. It is an alternative to
+       // [KMSTypeKey] for vendors that ship their own KMS integration under a
+       // distinct name; the two properties share the same registry and
+       // resolution mechanism.
+       KMSImplKey = "encryption.kms-impl"
+)
+
+// ErrKMSTypeNotFound is returned by [LoadKeyManagementClient] when the
+// requested kmsType has not been registered via [RegisterKMS].
+var ErrKMSTypeNotFound = errors.New("encryption: kms type not found")
+
+// KMSFactory creates a [KeyManagementClient] from catalog/table properties.
+// props is the full property map (typically catalog properties); factories
+// should read whichever keys they need (e.g. key material, endpoints,
+// credentials) directly from it.
+type KMSFactory func(props map[string]string) (KeyManagementClient, error)
+
+var (
+       kmsRegMutex sync.RWMutex
+       kmsRegistry = map[string]KMSFactory{}
+)
+
+// RegisterKMS adds a new named [KMSFactory] to the registry so it can later
+// be selected via the [KMSTypeKey] or [KMSImplKey] catalog property. It
+// panics if factory is nil or if name is already registered.
+func RegisterKMS(name string, factory KMSFactory) {
+       if factory == nil {
+               panic("encryption: RegisterKMS factory is nil")
+       }
+
+       kmsRegMutex.Lock()
+       defer kmsRegMutex.Unlock()
+
+       if _, dup := kmsRegistry[name]; dup {
+               panic("encryption: RegisterKMS called twice for name " + name)
+       }
+       kmsRegistry[name] = factory
+}
+
+// UnregisterKMS removes the requested named factory from the registry.
+func UnregisterKMS(name string) {
+       kmsRegMutex.Lock()
+       defer kmsRegMutex.Unlock()
+       delete(kmsRegistry, name)
+}
+
+// GetRegisteredKMSNames returns the names of all currently registered KMS
+// factories.
+func GetRegisteredKMSNames() []string {
+       kmsRegMutex.RLock()
+       defer kmsRegMutex.RUnlock()
+
+       return slices.Collect(maps.Keys(kmsRegistry))
+}
+
+// LoadKeyManagementClient resolves and constructs a [KeyManagementClient]
+// from props. It looks up the KMS name under [KMSTypeKey] first, falling
+// back to [KMSImplKey] if present; it returns an error wrapping
+// [ErrKMSTypeNotFound] if neither property is set or the named KMS has not
+// been registered via [RegisterKMS].
+func LoadKeyManagementClient(props map[string]string) (KeyManagementClient, 
error) {
+       name := props[KMSTypeKey]
+       if name == "" {
+               name = props[KMSImplKey]
+       }
+
+       if name == "" {
+               return nil, fmt.Errorf("%w: neither %q nor %q is set", 
ErrKMSTypeNotFound, KMSTypeKey, KMSImplKey)
+       }
+
+       kmsRegMutex.RLock()
+       factory, ok := kmsRegistry[name]
+       kmsRegMutex.RUnlock()
+
+       if !ok {
+               return nil, fmt.Errorf("%w: %q", ErrKMSTypeNotFound, name)
+       }
+
+       return factory(props)

Review Comment:
   Nothing verifies props actually reaches the factory here — every test 
registers a factory that ignores its argument. The whole point of 
KMSFactory(props) is that real KMS factories read key  
material/region/endpoints out of props, so a future cleanup that filtered the 
map or passed nil before this call would compile, pass the whole suite, and 
silently hand an AWS/GCP factory an empty map — a factory reading 
encryption.kms.region would build a wrong-region client or fail at 
construction, uncaught. Please add a test that registers a factory recording 
its received props and assert it equals the input (include a sentinel key like 
encryption.kms.region).



##########
encryption/kms_registry.go:
##########
@@ -0,0 +1,120 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 encryption
+
+import (
+       "errors"
+       "fmt"
+       "maps"
+       "slices"
+       "sync"
+)
+
+// Catalog/table property keys used to select and configure a 
KeyManagementClient.
+const (
+       // KMSTypeKey selects a built-in KeyManagementClient implementation
+       // registered via [RegisterKMS] (e.g. "memory").
+       KMSTypeKey = "encryption.kms-type"
+
+       // KMSImplKey selects a custom KeyManagementClient implementation by a
+       // name registered via [RegisterKMS]. It is an alternative to
+       // [KMSTypeKey] for vendors that ship their own KMS integration under a
+       // distinct name; the two properties share the same registry and
+       // resolution mechanism.
+       KMSImplKey = "encryption.kms-impl"
+)
+
+// ErrKMSTypeNotFound is returned by [LoadKeyManagementClient] when the
+// requested kmsType has not been registered via [RegisterKMS].
+var ErrKMSTypeNotFound = errors.New("encryption: kms type not found")
+
+// KMSFactory creates a [KeyManagementClient] from catalog/table properties.
+// props is the full property map (typically catalog properties); factories
+// should read whichever keys they need (e.g. key material, endpoints,
+// credentials) directly from it.
+type KMSFactory func(props map[string]string) (KeyManagementClient, error)
+
+var (
+       kmsRegMutex sync.RWMutex
+       kmsRegistry = map[string]KMSFactory{}
+)
+
+// RegisterKMS adds a new named [KMSFactory] to the registry so it can later
+// be selected via the [KMSTypeKey] or [KMSImplKey] catalog property. It
+// panics if factory is nil or if name is already registered.
+func RegisterKMS(name string, factory KMSFactory) {
+       if factory == nil {
+               panic("encryption: RegisterKMS factory is nil")
+       }
+
+       kmsRegMutex.Lock()
+       defer kmsRegMutex.Unlock()
+
+       if _, dup := kmsRegistry[name]; dup {
+               panic("encryption: RegisterKMS called twice for name " + name)
+       }
+       kmsRegistry[name] = factory
+}
+
+// UnregisterKMS removes the requested named factory from the registry.
+func UnregisterKMS(name string) {
+       kmsRegMutex.Lock()
+       defer kmsRegMutex.Unlock()
+       delete(kmsRegistry, name)
+}
+
+// GetRegisteredKMSNames returns the names of all currently registered KMS
+// factories.
+func GetRegisteredKMSNames() []string {
+       kmsRegMutex.RLock()
+       defer kmsRegMutex.RUnlock()
+
+       return slices.Collect(maps.Keys(kmsRegistry))
+}
+
+// LoadKeyManagementClient resolves and constructs a [KeyManagementClient]
+// from props. It looks up the KMS name under [KMSTypeKey] first, falling
+// back to [KMSImplKey] if present; it returns an error wrapping
+// [ErrKMSTypeNotFound] if neither property is set or the named KMS has not
+// been registered via [RegisterKMS].
+func LoadKeyManagementClient(props map[string]string) (KeyManagementClient, 
error) {
+       name := props[KMSTypeKey]

Review Comment:
   The doc says it falls back to KMSImplKey "if present," but the code only 
falls back when kms-type is empty, not when it's set to an unregistered name — 
so kms-type=typo + kms-impl=memory errors instead of loading memory. The  
behavior's defensible (an explicit type shouldn't silently swallow a typo), but 
the doc reads otherwise. Tighten it to "falls back only when KMSTypeKey is 
unset," and consider adding that set-but-unregistered-type + valid-impl case  
to the precedence test at kms_registry_test.go:44.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to