-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Net-2712/resource hcl parsing (#18250)
* Initial protohcl implementation Co-authored-by: Matt Keeler <[email protected]> Co-authored-by: Daniel Upton <[email protected]> * resourcehcl: implement resource decoding on top of protohcl Co-authored-by: Daniel Upton <[email protected]> * fix: resolve ci failures * test: add additional unmarshalling tests * refactor: update function test to clean protohcl package imports --------- Co-authored-by: Matt Keeler <[email protected]> Co-authored-by: Daniel Upton <[email protected]>
- Loading branch information
1 parent
4a0afb5
commit 559c61e
Showing
50 changed files
with
3,709 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package protohcl | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"google.golang.org/protobuf/proto" | ||
"google.golang.org/protobuf/reflect/protoreflect" | ||
"google.golang.org/protobuf/reflect/protoregistry" | ||
"google.golang.org/protobuf/types/known/anypb" | ||
) | ||
|
||
const wellKnownTypeAny = "google.protobuf.Any" | ||
|
||
type AnyTypeProvider interface { | ||
AnyType(*UnmarshalContext, MessageDecoder) (protoreflect.FullName, MessageDecoder, error) | ||
} | ||
|
||
type AnyTypeURLProvider struct { | ||
TypeURLFieldName string | ||
} | ||
|
||
func (p *AnyTypeURLProvider) AnyType(ctx *UnmarshalContext, decoder MessageDecoder) (protoreflect.FullName, MessageDecoder, error) { | ||
typeURLFieldName := "type_url" | ||
if p != nil { | ||
typeURLFieldName = p.TypeURLFieldName | ||
} | ||
|
||
var typeURL *IterField | ||
err := decoder.EachField(FieldIterator{ | ||
Desc: (&anypb.Any{}).ProtoReflect().Descriptor(), | ||
Func: func(field *IterField) error { | ||
if field.Name == typeURLFieldName { | ||
typeURL = field | ||
} | ||
return nil | ||
}, | ||
IgnoreUnknown: true, | ||
}) | ||
if err != nil { | ||
return "", nil, err | ||
} | ||
|
||
if typeURL == nil || typeURL.Val == nil { | ||
return "", nil, fmt.Errorf("%s field is required to decode Any", typeURLFieldName) | ||
} | ||
|
||
url, err := stringFromCty(*typeURL.Val) | ||
if err != nil { | ||
return "", nil, err | ||
} | ||
|
||
slashIdx := strings.LastIndex(url, "/") | ||
typeName := url | ||
// strip all "hostname" parts of the URL path | ||
if slashIdx > 1 && slashIdx+1 < len(url) { | ||
typeName = url[slashIdx+1:] | ||
} | ||
|
||
return protoreflect.FullName(typeName), decoder.SkipFields(typeURLFieldName), nil | ||
} | ||
|
||
func (u UnmarshalOptions) decodeAny(ctx *UnmarshalContext, decoder MessageDecoder, msg protoreflect.Message) error { | ||
var typeProvider AnyTypeProvider = &AnyTypeURLProvider{TypeURLFieldName: "type_url"} | ||
if u.AnyTypeProvider != nil { | ||
typeProvider = u.AnyTypeProvider | ||
} | ||
|
||
var ( | ||
typeName protoreflect.FullName | ||
err error | ||
) | ||
typeName, decoder, err = typeProvider.AnyType(ctx, decoder) | ||
if err != nil { | ||
return fmt.Errorf("error getting type for Any field: %w", err) | ||
} | ||
|
||
// the type.googleapis.come/ should be optional | ||
mt, err := protoregistry.GlobalTypes.FindMessageByName(typeName) | ||
if err != nil { | ||
return fmt.Errorf("error looking up type information for %s: %w", typeName, err) | ||
} | ||
|
||
newMsg := mt.New() | ||
|
||
err = u.decodeMessage(&UnmarshalContext{ | ||
Parent: ctx.Parent, | ||
Name: ctx.Name, | ||
Message: newMsg, | ||
}, decoder, newMsg) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
enc, err := proto.Marshal(newMsg.Interface()) | ||
if err != nil { | ||
return fmt.Errorf("error marshalling Any data as protobuf value: %w", err) | ||
} | ||
|
||
anyValue := msg.Interface().(*anypb.Any) | ||
|
||
// This will look like <proto package>.<proto Message name> and not quite like a full URL with a path | ||
anyValue.TypeUrl = string(newMsg.Descriptor().FullName()) | ||
anyValue.Value = enc | ||
|
||
return nil | ||
} | ||
|
||
func isAnyField(desc protoreflect.FieldDescriptor) bool { | ||
if desc.Kind() != protoreflect.MessageKind { | ||
return false | ||
} | ||
return desc.Message().FullName() == wellKnownTypeAny | ||
} |
Oops, something went wrong.