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  }