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 }