From daad9881c0271a87f749ea28a16947704db6ee18 Mon Sep 17 00:00:00 2001 From: Lucas Bajolet Date: Fri, 16 Feb 2024 10:38:51 -0500 Subject: [PATCH 1/2] rpc: add base protos for HCLSpec This commit introduces a protobuf-serialisable type for hcldec.Spec types. We need this be able to use another format to serialise them without using gob, as gob isn't supported anymore for cty.Type. The HCL2Spec function, implemented by all the plugins, are the ones responsible for transmitting the schema of their configurations to Packer, which as of v0.5.1 of the SDK, is using gob for serialisation, which has been removed from the cty library. --- rpc/convert.go | 263 +++++++++++++ rpc/hcl_spec.pb.go | 949 +++++++++++++++++++++++++++++++++++++++++++++ rpc/hcl_spec.proto | 94 +++++ 3 files changed, 1306 insertions(+) create mode 100644 rpc/convert.go create mode 100644 rpc/hcl_spec.pb.go create mode 100644 rpc/hcl_spec.proto diff --git a/rpc/convert.go b/rpc/convert.go new file mode 100644 index 000000000..12f397499 --- /dev/null +++ b/rpc/convert.go @@ -0,0 +1,263 @@ +package rpc + +import ( + "fmt" + "reflect" + + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// ToProto converts a hcldec.Spec to a protobuf-serialisable equivalent. +// +// This can then be used for gRPC communication over-the-wire for Packer plugins. +func ToProto(spec hcldec.Spec) (*Spec, error) { + switch concreteSpec := spec.(type) { + case *hcldec.AttrSpec: + attr, err := attrSpecToProto(concreteSpec) + if err != nil { + return nil, fmt.Errorf("failed to decode attribute spec: %s", err) + } + return &Spec{ + Block: &Spec_Attr{ + Attr: attr, + }, + }, nil + case *hcldec.BlockListSpec: + blspec, err := blockListSpecToProto(concreteSpec) + if err != nil { + return nil, fmt.Errorf("failed to decode block list spec: %s", err) + } + return &Spec{ + Block: &Spec_BlockList{ + BlockList: blspec, + }, + }, nil + case hcldec.ObjectSpec: + objSpec, err := objectSpecToProto(&concreteSpec) + if err != nil { + return nil, fmt.Errorf("failed to decode object spec: %s", err) + } + return &Spec{ + Block: &Spec_Object{ + Object: objSpec, + }, + }, nil + case *hcldec.BlockSpec: + blockSpec, err := blockSpecToProto(concreteSpec) + if err != nil { + return nil, fmt.Errorf("failed to decode object spec: %s", err) + } + return &Spec{ + Block: &Spec_BlockValue{ + BlockValue: blockSpec, + }, + }, nil + } + + return nil, fmt.Errorf("unsupported hcldec.Spec type: %#v", reflect.TypeOf(spec).String()) +} + +func blockSpecToProto(bsp *hcldec.BlockSpec) (*Block, error) { + nst, err := ToProto(bsp.Nested) + if err != nil { + return nil, fmt.Errorf("failed to decode block nested spec: %s", err) + } + + return &Block{ + Name: bsp.TypeName, + Required: bsp.Required, + Nested: nst, + }, nil +} + +func objectSpecToProto(hsp *hcldec.ObjectSpec) (*Object, error) { + outSpec := &Object{ + Attributes: map[string]*Spec{}, + } + + for name, spec := range *hsp { + pSpec, err := ToProto(spec) + if err != nil { + return nil, fmt.Errorf("failed to convert object attribute %q to hcldec.Spec: %s", name, err) + } + outSpec.Attributes[name] = pSpec + } + + return outSpec, nil +} + +func blockListSpecToProto(blspec *hcldec.BlockListSpec) (*BlockList, error) { + nested, err := ToProto(blspec.Nested) + if err != nil { + return nil, fmt.Errorf("failed to decode block list nested type: %s", err) + } + + return &BlockList{ + Name: blspec.TypeName, + Nested: nested, + }, nil +} + +func attrSpecToProto(attrSpec *hcldec.AttrSpec) (*Attr, error) { + convertedType, err := ctyTypeToProto(attrSpec.Type) + if err != nil { + return nil, fmt.Errorf("failed to convert ctyType for attribute %q: %s", attrSpec.Name, err) + } + + return &Attr{ + Name: attrSpec.Name, + Required: attrSpec.Required, + Type: convertedType, + }, nil +} + +func ctyTypeToProto(cType cty.Type) (*CtyType, error) { + if cType.IsPrimitiveType() { + switch cType { + case cty.Bool, + cty.String, + cty.Number: + return &CtyType{ + TypeDef: &CtyType_Primitive{ + Primitive: &CtyPrimitive{ + TypeString: cType.GoString(), + }, + }, + }, nil + default: + return nil, fmt.Errorf("Unknown primitive type: %s", cType.FriendlyName()) + } + } + + if cType.IsListType() { + el := cType.ElementType() + elType, err := ctyTypeToProto(el) + if err != nil { + return nil, fmt.Errorf("failed to extract valid cty.Type from list element: %s", err) + } + return &CtyType{ + TypeDef: &CtyType_List{ + List: &CtyList{ + ElementType: elType, + }, + }, + }, nil + } + + // As per the specification, cty.Map are always a map from string to a cty type + // + // Therefore, we don't need to worry about other types than the element's + if cType.IsMapType() { + el := cType.MapElementType() + elType, err := ctyTypeToProto(*el) + if err != nil { + return nil, fmt.Errorf("failed to extract valid cty.Type from map: %s", err) + } + return &CtyType{ + TypeDef: &CtyType_Map{ + Map: &CtyMap{ + ElementType: elType, + }, + }, + }, nil + } + + return nil, fmt.Errorf("unsupported cty.Type conversion to protobuf-compatible structure: %+v", cType) +} + +func (spec *Spec) FromProto() (hcldec.Spec, error) { + switch realSpec := spec.Block.(type) { + case *Spec_Attr: + return protoArgToHCLDecSpec(realSpec.Attr) + case *Spec_BlockList: + return protoBlockListToHCLDecSpec(realSpec.BlockList) + case *Spec_BlockValue: + return protoBlockToHCLDecSpec(realSpec.BlockValue) + case *Spec_Object: + return protoObjectSpecToHCLDecSpec(realSpec.Object) + } + + return nil, fmt.Errorf("unsupported spec type: %s", spec.String()) +} + +func protoObjectSpecToHCLDecSpec(protoSpec *Object) (*hcldec.ObjectSpec, error) { + outSpec := hcldec.ObjectSpec{} + + for name, spec := range protoSpec.Attributes { + attrSpec, err := spec.FromProto() + if err != nil { + return nil, fmt.Errorf("failed to decode object attribute %q: %s", name, err) + } + outSpec[name] = attrSpec + } + + return &outSpec, nil +} + +func protoBlockToHCLDecSpec(bl *Block) (*hcldec.BlockSpec, error) { + nested, err := bl.Nested.FromProto() + if err != nil { + return nil, fmt.Errorf("failed to decode block nested type from proto: %s", err) + } + + return &hcldec.BlockSpec{ + TypeName: bl.Name, + Required: bl.Required, + Nested: nested, + }, nil +} + +func protoBlockListToHCLDecSpec(bll *BlockList) (*hcldec.BlockListSpec, error) { + blSpec := bll.Nested + nested, err := blSpec.FromProto() + if err != nil { + return nil, fmt.Errorf("failed to decode block list nested type from proto: %s", err) + } + + return &hcldec.BlockListSpec{ + TypeName: bll.Name, + Nested: nested, + }, nil +} + +func protoArgToHCLDecSpec(attr *Attr) (*hcldec.AttrSpec, error) { + relType, err := protoTypeToCtyType(attr.Type) + if err != nil { + return nil, fmt.Errorf("failed to convert type of attribute %q: %s", attr.Name, err) + } + + return &hcldec.AttrSpec{ + Name: attr.Name, + Required: attr.Required, + Type: relType, + }, nil +} + +func protoTypeToCtyType(protoType *CtyType) (cty.Type, error) { + switch concrete := protoType.TypeDef.(type) { + case *CtyType_Primitive: + switch concrete.Primitive.TypeString { + case "cty.String": + return cty.String, nil + case "cty.Bool": + return cty.Bool, nil + case "cty.Number": + return cty.Number, nil + } + case *CtyType_List: + elType, err := protoTypeToCtyType(concrete.List.ElementType) + if err != nil { + return cty.NilType, fmt.Errorf("failed to convert list element type: %s", err) + } + return cty.List(elType), nil + case *CtyType_Map: + elType, err := protoTypeToCtyType(concrete.Map.ElementType) + if err != nil { + return cty.NilType, fmt.Errorf("failed to convert map element type: %s", err) + } + return cty.Map(elType), nil + } + + return cty.NilType, fmt.Errorf("unsupported cty.Type: %+v", protoType) +} diff --git a/rpc/hcl_spec.pb.go b/rpc/hcl_spec.pb.go new file mode 100644 index 000000000..2c1adfce9 --- /dev/null +++ b/rpc/hcl_spec.pb.go @@ -0,0 +1,949 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.12.4 +// source: rpc/hcl_spec.proto + +package rpc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// CtyType is any of the cty types that can be used for a Attribute. +// +// Bodies aren't an issue since they're encompassing a bunch of different +// attributes, which end-up referencing a type from this structure. +type CtyType struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to TypeDef: + // + // *CtyType_Primitive + // *CtyType_List + // *CtyType_Map + TypeDef isCtyType_TypeDef `protobuf_oneof:"typeDef"` +} + +func (x *CtyType) Reset() { + *x = CtyType{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_hcl_spec_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CtyType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CtyType) ProtoMessage() {} + +func (x *CtyType) ProtoReflect() protoreflect.Message { + mi := &file_rpc_hcl_spec_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CtyType.ProtoReflect.Descriptor instead. +func (*CtyType) Descriptor() ([]byte, []int) { + return file_rpc_hcl_spec_proto_rawDescGZIP(), []int{0} +} + +func (m *CtyType) GetTypeDef() isCtyType_TypeDef { + if m != nil { + return m.TypeDef + } + return nil +} + +func (x *CtyType) GetPrimitive() *CtyPrimitive { + if x, ok := x.GetTypeDef().(*CtyType_Primitive); ok { + return x.Primitive + } + return nil +} + +func (x *CtyType) GetList() *CtyList { + if x, ok := x.GetTypeDef().(*CtyType_List); ok { + return x.List + } + return nil +} + +func (x *CtyType) GetMap() *CtyMap { + if x, ok := x.GetTypeDef().(*CtyType_Map); ok { + return x.Map + } + return nil +} + +type isCtyType_TypeDef interface { + isCtyType_TypeDef() +} + +type CtyType_Primitive struct { + Primitive *CtyPrimitive `protobuf:"bytes,1,opt,name=primitive,proto3,oneof"` +} + +type CtyType_List struct { + List *CtyList `protobuf:"bytes,2,opt,name=list,proto3,oneof"` +} + +type CtyType_Map struct { + Map *CtyMap `protobuf:"bytes,3,opt,name=map,proto3,oneof"` +} + +func (*CtyType_Primitive) isCtyType_TypeDef() {} + +func (*CtyType_List) isCtyType_TypeDef() {} + +func (*CtyType_Map) isCtyType_TypeDef() {} + +// CtyPrimitive is any of the cty.Type that match the `IsPrimitiveType` function +// i.e. either Number, Bool or String. +type CtyPrimitive struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeString string `protobuf:"bytes,1,opt,name=typeString,proto3" json:"typeString,omitempty"` +} + +func (x *CtyPrimitive) Reset() { + *x = CtyPrimitive{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_hcl_spec_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CtyPrimitive) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CtyPrimitive) ProtoMessage() {} + +func (x *CtyPrimitive) ProtoReflect() protoreflect.Message { + mi := &file_rpc_hcl_spec_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CtyPrimitive.ProtoReflect.Descriptor instead. +func (*CtyPrimitive) Descriptor() ([]byte, []int) { + return file_rpc_hcl_spec_proto_rawDescGZIP(), []int{1} +} + +func (x *CtyPrimitive) GetTypeString() string { + if x != nil { + return x.TypeString + } + return "" +} + +// CtyList is a list of a cty.Type +type CtyList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ElementType *CtyType `protobuf:"bytes,1,opt,name=elementType,proto3" json:"elementType,omitempty"` +} + +func (x *CtyList) Reset() { + *x = CtyList{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_hcl_spec_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CtyList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CtyList) ProtoMessage() {} + +func (x *CtyList) ProtoReflect() protoreflect.Message { + mi := &file_rpc_hcl_spec_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CtyList.ProtoReflect.Descriptor instead. +func (*CtyList) Descriptor() ([]byte, []int) { + return file_rpc_hcl_spec_proto_rawDescGZIP(), []int{2} +} + +func (x *CtyList) GetElementType() *CtyType { + if x != nil { + return x.ElementType + } + return nil +} + +// CtyMap is a map from one type to another +type CtyMap struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ElementType *CtyType `protobuf:"bytes,1,opt,name=elementType,proto3" json:"elementType,omitempty"` +} + +func (x *CtyMap) Reset() { + *x = CtyMap{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_hcl_spec_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CtyMap) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CtyMap) ProtoMessage() {} + +func (x *CtyMap) ProtoReflect() protoreflect.Message { + mi := &file_rpc_hcl_spec_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CtyMap.ProtoReflect.Descriptor instead. +func (*CtyMap) Descriptor() ([]byte, []int) { + return file_rpc_hcl_spec_proto_rawDescGZIP(), []int{3} +} + +func (x *CtyMap) GetElementType() *CtyType { + if x != nil { + return x.ElementType + } + return nil +} + +// HCL2Spec matches what Packer already consumes from plugins in order to describe +// their contents' schema, and lets Packer decode the configuration provided by +// the user to cty values, and detect problems with the contents before executing them. +// +// These are sent over-the-wire over gRPC, much like the old system did using gob +// encoding and standard go RPC servers. +type HCL2Spec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeSpec map[string]*Spec `protobuf:"bytes,1,rep,name=TypeSpec,proto3" json:"TypeSpec,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *HCL2Spec) Reset() { + *x = HCL2Spec{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_hcl_spec_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HCL2Spec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HCL2Spec) ProtoMessage() {} + +func (x *HCL2Spec) ProtoReflect() protoreflect.Message { + mi := &file_rpc_hcl_spec_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HCL2Spec.ProtoReflect.Descriptor instead. +func (*HCL2Spec) Descriptor() ([]byte, []int) { + return file_rpc_hcl_spec_proto_rawDescGZIP(), []int{4} +} + +func (x *HCL2Spec) GetTypeSpec() map[string]*Spec { + if x != nil { + return x.TypeSpec + } + return nil +} + +// A Spec is any kind of object that can convert losslessly to any of the hcldec.Spec types. +type Spec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Block: + // + // *Spec_Object + // *Spec_Attr + // *Spec_BlockValue + // *Spec_BlockList + Block isSpec_Block `protobuf_oneof:"block"` +} + +func (x *Spec) Reset() { + *x = Spec{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_hcl_spec_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Spec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Spec) ProtoMessage() {} + +func (x *Spec) ProtoReflect() protoreflect.Message { + mi := &file_rpc_hcl_spec_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Spec.ProtoReflect.Descriptor instead. +func (*Spec) Descriptor() ([]byte, []int) { + return file_rpc_hcl_spec_proto_rawDescGZIP(), []int{5} +} + +func (m *Spec) GetBlock() isSpec_Block { + if m != nil { + return m.Block + } + return nil +} + +func (x *Spec) GetObject() *Object { + if x, ok := x.GetBlock().(*Spec_Object); ok { + return x.Object + } + return nil +} + +func (x *Spec) GetAttr() *Attr { + if x, ok := x.GetBlock().(*Spec_Attr); ok { + return x.Attr + } + return nil +} + +func (x *Spec) GetBlockValue() *Block { + if x, ok := x.GetBlock().(*Spec_BlockValue); ok { + return x.BlockValue + } + return nil +} + +func (x *Spec) GetBlockList() *BlockList { + if x, ok := x.GetBlock().(*Spec_BlockList); ok { + return x.BlockList + } + return nil +} + +type isSpec_Block interface { + isSpec_Block() +} + +type Spec_Object struct { + Object *Object `protobuf:"bytes,1,opt,name=object,proto3,oneof"` +} + +type Spec_Attr struct { + // buf:lint:ignore FIELD_LOWER_SNAKE_CASE + Attr *Attr `protobuf:"bytes,2,opt,name=Attr,proto3,oneof"` +} + +type Spec_BlockValue struct { + BlockValue *Block `protobuf:"bytes,3,opt,name=block_value,json=blockValue,proto3,oneof"` +} + +type Spec_BlockList struct { + BlockList *BlockList `protobuf:"bytes,4,opt,name=block_list,json=blockList,proto3,oneof"` +} + +func (*Spec_Object) isSpec_Block() {} + +func (*Spec_Attr) isSpec_Block() {} + +func (*Spec_BlockValue) isSpec_Block() {} + +func (*Spec_BlockList) isSpec_Block() {} + +// Attr spec type reads the value of an attribute in the current body +// and returns that value as its result. It also creates validation constraints +// for the given attribute name and its value. +type Attr struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type *CtyType `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Required bool `protobuf:"varint,3,opt,name=required,proto3" json:"required,omitempty"` +} + +func (x *Attr) Reset() { + *x = Attr{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_hcl_spec_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Attr) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Attr) ProtoMessage() {} + +func (x *Attr) ProtoReflect() protoreflect.Message { + mi := &file_rpc_hcl_spec_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Attr.ProtoReflect.Descriptor instead. +func (*Attr) Descriptor() ([]byte, []int) { + return file_rpc_hcl_spec_proto_rawDescGZIP(), []int{6} +} + +func (x *Attr) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Attr) GetType() *CtyType { + if x != nil { + return x.Type + } + return nil +} + +func (x *Attr) GetRequired() bool { + if x != nil { + return x.Required + } + return false +} + +// Block spec type applies one nested spec block to the contents of a +// block within the current body and returns the result of that spec. It also +// creates validation constraints for the given block type name. +type Block struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Required bool `protobuf:"varint,2,opt,name=required,proto3" json:"required,omitempty"` + Nested *Spec `protobuf:"bytes,3,opt,name=nested,proto3" json:"nested,omitempty"` +} + +func (x *Block) Reset() { + *x = Block{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_hcl_spec_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Block) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Block) ProtoMessage() {} + +func (x *Block) ProtoReflect() protoreflect.Message { + mi := &file_rpc_hcl_spec_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Block.ProtoReflect.Descriptor instead. +func (*Block) Descriptor() ([]byte, []int) { + return file_rpc_hcl_spec_proto_rawDescGZIP(), []int{7} +} + +func (x *Block) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Block) GetRequired() bool { + if x != nil { + return x.Required + } + return false +} + +func (x *Block) GetNested() *Spec { + if x != nil { + return x.Nested + } + return nil +} + +// BlockList spec type is similar to `Block`, but it accepts zero or +// more blocks of a specified type rather than requiring zero or one. The +// result is a JSON array with one entry per block of the given type. +type BlockList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Nested *Spec `protobuf:"bytes,2,opt,name=nested,proto3" json:"nested,omitempty"` +} + +func (x *BlockList) Reset() { + *x = BlockList{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_hcl_spec_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockList) ProtoMessage() {} + +func (x *BlockList) ProtoReflect() protoreflect.Message { + mi := &file_rpc_hcl_spec_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockList.ProtoReflect.Descriptor instead. +func (*BlockList) Descriptor() ([]byte, []int) { + return file_rpc_hcl_spec_proto_rawDescGZIP(), []int{8} +} + +func (x *BlockList) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *BlockList) GetNested() *Spec { + if x != nil { + return x.Nested + } + return nil +} + +// Object spec type is the most commonly used at the root of a spec file. +// Its result is a JSON object whose properties are set based on any nested +// spec blocks: +type Object struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Attributes map[string]*Spec `protobuf:"bytes,1,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *Object) Reset() { + *x = Object{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_hcl_spec_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Object) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Object) ProtoMessage() {} + +func (x *Object) ProtoReflect() protoreflect.Message { + mi := &file_rpc_hcl_spec_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Object.ProtoReflect.Descriptor instead. +func (*Object) Descriptor() ([]byte, []int) { + return file_rpc_hcl_spec_proto_rawDescGZIP(), []int{9} +} + +func (x *Object) GetAttributes() map[string]*Spec { + if x != nil { + return x.Attributes + } + return nil +} + +var File_rpc_hcl_spec_proto protoreflect.FileDescriptor + +var file_rpc_hcl_spec_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x72, 0x70, 0x63, 0x2f, 0x68, 0x63, 0x6c, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x80, 0x01, 0x0a, 0x07, 0x43, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x2d, 0x0a, 0x09, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x43, 0x74, 0x79, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, + 0x76, 0x65, 0x48, 0x00, 0x52, 0x09, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, + 0x1e, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, + 0x43, 0x74, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x12, + 0x1b, 0x0a, 0x03, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x43, + 0x74, 0x79, 0x4d, 0x61, 0x70, 0x48, 0x00, 0x52, 0x03, 0x6d, 0x61, 0x70, 0x42, 0x09, 0x0a, 0x07, + 0x74, 0x79, 0x70, 0x65, 0x44, 0x65, 0x66, 0x22, 0x2e, 0x0a, 0x0c, 0x43, 0x74, 0x79, 0x50, 0x72, + 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x79, 0x70, 0x65, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x79, 0x70, + 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x35, 0x0a, 0x07, 0x43, 0x74, 0x79, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x43, 0x74, 0x79, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x34, + 0x0a, 0x06, 0x43, 0x74, 0x79, 0x4d, 0x61, 0x70, 0x12, 0x2a, 0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, + 0x43, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x22, 0x83, 0x01, 0x0a, 0x08, 0x48, 0x43, 0x4c, 0x32, 0x53, 0x70, 0x65, + 0x63, 0x12, 0x33, 0x0a, 0x08, 0x54, 0x79, 0x70, 0x65, 0x53, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x48, 0x43, 0x4c, 0x32, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x54, + 0x79, 0x70, 0x65, 0x53, 0x70, 0x65, 0x63, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x54, 0x79, + 0x70, 0x65, 0x53, 0x70, 0x65, 0x63, 0x1a, 0x42, 0x0a, 0x0d, 0x54, 0x79, 0x70, 0x65, 0x53, 0x70, + 0x65, 0x63, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa7, 0x01, 0x0a, 0x04, 0x53, + 0x70, 0x65, 0x63, 0x12, 0x21, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x06, + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x41, 0x74, 0x74, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x48, 0x00, 0x52, 0x04, 0x41, + 0x74, 0x74, 0x72, 0x12, 0x29, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x00, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2b, + 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, + 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x54, 0x0a, 0x04, 0x41, 0x74, 0x74, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, + 0x2e, 0x43, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x22, 0x56, 0x0a, 0x05, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x06, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x06, 0x6e, 0x65, 0x73, 0x74, + 0x65, 0x64, 0x22, 0x3e, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x06, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x06, 0x6e, 0x65, 0x73, 0x74, + 0x65, 0x64, 0x22, 0x87, 0x01, 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x37, 0x0a, + 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x44, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x53, 0x70, 0x65, + 0x63, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x5a, 0x04, + 0x72, 0x70, 0x63, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_rpc_hcl_spec_proto_rawDescOnce sync.Once + file_rpc_hcl_spec_proto_rawDescData = file_rpc_hcl_spec_proto_rawDesc +) + +func file_rpc_hcl_spec_proto_rawDescGZIP() []byte { + file_rpc_hcl_spec_proto_rawDescOnce.Do(func() { + file_rpc_hcl_spec_proto_rawDescData = protoimpl.X.CompressGZIP(file_rpc_hcl_spec_proto_rawDescData) + }) + return file_rpc_hcl_spec_proto_rawDescData +} + +var file_rpc_hcl_spec_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_rpc_hcl_spec_proto_goTypes = []interface{}{ + (*CtyType)(nil), // 0: CtyType + (*CtyPrimitive)(nil), // 1: CtyPrimitive + (*CtyList)(nil), // 2: CtyList + (*CtyMap)(nil), // 3: CtyMap + (*HCL2Spec)(nil), // 4: HCL2Spec + (*Spec)(nil), // 5: Spec + (*Attr)(nil), // 6: Attr + (*Block)(nil), // 7: Block + (*BlockList)(nil), // 8: BlockList + (*Object)(nil), // 9: Object + nil, // 10: HCL2Spec.TypeSpecEntry + nil, // 11: Object.AttributesEntry +} +var file_rpc_hcl_spec_proto_depIdxs = []int32{ + 1, // 0: CtyType.primitive:type_name -> CtyPrimitive + 2, // 1: CtyType.list:type_name -> CtyList + 3, // 2: CtyType.map:type_name -> CtyMap + 0, // 3: CtyList.elementType:type_name -> CtyType + 0, // 4: CtyMap.elementType:type_name -> CtyType + 10, // 5: HCL2Spec.TypeSpec:type_name -> HCL2Spec.TypeSpecEntry + 9, // 6: Spec.object:type_name -> Object + 6, // 7: Spec.Attr:type_name -> Attr + 7, // 8: Spec.block_value:type_name -> Block + 8, // 9: Spec.block_list:type_name -> BlockList + 0, // 10: Attr.type:type_name -> CtyType + 5, // 11: Block.nested:type_name -> Spec + 5, // 12: BlockList.nested:type_name -> Spec + 11, // 13: Object.attributes:type_name -> Object.AttributesEntry + 5, // 14: HCL2Spec.TypeSpecEntry.value:type_name -> Spec + 5, // 15: Object.AttributesEntry.value:type_name -> Spec + 16, // [16:16] is the sub-list for method output_type + 16, // [16:16] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name +} + +func init() { file_rpc_hcl_spec_proto_init() } +func file_rpc_hcl_spec_proto_init() { + if File_rpc_hcl_spec_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_rpc_hcl_spec_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CtyType); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_hcl_spec_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CtyPrimitive); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_hcl_spec_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CtyList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_hcl_spec_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CtyMap); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_hcl_spec_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HCL2Spec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_hcl_spec_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Spec); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_hcl_spec_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Attr); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_hcl_spec_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Block); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_hcl_spec_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_hcl_spec_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Object); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_rpc_hcl_spec_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*CtyType_Primitive)(nil), + (*CtyType_List)(nil), + (*CtyType_Map)(nil), + } + file_rpc_hcl_spec_proto_msgTypes[5].OneofWrappers = []interface{}{ + (*Spec_Object)(nil), + (*Spec_Attr)(nil), + (*Spec_BlockValue)(nil), + (*Spec_BlockList)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_rpc_hcl_spec_proto_rawDesc, + NumEnums: 0, + NumMessages: 12, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_rpc_hcl_spec_proto_goTypes, + DependencyIndexes: file_rpc_hcl_spec_proto_depIdxs, + MessageInfos: file_rpc_hcl_spec_proto_msgTypes, + }.Build() + File_rpc_hcl_spec_proto = out.File + file_rpc_hcl_spec_proto_rawDesc = nil + file_rpc_hcl_spec_proto_goTypes = nil + file_rpc_hcl_spec_proto_depIdxs = nil +} diff --git a/rpc/hcl_spec.proto b/rpc/hcl_spec.proto new file mode 100644 index 000000000..ea83f8f56 --- /dev/null +++ b/rpc/hcl_spec.proto @@ -0,0 +1,94 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +syntax = "proto3"; + +option go_package = "rpc/"; + +// CtyType is any of the cty types that can be used for a Attribute. +// +// Bodies aren't an issue since they're encompassing a bunch of different +// attributes, which end-up referencing a type from this structure. +message CtyType { + oneof typeDef { + CtyPrimitive primitive = 1; + CtyList list = 2; + CtyMap map = 3; + } +} + +// CtyPrimitive is any of the cty.Type that match the `IsPrimitiveType` function +// i.e. either Number, Bool or String. +message CtyPrimitive { + string typeString = 1; +} + +// CtyList is a list of a cty.Type +message CtyList { + CtyType elementType = 1; +} + +// CtyMap is a map from one type to another +message CtyMap { + CtyType elementType = 1; +} + +/* +HCL2Spec matches what Packer already consumes from plugins in order to describe +their contents' schema, and lets Packer decode the configuration provided by +the user to cty values, and detect problems with the contents before executing them. + +These are sent over-the-wire over gRPC, much like the old system did using gob +encoding and standard go RPC servers. +*/ +message HCL2Spec { + map TypeSpec = 1; +} + +// A Spec is any kind of object that can convert losslessly to any of the hcldec.Spec types. +message Spec { + oneof block { + Object object = 1; + // buf:lint:ignore FIELD_LOWER_SNAKE_CASE + Attr Attr = 2; + Block block_value = 3; + BlockList block_list = 4; + } +} + +/* Attr spec type reads the value of an attribute in the current body +and returns that value as its result. It also creates validation constraints +for the given attribute name and its value. +*/ +message Attr { + string name = 1; + CtyType type = 2; + bool required = 3; +} + +/* Block spec type applies one nested spec block to the contents of a +block within the current body and returns the result of that spec. It also +creates validation constraints for the given block type name. +*/ +message Block { + string name = 1; + bool required = 2; + Spec nested = 3; +} + +/* BlockList spec type is similar to `Block`, but it accepts zero or +more blocks of a specified type rather than requiring zero or one. The +result is a JSON array with one entry per block of the given type. +*/ +message BlockList { + string name = 1; + Spec nested = 2; +} + +/* Object spec type is the most commonly used at the root of a spec file. +Its result is a JSON object whose properties are set based on any nested +spec blocks: +*/ +message Object { + map attributes = 1; +} From fdcd784d5f4ece22b69d077abd7b1ca7f3f90a3a Mon Sep 17 00:00:00 2001 From: Lucas Bajolet Date: Fri, 16 Feb 2024 10:41:09 -0500 Subject: [PATCH 2/2] rpc: allow using both gob/protobuf for structs As follow-up to the introduction of the protobufs for HCLSpec, we introduce a new environment variable and code to use those structures, so we don't use gob for serialising HCLSpecs. This should make the plugins and packer able to transmit data over-the-wire without using gob for the most part (the communicators still use it, and will probably need some work to replace). --- Makefile | 2 +- go.mod | 11 +++-- go.sum | 22 ++++++++-- plugin/server.go | 2 +- rpc/common.go | 104 ++++++++++++++++++++++++++++++++++++++++++---- rpc/datasource.go | 81 +++++++++++++++++++++++++++++------- 6 files changed, 191 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 2e86ca800..82d27908d 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ generate: install-gen-deps ## Generate dynamically generated code @find ./ -type f | xargs grep -l '^// Code generated' | xargs rm -f PROJECT_ROOT="$(CURDIR)" go generate ./... go fmt bootcommand/boot_command.go -# go run ./cmd/generate-fixer-deprecations + protoc rpc/hcl_spec.proto --go_out=. --go_opt=paths=source_relative generate-check: generate ## Check go code generation is on par @echo "==> Checking that auto-generated code is not changed..." diff --git a/go.mod b/go.mod index 70f3f37be..a0ba47790 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/agext/levenshtein v1.2.3 github.com/antchfx/xpath v1.1.11 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go v1.44.114 + github.com/aws/aws-sdk-go v1.45.6 github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/dylanmei/iso8601 v0.1.0 // indirect github.com/dylanmei/winrmtest v0.0.0-20210303004826-fbc9ae56efb6 @@ -74,6 +74,12 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) +require ( + github.com/vmihailenco/msgpack/v5 v5.3.5 + google.golang.org/grpc v1.50.1 + google.golang.org/protobuf v1.28.1 +) + require ( cloud.google.com/go/compute v1.12.1 // indirect cloud.google.com/go/compute/metadata v0.1.1 // indirect @@ -110,14 +116,13 @@ require ( github.com/posener/complete v1.2.3 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/spf13/cast v1.3.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect go.opencensus.io v0.23.0 // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect golang.org/x/oauth2 v0.1.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect - google.golang.org/grpc v1.50.1 // indirect - google.golang.org/protobuf v1.28.1 // indirect ) go 1.20 diff --git a/go.sum b/go.sum index 366761cea..6d6495cbb 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.44.114 h1:plIkWc/RsHr3DXBj4MEw9sEW4CcL/e2ryokc+CKyq1I= -github.com/aws/aws-sdk-go v1.44.114/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.45.6 h1:Y2isQQBZsnO15dzUQo9YQRThtHgrV200XCH05BRHVJI= +github.com/aws/aws-sdk-go v1.45.6/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -349,7 +349,12 @@ github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.13.3 h1:m+b9q3YDbg6Bec5rr+KGy1MzEVzY/jC2X+YX4yqKtHI= github.com/zclconf/go-cty v1.13.3/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= @@ -366,6 +371,7 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -382,6 +388,7 @@ golang.org/x/mobile v0.0.0-20210901025245-1fde1d6c3ca1 h1:t3ZHqovedSY8DEAUmZA99f golang.org/x/mobile v0.0.0-20210901025245-1fde1d6c3ca1/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -399,7 +406,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -411,6 +419,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -437,14 +446,17 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -452,6 +464,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= @@ -465,6 +478,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/plugin/server.go b/plugin/server.go index 5c657efab..57e66aed9 100644 --- a/plugin/server.go +++ b/plugin/server.go @@ -54,7 +54,7 @@ const ( // // Api version 4 did not have the notion of a minor version, so Packer will // error with a weird error message. - APIVersionMajor, APIVersionMinor = "5", "0" + APIVersionMajor, APIVersionMinor = "5", "1" ) var ErrManuallyStartedPlugin = errors.New( diff --git a/rpc/common.go b/rpc/common.go index 2a0dadc34..2f98606a3 100644 --- a/rpc/common.go +++ b/rpc/common.go @@ -7,12 +7,30 @@ import ( "bytes" "encoding/gob" "fmt" + "log" "net/rpc" + "os" + "reflect" "github.com/hashicorp/hcl/v2/hcldec" "github.com/zclconf/go-cty/cty" + "google.golang.org/protobuf/proto" ) +const PackerUsePBEnvVar = "PACKER_RPC_PB" + +// useProtobuf is used by Packer/plugins to determine if structs like +// hclspec.Spec should be serialised to protobuf instead of gob. +func useProtobuf() bool { + useProto := os.Getenv(PackerUsePBEnvVar) + switch useProto { + case "", "0", "no", "false": + return false + } + + return true +} + // commonClient allows to rpc call funcs that can be defined on the different // build blocks of Packer. type commonClient struct { @@ -48,21 +66,91 @@ func (p *commonClient) ConfigSpec() hcldec.ObjectSpec { panic(err.Error()) } - res := hcldec.ObjectSpec{} - err := gob.NewDecoder(bytes.NewReader(resp.ConfigSpec)).Decode(&res) + // Legacy: this will need to be removed when we discontinue gob-encoding + // + // This is required for backwards compatibility for now, but using + // gob to encode the spec objects will fail against the upstream cty + // library, since they removed support for it. + // + // This will be a breaking change, as older plugins won't be able to + // communicate with Packer any longer. + if !useProtobuf() { + log.Printf("[DEBUG] - common: receiving ConfigSpec as gob") + res := hcldec.ObjectSpec{} + err := gob.NewDecoder(bytes.NewReader(resp.ConfigSpec)).Decode(&res) + if err != nil { + panic(fmt.Errorf("failed to decode HCL spec from gob: %s", err)) + } + return res + } + + log.Printf("[DEBUG] - common: receiving ConfigSpec as protobuf") + spec, err := protobufToHCL2Spec(resp.ConfigSpec) if err != nil { - panic("ici:" + err.Error()) + panic(err) } - return res + + return spec } func (s *commonServer) ConfigSpec(_ interface{}, reply *ConfigSpecResponse) error { spec := s.selfConfigurable.ConfigSpec() - b := bytes.NewBuffer(nil) - err := gob.NewEncoder(b).Encode(spec) - reply.ConfigSpec = b.Bytes() - return err + if !useProtobuf() { + log.Printf("[DEBUG] - common: sending ConfigSpec as gob") + b := &bytes.Buffer{} + err := gob.NewEncoder(b).Encode(spec) + if err != nil { + return fmt.Errorf("failed to encode spec from gob: %s", err) + } + reply.ConfigSpec = b.Bytes() + + return nil + } + + log.Printf("[DEBUG] - common: sending ConfigSpec as protobuf") + rawBytes, err := hcl2SpecToProtobuf(spec) + if err != nil { + return fmt.Errorf("failed to encode HCL spec from protobuf: %s", err) + } + reply.ConfigSpec = rawBytes + + return nil +} + +// hcl2SpecToProtobuf converts a hcldec.ObjectSpec to a protobuf-serialised +// byte array so it can then be used to send to a Plugin/Packer. +func hcl2SpecToProtobuf(spec hcldec.ObjectSpec) ([]byte, error) { + ret, err := ToProto(spec) + if err != nil { + return nil, fmt.Errorf("failed to convert hcldec.Spec to hclspec.Spec: %s", err) + } + rawBytes, err := proto.Marshal(ret) + if err != nil { + return nil, fmt.Errorf("failed to serialise hclspec.Spec to protobuf: %s", err) + } + + return rawBytes, nil +} + +// protobufToHCL2Spec converts a protobuf-encoded spec to a usable hcldec.Spec. +func protobufToHCL2Spec(serData []byte) (hcldec.ObjectSpec, error) { + confSpec := &Spec{} + err := proto.Unmarshal(serData, confSpec) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal hclspec.Spec from raw protobuf: %q", err) + } + spec, err := confSpec.FromProto() + if err != nil { + return nil, fmt.Errorf("failed to decode HCL spec: %q", err) + } + + obj, ok := spec.(*hcldec.ObjectSpec) + if !ok { + return nil, fmt.Errorf("decoded HCL spec is not an object spec: %s", reflect.TypeOf(spec).String()) + } + + return *obj, nil } func init() { diff --git a/rpc/datasource.go b/rpc/datasource.go index 443eeee85..d7cab6f85 100644 --- a/rpc/datasource.go +++ b/rpc/datasource.go @@ -7,10 +7,12 @@ import ( "bytes" "encoding/gob" "fmt" + "log" "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/msgpack" ) // An implementation of packer.Datasource where the data source is actually @@ -52,10 +54,21 @@ func (d *datasource) OutputSpec() hcldec.ObjectSpec { err := fmt.Errorf("Datasource.OutputSpec failed: %v", err) panic(err.Error()) } - res := hcldec.ObjectSpec{} - err := gob.NewDecoder(bytes.NewReader(resp.OutputSpec)).Decode(&res) + + if !useProtobuf() { + log.Printf("[DEBUG] - datasource: receiving OutputSpec as gob") + res := hcldec.ObjectSpec{} + err := gob.NewDecoder(bytes.NewReader(resp.OutputSpec)).Decode(&res) + if err != nil { + panic(fmt.Sprintf("datasource: failed to deserialise HCL spec from gob: %s", err)) + } + return res + } + + log.Printf("[DEBUG] - datasource: receiving OutputSpec as gob") + res, err := protobufToHCL2Spec(resp.OutputSpec) if err != nil { - panic("ici:" + err.Error()) + panic(fmt.Sprintf("datasource: failed to deserialise HCL spec from protobuf: %s", err)) } return res } @@ -66,20 +79,35 @@ type ExecuteResponse struct { } func (d *datasource) Execute() (cty.Value, error) { - res := new(cty.Value) resp := new(ExecuteResponse) if err := d.client.Call(d.endpoint+".Execute", new(interface{}), resp); err != nil { err := fmt.Errorf("Datasource.Execute failed: %v", err) - return *res, err + return cty.NilVal, err + } + + if !useProtobuf() { + log.Printf("[DEBUG] - datasource: receiving Execute as gob") + res := cty.Value{} + err := gob.NewDecoder(bytes.NewReader(resp.Value)).Decode(&res) + if err != nil { + return res, fmt.Errorf("failed to unmarshal cty.Value from gob blob: %s", err) + } + if resp.Error != nil { + err = resp.Error + } + return res, err } - err := gob.NewDecoder(bytes.NewReader(resp.Value)).Decode(&res) + + log.Printf("[DEBUG] - datasource: receiving Execute as msgpack") + res, err := msgpack.Unmarshal(resp.Value, cty.DynamicPseudoType) if err != nil { - return *res, err + return cty.NilVal, fmt.Errorf("failed to unmarshal cty.Value from msgpack blob: %s", err) } + if resp.Error != nil { err = resp.Error } - return *res, err + return res, err } // DatasourceServer wraps a packer.Datasource implementation and makes it @@ -103,18 +131,43 @@ func (d *DatasourceServer) Configure(args *DatasourceConfigureArgs, reply *Datas func (d *DatasourceServer) OutputSpec(args *DatasourceConfigureArgs, reply *OutputSpecResponse) error { spec := d.d.OutputSpec() - b := bytes.NewBuffer(nil) - err := gob.NewEncoder(b).Encode(spec) - reply.OutputSpec = b.Bytes() + + if !useProtobuf() { + log.Printf("[DEBUG] - datasource: sending OutputSpec as gob") + b := &bytes.Buffer{} + err := gob.NewEncoder(b).Encode(spec) + reply.OutputSpec = b.Bytes() + return err + } + + log.Printf("[DEBUG] - datasource: sending OutputSpec as protobuf") + ret, err := hcl2SpecToProtobuf(spec) + if err != nil { + return err + } + reply.OutputSpec = ret + return err } func (d *DatasourceServer) Execute(args *interface{}, reply *ExecuteResponse) error { spec, err := d.d.Execute() reply.Error = NewBasicError(err) - b := bytes.NewBuffer(nil) - err = gob.NewEncoder(b).Encode(spec) - reply.Value = b.Bytes() + + if !useProtobuf() { + log.Printf("[DEBUG] - datasource: sending Execute as gob") + b := &bytes.Buffer{} + err = gob.NewEncoder(b).Encode(spec) + reply.Value = b.Bytes() + if reply.Error != nil { + err = reply.Error + } + return err + } + + log.Printf("[DEBUG] - datasource: sending Execute as msgpack") + raw, err := msgpack.Marshal(spec, cty.DynamicPseudoType) + reply.Value = raw if reply.Error != nil { err = reply.Error }