Bind engine secret handling to facade types
This commit is contained in:
@@ -29,8 +29,9 @@ const (
|
||||
)
|
||||
|
||||
type schemaDescriptor struct {
|
||||
root *typeDescriptor
|
||||
types map[reflect.Type]*typeDescriptor
|
||||
root *typeDescriptor
|
||||
types map[reflect.Type]*typeDescriptor
|
||||
secrets SecretBinding
|
||||
}
|
||||
|
||||
type typeDescriptor struct {
|
||||
@@ -65,20 +66,39 @@ type schemaIssue struct {
|
||||
message string
|
||||
}
|
||||
|
||||
// SecretState is the private operation state for one exact-type secret value.
|
||||
// It is supplied by the facade and never enters inspection records.
|
||||
type SecretState struct {
|
||||
Name string
|
||||
Value string
|
||||
Specified bool
|
||||
Resolved bool
|
||||
}
|
||||
|
||||
// SecretBinding adapts the facade-owned exact secret type for one load.
|
||||
// Bindings are operation data: callers must supply the type and conversions
|
||||
// together, and a binding never becomes package-global state.
|
||||
type SecretBinding struct {
|
||||
Type reflect.Type
|
||||
Read func(reflect.Value) SecretState
|
||||
Make func(SecretState) reflect.Value
|
||||
}
|
||||
|
||||
var (
|
||||
durationType = reflect.TypeFor[time.Duration]()
|
||||
secretType = reflect.TypeFor[Secret]()
|
||||
textMarshalerType = reflect.TypeFor[encoding.TextMarshaler]()
|
||||
textUnmarshalerType = reflect.TypeFor[encoding.TextUnmarshaler]()
|
||||
)
|
||||
|
||||
func buildSchema(root reflect.Type) (*schemaDescriptor, *schemaIssue) {
|
||||
func buildSchema(root reflect.Type, secrets SecretBinding) (*schemaDescriptor, *schemaIssue) {
|
||||
secrets.validate()
|
||||
if root == nil || (root.Kind() != reflect.Struct && !isStringMap(root)) {
|
||||
return nil, &schemaIssue{code: CodeSchemaRoot, message: "configuration root must be a struct or string-keyed map"}
|
||||
}
|
||||
builder := schemaBuilder{
|
||||
cache: make(map[reflect.Type]*typeDescriptor),
|
||||
building: make(map[reflect.Type]bool),
|
||||
secrets: secrets,
|
||||
}
|
||||
descriptor, issue := builder.describe(root, Path{})
|
||||
if issue != nil {
|
||||
@@ -87,12 +107,13 @@ func buildSchema(root reflect.Type) (*schemaDescriptor, *schemaIssue) {
|
||||
if descriptor.kind != descriptorStruct && descriptor.kind != descriptorMap {
|
||||
return nil, &schemaIssue{code: CodeSchemaRoot, message: "configuration root must be a structural struct or string-keyed map"}
|
||||
}
|
||||
return &schemaDescriptor{root: descriptor, types: builder.cache}, nil
|
||||
return &schemaDescriptor{root: descriptor, types: builder.cache, secrets: secrets}, nil
|
||||
}
|
||||
|
||||
type schemaBuilder struct {
|
||||
cache map[reflect.Type]*typeDescriptor
|
||||
building map[reflect.Type]bool
|
||||
secrets SecretBinding
|
||||
}
|
||||
|
||||
func (b *schemaBuilder) describe(current reflect.Type, path Path) (*typeDescriptor, *schemaIssue) {
|
||||
@@ -122,7 +143,7 @@ func (b *schemaBuilder) describe(current reflect.Type, path Path) (*typeDescript
|
||||
descriptor.kind = descriptorDuration
|
||||
return descriptor, nil
|
||||
}
|
||||
if current == secretType {
|
||||
if b.secrets.Type != nil && current == b.secrets.Type {
|
||||
descriptor.kind = descriptorSecret
|
||||
return descriptor, nil
|
||||
}
|
||||
@@ -189,6 +210,15 @@ func (b *schemaBuilder) describe(current reflect.Type, path Path) (*typeDescript
|
||||
return descriptor, nil
|
||||
}
|
||||
|
||||
func (binding SecretBinding) validate() {
|
||||
if binding.Type == nil {
|
||||
return
|
||||
}
|
||||
if binding.Read == nil || binding.Make == nil {
|
||||
panic("configkit engine: incomplete secret binding")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *schemaBuilder) describeFields(descriptor *typeDescriptor, current reflect.Type, path Path) *schemaIssue {
|
||||
for i := 0; i < current.NumField(); i++ {
|
||||
field := current.Field(i)
|
||||
|
||||
Reference in New Issue
Block a user