github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/plugin/convert/schema.go (about) 1 package convert 2 3 import ( 4 "encoding/json" 5 "reflect" 6 "sort" 7 8 "github.com/hashicorp/terraform/configs/configschema" 9 proto "github.com/hashicorp/terraform/internal/tfplugin5" 10 "github.com/hashicorp/terraform/providers" 11 ) 12 13 // ConfigSchemaToProto takes a *configschema.Block and converts it to a 14 // proto.Schema_Block for a grpc response. 15 func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block { 16 block := &proto.Schema_Block{} 17 18 for _, name := range sortedKeys(b.Attributes) { 19 a := b.Attributes[name] 20 attr := &proto.Schema_Attribute{ 21 Name: name, 22 Description: a.Description, 23 Optional: a.Optional, 24 Computed: a.Computed, 25 Required: a.Required, 26 Sensitive: a.Sensitive, 27 } 28 29 ty, err := json.Marshal(a.Type) 30 if err != nil { 31 panic(err) 32 } 33 34 attr.Type = ty 35 36 block.Attributes = append(block.Attributes, attr) 37 } 38 39 for _, name := range sortedKeys(b.BlockTypes) { 40 b := b.BlockTypes[name] 41 block.BlockTypes = append(block.BlockTypes, protoSchemaNestedBlock(name, b)) 42 } 43 44 return block 45 } 46 47 func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Schema_NestedBlock { 48 var nesting proto.Schema_NestedBlock_NestingMode 49 switch b.Nesting { 50 case configschema.NestingSingle: 51 nesting = proto.Schema_NestedBlock_SINGLE 52 case configschema.NestingGroup: 53 nesting = proto.Schema_NestedBlock_GROUP 54 case configschema.NestingList: 55 nesting = proto.Schema_NestedBlock_LIST 56 case configschema.NestingSet: 57 nesting = proto.Schema_NestedBlock_SET 58 case configschema.NestingMap: 59 nesting = proto.Schema_NestedBlock_MAP 60 default: 61 nesting = proto.Schema_NestedBlock_INVALID 62 } 63 return &proto.Schema_NestedBlock{ 64 TypeName: name, 65 Block: ConfigSchemaToProto(&b.Block), 66 Nesting: nesting, 67 MinItems: int64(b.MinItems), 68 MaxItems: int64(b.MaxItems), 69 } 70 } 71 72 // ProtoToProviderSchema takes a proto.Schema and converts it to a providers.Schema. 73 func ProtoToProviderSchema(s *proto.Schema) providers.Schema { 74 return providers.Schema{ 75 Version: s.Version, 76 Block: ProtoToConfigSchema(s.Block), 77 } 78 } 79 80 // ProtoToConfigSchema takes the GetSchcema_Block from a grpc response and converts it 81 // to a terraform *configschema.Block. 82 func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block { 83 block := &configschema.Block{ 84 Attributes: make(map[string]*configschema.Attribute), 85 BlockTypes: make(map[string]*configschema.NestedBlock), 86 } 87 88 for _, a := range b.Attributes { 89 attr := &configschema.Attribute{ 90 Description: a.Description, 91 Required: a.Required, 92 Optional: a.Optional, 93 Computed: a.Computed, 94 Sensitive: a.Sensitive, 95 } 96 97 if err := json.Unmarshal(a.Type, &attr.Type); err != nil { 98 panic(err) 99 } 100 101 block.Attributes[a.Name] = attr 102 } 103 104 for _, b := range b.BlockTypes { 105 block.BlockTypes[b.TypeName] = schemaNestedBlock(b) 106 } 107 108 return block 109 } 110 111 func schemaNestedBlock(b *proto.Schema_NestedBlock) *configschema.NestedBlock { 112 var nesting configschema.NestingMode 113 switch b.Nesting { 114 case proto.Schema_NestedBlock_SINGLE: 115 nesting = configschema.NestingSingle 116 case proto.Schema_NestedBlock_GROUP: 117 nesting = configschema.NestingGroup 118 case proto.Schema_NestedBlock_LIST: 119 nesting = configschema.NestingList 120 case proto.Schema_NestedBlock_MAP: 121 nesting = configschema.NestingMap 122 case proto.Schema_NestedBlock_SET: 123 nesting = configschema.NestingSet 124 default: 125 // In all other cases we'll leave it as the zero value (invalid) and 126 // let the caller validate it and deal with this. 127 } 128 129 nb := &configschema.NestedBlock{ 130 Nesting: nesting, 131 MinItems: int(b.MinItems), 132 MaxItems: int(b.MaxItems), 133 } 134 135 nested := ProtoToConfigSchema(b.Block) 136 nb.Block = *nested 137 return nb 138 } 139 140 // sortedKeys returns the lexically sorted keys from the given map. This is 141 // used to make schema conversions are deterministic. This panics if map keys 142 // are not a string. 143 func sortedKeys(m interface{}) []string { 144 v := reflect.ValueOf(m) 145 keys := make([]string, v.Len()) 146 147 mapKeys := v.MapKeys() 148 for i, k := range mapKeys { 149 keys[i] = k.Interface().(string) 150 } 151 152 sort.Strings(keys) 153 return keys 154 }