github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/ns1/meta.go (about) 1 package ns1 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/hashicorp/terraform/helper/schema" 8 "gopkg.in/ns1/ns1-go.v2/rest/model/data" 9 ) 10 11 type TfSchemaBuilder func(*schema.Schema) 12 13 func mtSimple(t schema.ValueType) TfSchemaBuilder { 14 return func(s *schema.Schema) { 15 s.Type = t 16 } 17 } 18 19 func mtStringEnum(se *StringEnum) TfSchemaBuilder { 20 return func(s *schema.Schema) { 21 s.Type = schema.TypeString 22 s.ValidateFunc = func(v interface{}, k string) ([]string, []error) { 23 _, err := se.Check(v.(string)) 24 if err != nil { 25 return nil, []error{err} 26 } 27 return nil, nil 28 } 29 } 30 } 31 32 var mtInt TfSchemaBuilder = mtSimple(schema.TypeInt) 33 var mtBool TfSchemaBuilder = mtSimple(schema.TypeBool) 34 var mtString TfSchemaBuilder = mtSimple(schema.TypeString) 35 var mtFloat64 TfSchemaBuilder = mtSimple(schema.TypeFloat) 36 37 func mtList(elementSchemaBuilder TfSchemaBuilder) TfSchemaBuilder { 38 return func(s *schema.Schema) { 39 s.Type = schema.TypeList 40 elementSchema := &schema.Schema{} 41 elementSchemaBuilder(elementSchema) 42 s.Elem = elementSchema 43 } 44 } 45 46 var mtStringList TfSchemaBuilder = mtList(mtString) 47 48 type MetaFieldSpec struct { 49 NameInDynamic string 50 NameInStruct string 51 SchemaBuilder TfSchemaBuilder 52 } 53 54 type MetaField struct { 55 MetaFieldSpec 56 NameInDynamicForFeed string 57 StructIndex int 58 StructGoType reflect.Type 59 } 60 61 var georegionEnum *StringEnum = NewStringEnum([]string{ 62 "US-WEST", 63 "US-EAST", 64 "US-CENTRAL", 65 "EUROPE", 66 "AFRICA", 67 "ASIAPAC", 68 "SOUTH-AMERICA", 69 }) 70 71 func makeMetaFields() []MetaField { 72 var specs []MetaFieldSpec = []MetaFieldSpec{ 73 {"up", "Up", mtBool}, 74 {"connections", "Connections", mtInt}, 75 {"requests", "Requests", mtInt}, 76 {"loadavg", "LoadAvg", mtFloat64}, 77 {"pulsar", "Pulsar", mtInt}, 78 {"latitude", "Latitude", mtFloat64}, 79 {"longitude", "Longitude", mtFloat64}, 80 {"georegion", "Georegion", mtList(mtStringEnum(georegionEnum))}, 81 {"country", "Country", mtStringList}, 82 {"us_state", "USState", mtStringList}, 83 {"ca_province", "CAProvince", mtStringList}, 84 {"note", "Note", mtString}, 85 {"ip_prefixes", "IPPrefixes", mtStringList}, 86 {"asn", "ASN", mtList(mtInt)}, 87 {"priority", "Priority", mtInt}, 88 {"weight", "Weight", mtFloat64}, 89 {"low_watermark", "LowWatermark", mtInt}, 90 {"high_watermark", "HighWatermark", mtInt}, 91 } 92 93 // Figure out the field indexes (in data.Meta) for all the fields. 94 // This way we can later lookup by index, which should be faster than by name. 95 96 rt := reflect.TypeOf(data.Meta{}) 97 fields := make([]MetaField, len(specs)) 98 for i, spec := range specs { 99 rf, present := rt.FieldByName(spec.NameInStruct) 100 if !present { 101 panic(fmt.Sprintf("Field %q not present", spec.NameInStruct)) 102 } 103 if len(rf.Index) != 1 { 104 panic(fmt.Sprintf("Expecting a single index, got %#v", rf.Index)) 105 } 106 index := rf.Index[0] 107 fields[i] = MetaField{ 108 MetaFieldSpec: spec, 109 StructIndex: index, 110 NameInDynamicForFeed: spec.NameInDynamic + "_feed", 111 StructGoType: rf.Type, 112 } 113 } 114 115 return fields 116 } 117 118 var metaFields []MetaField = makeMetaFields() 119 120 func makeMetaSchema() *schema.Schema { 121 fields := make(map[string]*schema.Schema) 122 123 for _, f := range metaFields { 124 fieldSchema := &schema.Schema{ 125 Optional: true, 126 ForceNew: true, 127 // TODO: Fields that arent in configuration shouldnt show up in resource data 128 // ConflictsWith: []string{f.NameInDynamicForFeed}, 129 } 130 f.SchemaBuilder(fieldSchema) 131 132 fields[f.NameInDynamic] = fieldSchema 133 134 // Add an "_feed"-suffixed field for the {"feed":...} value. 135 fields[f.NameInDynamicForFeed] = &schema.Schema{ 136 Optional: true, 137 ForceNew: true, 138 // TODO: Fields that arent in configuration shouldnt show up in resource data 139 // ConflictsWith: []string{f.NameInDynamic}, 140 Type: schema.TypeString, 141 } 142 } 143 144 metaSchemaInner := &schema.Resource{ 145 Schema: fields, 146 } 147 148 // Wrap it in a list because that seems to be the only way to have nested structs. 149 return &schema.Schema{ 150 Type: schema.TypeList, 151 Optional: true, 152 MaxItems: 1, 153 Elem: metaSchemaInner, 154 } 155 } 156 157 var metaSchema *schema.Schema = makeMetaSchema() 158 159 func metaStructToDynamic(m *data.Meta) interface{} { 160 d := make(map[string]interface{}) 161 mr := reflect.ValueOf(m).Elem() 162 for _, f := range metaFields { 163 fr := mr.Field(f.StructIndex) 164 fv := fr.Interface() 165 166 if fv == nil { 167 continue 168 } 169 170 if mapVal, isMap := fv.(map[string]interface{}); isMap { 171 if len(mapVal) == 1 { 172 if feedVal, ok := mapVal["feed"]; ok { 173 if feedStr, ok := feedVal.(string); ok { 174 d[f.NameInDynamicForFeed] = feedStr 175 continue 176 } 177 } 178 } 179 panic(fmt.Sprintf("expecting feed dict, got %+v", mapVal)) 180 } 181 182 d[f.NameInDynamic] = fv 183 } 184 return []interface{}{d} 185 } 186 187 func metaDynamicToStruct(m *data.Meta, raw interface{}) { 188 l := raw.([]interface{}) 189 if len(l) > 1 { 190 panic(fmt.Sprintf("list too long %#v", l)) 191 } 192 if len(l) == 0 { 193 return 194 } 195 if l[0] == nil { 196 return 197 } 198 199 d := l[0].(map[string]interface{}) 200 201 mr := reflect.ValueOf(m).Elem() 202 for _, f := range metaFields { 203 val, present := d[f.NameInDynamic] 204 if present { 205 fr := mr.Field(f.StructIndex) 206 fr.Set(reflect.ValueOf(val)) 207 } 208 209 feed, present := d[f.NameInDynamicForFeed] 210 if present && feed != "" { 211 if feed == nil { 212 panic("unexpected nil") 213 } 214 fr := mr.Field(f.StructIndex) 215 fr.Set(reflect.ValueOf(map[string]interface{}{"feed": feed.(string)})) 216 } 217 } 218 }