kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/plugin6/convert/schema.go (about)

     1  package convert
     2  
     3  import (
     4  	"encoding/json"
     5  	"reflect"
     6  	"sort"
     7  
     8  	"kubeform.dev/terraform-backend-sdk/configs/configschema"
     9  	"kubeform.dev/terraform-backend-sdk/providers"
    10  	proto "kubeform.dev/terraform-backend-sdk/tfplugin6"
    11  	"github.com/zclconf/go-cty/cty"
    12  )
    13  
    14  // ConfigSchemaToProto takes a *configschema.Block and converts it to a
    15  // proto.Schema_Block for a grpc response.
    16  func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block {
    17  	block := &proto.Schema_Block{
    18  		Description:     b.Description,
    19  		DescriptionKind: protoStringKind(b.DescriptionKind),
    20  		Deprecated:      b.Deprecated,
    21  	}
    22  
    23  	for _, name := range sortedKeys(b.Attributes) {
    24  		a := b.Attributes[name]
    25  
    26  		attr := &proto.Schema_Attribute{
    27  			Name:            name,
    28  			Description:     a.Description,
    29  			DescriptionKind: protoStringKind(a.DescriptionKind),
    30  			Optional:        a.Optional,
    31  			Computed:        a.Computed,
    32  			Required:        a.Required,
    33  			Sensitive:       a.Sensitive,
    34  			Deprecated:      a.Deprecated,
    35  		}
    36  
    37  		if a.Type != cty.NilType {
    38  			ty, err := json.Marshal(a.Type)
    39  			if err != nil {
    40  				panic(err)
    41  			}
    42  			attr.Type = ty
    43  		}
    44  
    45  		if a.NestedType != nil {
    46  			attr.NestedType = configschemaObjectToProto(a.NestedType)
    47  		}
    48  
    49  		block.Attributes = append(block.Attributes, attr)
    50  	}
    51  
    52  	for _, name := range sortedKeys(b.BlockTypes) {
    53  		b := b.BlockTypes[name]
    54  		block.BlockTypes = append(block.BlockTypes, protoSchemaNestedBlock(name, b))
    55  	}
    56  
    57  	return block
    58  }
    59  
    60  func protoStringKind(k configschema.StringKind) proto.StringKind {
    61  	switch k {
    62  	default:
    63  		return proto.StringKind_PLAIN
    64  	case configschema.StringMarkdown:
    65  		return proto.StringKind_MARKDOWN
    66  	}
    67  }
    68  
    69  func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Schema_NestedBlock {
    70  	var nesting proto.Schema_NestedBlock_NestingMode
    71  	switch b.Nesting {
    72  	case configschema.NestingSingle:
    73  		nesting = proto.Schema_NestedBlock_SINGLE
    74  	case configschema.NestingGroup:
    75  		nesting = proto.Schema_NestedBlock_GROUP
    76  	case configschema.NestingList:
    77  		nesting = proto.Schema_NestedBlock_LIST
    78  	case configschema.NestingSet:
    79  		nesting = proto.Schema_NestedBlock_SET
    80  	case configschema.NestingMap:
    81  		nesting = proto.Schema_NestedBlock_MAP
    82  	default:
    83  		nesting = proto.Schema_NestedBlock_INVALID
    84  	}
    85  	return &proto.Schema_NestedBlock{
    86  		TypeName: name,
    87  		Block:    ConfigSchemaToProto(&b.Block),
    88  		Nesting:  nesting,
    89  		MinItems: int64(b.MinItems),
    90  		MaxItems: int64(b.MaxItems),
    91  	}
    92  }
    93  
    94  // ProtoToProviderSchema takes a proto.Schema and converts it to a providers.Schema.
    95  func ProtoToProviderSchema(s *proto.Schema) providers.Schema {
    96  	return providers.Schema{
    97  		Version: s.Version,
    98  		Block:   ProtoToConfigSchema(s.Block),
    99  	}
   100  }
   101  
   102  // ProtoToConfigSchema takes the GetSchcema_Block from a grpc response and converts it
   103  // to a terraform *configschema.Block.
   104  func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block {
   105  	block := &configschema.Block{
   106  		Attributes: make(map[string]*configschema.Attribute),
   107  		BlockTypes: make(map[string]*configschema.NestedBlock),
   108  
   109  		Description:     b.Description,
   110  		DescriptionKind: schemaStringKind(b.DescriptionKind),
   111  		Deprecated:      b.Deprecated,
   112  	}
   113  
   114  	for _, a := range b.Attributes {
   115  		attr := &configschema.Attribute{
   116  			Description:     a.Description,
   117  			DescriptionKind: schemaStringKind(a.DescriptionKind),
   118  			Required:        a.Required,
   119  			Optional:        a.Optional,
   120  			Computed:        a.Computed,
   121  			Sensitive:       a.Sensitive,
   122  			Deprecated:      a.Deprecated,
   123  		}
   124  
   125  		if a.Type != nil {
   126  			if err := json.Unmarshal(a.Type, &attr.Type); err != nil {
   127  				panic(err)
   128  			}
   129  		}
   130  
   131  		if a.NestedType != nil {
   132  			attr.NestedType = protoObjectToConfigSchema(a.NestedType)
   133  		}
   134  
   135  		block.Attributes[a.Name] = attr
   136  	}
   137  
   138  	for _, b := range b.BlockTypes {
   139  		block.BlockTypes[b.TypeName] = schemaNestedBlock(b)
   140  	}
   141  
   142  	return block
   143  }
   144  
   145  func schemaStringKind(k proto.StringKind) configschema.StringKind {
   146  	switch k {
   147  	default:
   148  		return configschema.StringPlain
   149  	case proto.StringKind_MARKDOWN:
   150  		return configschema.StringMarkdown
   151  	}
   152  }
   153  
   154  func schemaNestedBlock(b *proto.Schema_NestedBlock) *configschema.NestedBlock {
   155  	var nesting configschema.NestingMode
   156  	switch b.Nesting {
   157  	case proto.Schema_NestedBlock_SINGLE:
   158  		nesting = configschema.NestingSingle
   159  	case proto.Schema_NestedBlock_GROUP:
   160  		nesting = configschema.NestingGroup
   161  	case proto.Schema_NestedBlock_LIST:
   162  		nesting = configschema.NestingList
   163  	case proto.Schema_NestedBlock_MAP:
   164  		nesting = configschema.NestingMap
   165  	case proto.Schema_NestedBlock_SET:
   166  		nesting = configschema.NestingSet
   167  	default:
   168  		// In all other cases we'll leave it as the zero value (invalid) and
   169  		// let the caller validate it and deal with this.
   170  	}
   171  
   172  	nb := &configschema.NestedBlock{
   173  		Nesting:  nesting,
   174  		MinItems: int(b.MinItems),
   175  		MaxItems: int(b.MaxItems),
   176  	}
   177  
   178  	nested := ProtoToConfigSchema(b.Block)
   179  	nb.Block = *nested
   180  	return nb
   181  }
   182  
   183  func protoObjectToConfigSchema(b *proto.Schema_Object) *configschema.Object {
   184  	var nesting configschema.NestingMode
   185  	switch b.Nesting {
   186  	case proto.Schema_Object_SINGLE:
   187  		nesting = configschema.NestingSingle
   188  	case proto.Schema_Object_LIST:
   189  		nesting = configschema.NestingList
   190  	case proto.Schema_Object_MAP:
   191  		nesting = configschema.NestingMap
   192  	case proto.Schema_Object_SET:
   193  		nesting = configschema.NestingSet
   194  	default:
   195  		// In all other cases we'll leave it as the zero value (invalid) and
   196  		// let the caller validate it and deal with this.
   197  	}
   198  
   199  	object := &configschema.Object{
   200  		Attributes: make(map[string]*configschema.Attribute),
   201  		Nesting:    nesting,
   202  		MinItems:   int(b.MinItems),
   203  		MaxItems:   int(b.MaxItems),
   204  	}
   205  
   206  	for _, a := range b.Attributes {
   207  		attr := &configschema.Attribute{
   208  			Description:     a.Description,
   209  			DescriptionKind: schemaStringKind(a.DescriptionKind),
   210  			Required:        a.Required,
   211  			Optional:        a.Optional,
   212  			Computed:        a.Computed,
   213  			Sensitive:       a.Sensitive,
   214  			Deprecated:      a.Deprecated,
   215  		}
   216  
   217  		if a.Type != nil {
   218  			if err := json.Unmarshal(a.Type, &attr.Type); err != nil {
   219  				panic(err)
   220  			}
   221  		}
   222  
   223  		if a.NestedType != nil {
   224  			attr.NestedType = protoObjectToConfigSchema(a.NestedType)
   225  		}
   226  
   227  		object.Attributes[a.Name] = attr
   228  	}
   229  
   230  	return object
   231  }
   232  
   233  // sortedKeys returns the lexically sorted keys from the given map. This is
   234  // used to make schema conversions are deterministic. This panics if map keys
   235  // are not a string.
   236  func sortedKeys(m interface{}) []string {
   237  	v := reflect.ValueOf(m)
   238  	keys := make([]string, v.Len())
   239  
   240  	mapKeys := v.MapKeys()
   241  	for i, k := range mapKeys {
   242  		keys[i] = k.Interface().(string)
   243  	}
   244  
   245  	sort.Strings(keys)
   246  	return keys
   247  }
   248  
   249  func configschemaObjectToProto(b *configschema.Object) *proto.Schema_Object {
   250  	var nesting proto.Schema_Object_NestingMode
   251  	switch b.Nesting {
   252  	case configschema.NestingSingle:
   253  		nesting = proto.Schema_Object_SINGLE
   254  	case configschema.NestingList:
   255  		nesting = proto.Schema_Object_LIST
   256  	case configschema.NestingSet:
   257  		nesting = proto.Schema_Object_SET
   258  	case configschema.NestingMap:
   259  		nesting = proto.Schema_Object_MAP
   260  	default:
   261  		nesting = proto.Schema_Object_INVALID
   262  	}
   263  
   264  	attributes := make([]*proto.Schema_Attribute, len(b.Attributes))
   265  
   266  	for _, name := range sortedKeys(b.Attributes) {
   267  		a := b.Attributes[name]
   268  
   269  		attr := &proto.Schema_Attribute{
   270  			Name:            name,
   271  			Description:     a.Description,
   272  			DescriptionKind: protoStringKind(a.DescriptionKind),
   273  			Optional:        a.Optional,
   274  			Computed:        a.Computed,
   275  			Required:        a.Required,
   276  			Sensitive:       a.Sensitive,
   277  			Deprecated:      a.Deprecated,
   278  		}
   279  
   280  		if a.Type != cty.NilType {
   281  			ty, err := json.Marshal(a.Type)
   282  			if err != nil {
   283  				panic(err)
   284  			}
   285  			attr.Type = ty
   286  		}
   287  
   288  		if a.NestedType != nil {
   289  			attr.NestedType = configschemaObjectToProto(a.NestedType)
   290  		}
   291  
   292  		attributes = append(attributes, attr)
   293  	}
   294  
   295  	return &proto.Schema_Object{
   296  		Attributes: attributes,
   297  		Nesting:    nesting,
   298  		MinItems:   int64(b.MinItems),
   299  		MaxItems:   int64(b.MaxItems),
   300  	}
   301  }