github.com/cmalfait/terraform@v0.11.12-beta1/helper/schema/core_schema.go (about)

     1  package schema
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/config/configschema"
     7  	"github.com/zclconf/go-cty/cty"
     8  )
     9  
    10  // The functions and methods in this file are concerned with the conversion
    11  // of this package's schema model into the slightly-lower-level schema model
    12  // used by Terraform core for configuration parsing.
    13  
    14  // CoreConfigSchema lowers the receiver to the schema model expected by
    15  // Terraform core.
    16  //
    17  // This lower-level model has fewer features than the schema in this package,
    18  // describing only the basic structure of configuration and state values we
    19  // expect. The full schemaMap from this package is still required for full
    20  // validation, handling of default values, etc.
    21  //
    22  // This method presumes a schema that passes InternalValidate, and so may
    23  // panic or produce an invalid result if given an invalid schemaMap.
    24  func (m schemaMap) CoreConfigSchema() *configschema.Block {
    25  	if len(m) == 0 {
    26  		// We return an actual (empty) object here, rather than a nil,
    27  		// because a nil result would mean that we don't have a schema at
    28  		// all, rather than that we have an empty one.
    29  		return &configschema.Block{}
    30  	}
    31  
    32  	ret := &configschema.Block{
    33  		Attributes: map[string]*configschema.Attribute{},
    34  		BlockTypes: map[string]*configschema.NestedBlock{},
    35  	}
    36  
    37  	for name, schema := range m {
    38  		if schema.Elem == nil {
    39  			ret.Attributes[name] = schema.coreConfigSchemaAttribute()
    40  			continue
    41  		}
    42  		switch schema.Elem.(type) {
    43  		case *Schema:
    44  			ret.Attributes[name] = schema.coreConfigSchemaAttribute()
    45  		case *Resource:
    46  			ret.BlockTypes[name] = schema.coreConfigSchemaBlock()
    47  		default:
    48  			// Should never happen for a valid schema
    49  			panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem))
    50  		}
    51  	}
    52  
    53  	return ret
    54  }
    55  
    56  // coreConfigSchemaAttribute prepares a configschema.Attribute representation
    57  // of a schema. This is appropriate only for primitives or collections whose
    58  // Elem is an instance of Schema. Use coreConfigSchemaBlock for collections
    59  // whose elem is a whole resource.
    60  func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute {
    61  	return &configschema.Attribute{
    62  		Type:      s.coreConfigSchemaType(),
    63  		Optional:  s.Optional,
    64  		Required:  s.Required,
    65  		Computed:  s.Computed,
    66  		Sensitive: s.Sensitive,
    67  	}
    68  }
    69  
    70  // coreConfigSchemaBlock prepares a configschema.NestedBlock representation of
    71  // a schema. This is appropriate only for collections whose Elem is an instance
    72  // of Resource, and will panic otherwise.
    73  func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock {
    74  	ret := &configschema.NestedBlock{}
    75  	if nested := s.Elem.(*Resource).CoreConfigSchema(); nested != nil {
    76  		ret.Block = *nested
    77  	}
    78  	switch s.Type {
    79  	case TypeList:
    80  		ret.Nesting = configschema.NestingList
    81  	case TypeSet:
    82  		ret.Nesting = configschema.NestingSet
    83  	case TypeMap:
    84  		ret.Nesting = configschema.NestingMap
    85  	default:
    86  		// Should never happen for a valid schema
    87  		panic(fmt.Errorf("invalid s.Type %s for s.Elem being resource", s.Type))
    88  	}
    89  
    90  	ret.MinItems = s.MinItems
    91  	ret.MaxItems = s.MaxItems
    92  
    93  	if s.Required && s.MinItems == 0 {
    94  		// configschema doesn't have a "required" representation for nested
    95  		// blocks, but we can fake it by requiring at least one item.
    96  		ret.MinItems = 1
    97  	}
    98  
    99  	return ret
   100  }
   101  
   102  // coreConfigSchemaType determines the core config schema type that corresponds
   103  // to a particular schema's type.
   104  func (s *Schema) coreConfigSchemaType() cty.Type {
   105  	switch s.Type {
   106  	case TypeString:
   107  		return cty.String
   108  	case TypeBool:
   109  		return cty.Bool
   110  	case TypeInt, TypeFloat:
   111  		// configschema doesn't distinguish int and float, so helper/schema
   112  		// will deal with this as an additional validation step after
   113  		// configuration has been parsed and decoded.
   114  		return cty.Number
   115  	case TypeList, TypeSet, TypeMap:
   116  		var elemType cty.Type
   117  		switch set := s.Elem.(type) {
   118  		case *Schema:
   119  			elemType = set.coreConfigSchemaType()
   120  		case *Resource:
   121  			// In practice we don't actually use this for normal schema
   122  			// construction because we construct a NestedBlock in that
   123  			// case instead. See schemaMap.CoreConfigSchema.
   124  			elemType = set.CoreConfigSchema().ImpliedType()
   125  		default:
   126  			if set != nil {
   127  				// Should never happen for a valid schema
   128  				panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", s.Elem))
   129  			}
   130  			// Some pre-existing schemas assume string as default, so we need
   131  			// to be compatible with them.
   132  			elemType = cty.String
   133  		}
   134  		switch s.Type {
   135  		case TypeList:
   136  			return cty.List(elemType)
   137  		case TypeSet:
   138  			return cty.Set(elemType)
   139  		case TypeMap:
   140  			return cty.Map(elemType)
   141  		default:
   142  			// can never get here in practice, due to the case we're inside
   143  			panic("invalid collection type")
   144  		}
   145  	default:
   146  		// should never happen for a valid schema
   147  		panic(fmt.Errorf("invalid Schema.Type %s", s.Type))
   148  	}
   149  }
   150  
   151  // CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema
   152  // on the resource's schema.
   153  func (r *Resource) CoreConfigSchema() *configschema.Block {
   154  	return schemaMap(r.Schema).CoreConfigSchema()
   155  }