terraform/builtin/providers/ns1/meta.go

219 lines
5.2 KiB
Go
Raw Normal View History

Ns1 provider (#10782) * vendor: update gopkg.in/ns1/ns1-go.v2 * provider/ns1: Port the ns1 provider to Terraform core * docs/ns1: Document the ns1 provider * ns1: rename remaining nsone -> ns1 (#10805) * Ns1 provider (#11300) * provider/ns1: Flesh out support for meta structs. Following the structure outlined by @pashap. Using reflection to reduce copy/paste. Putting metas inside single-item lists. This is clunky, but I couldn't figure out how else to have a nested struct. Maybe the Terraform people know a better way? Inside the meta struct, all fields are always written to the state; I can't figure out how to omit fields that aren't used. This is not just verbose, it actually causes issues because you can't have both "up" and "up_feed" set). Also some minor other changes: - Add "terraform" import support to records and zones. - Create helper class StringEnum. * provider/ns1: Make fmt * provider/ns1: Remove stubbed out RecordRead (used for testing metadata change). * provider/ns1: Need to get interface that m contains from Ptr Value with Elem() * provider/ns1: Use empty string to indicate no feed given. * provider/ns1: Remove old record.regions fields. * provider/ns1: Removes redundant testAccCheckRecordState * provider/ns1: Moves account permissions logic to permissions.go * provider/ns1: Adds tests for team resource. * provider/ns1: Move remaining permissions logic to permissions.go * ns1/provider: Adds datasource.config * provider/ns1: Small clean up of datafeed resource tests * provider/ns1: removes testAccCheckZoneState in favor of explicit name check * provider/ns1: More renaming of nsone -> ns1 * provider/ns1: Comment out metadata for the moment. * Ns1 provider (#11347) * Fix the removal of empty containers from a flatmap Removal of empty nested containers from a flatmap would sometimes fail a sanity check when removed in the wrong order. This would only fail sometimes due to map iteration. There was also an off-by-one error in the prefix check which could match the incorrect keys. * provider/ns1: Adds ns1 go client through govendor. * provider/ns1: Removes unused debug line * docs/ns1: Adds docs around apikey/datasource/datafeed/team/user/record. * provider/ns1: Gets go vet green
2017-01-23 22:41:07 +01:00
package ns1
import (
"fmt"
"reflect"
"github.com/hashicorp/terraform/helper/schema"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
)
type TfSchemaBuilder func(*schema.Schema)
func mtSimple(t schema.ValueType) TfSchemaBuilder {
return func(s *schema.Schema) {
s.Type = t
}
}
func mtStringEnum(se *StringEnum) TfSchemaBuilder {
return func(s *schema.Schema) {
s.Type = schema.TypeString
s.ValidateFunc = func(v interface{}, k string) ([]string, []error) {
_, err := se.Check(v.(string))
if err != nil {
return nil, []error{err}
}
return nil, nil
}
}
}
var mtInt TfSchemaBuilder = mtSimple(schema.TypeInt)
var mtBool TfSchemaBuilder = mtSimple(schema.TypeBool)
var mtString TfSchemaBuilder = mtSimple(schema.TypeString)
var mtFloat64 TfSchemaBuilder = mtSimple(schema.TypeFloat)
func mtList(elementSchemaBuilder TfSchemaBuilder) TfSchemaBuilder {
return func(s *schema.Schema) {
s.Type = schema.TypeList
elementSchema := &schema.Schema{}
elementSchemaBuilder(elementSchema)
s.Elem = elementSchema
}
}
var mtStringList TfSchemaBuilder = mtList(mtString)
type MetaFieldSpec struct {
NameInDynamic string
NameInStruct string
SchemaBuilder TfSchemaBuilder
}
type MetaField struct {
MetaFieldSpec
NameInDynamicForFeed string
StructIndex int
StructGoType reflect.Type
}
var georegionEnum *StringEnum = NewStringEnum([]string{
"US-WEST",
"US-EAST",
"US-CENTRAL",
"EUROPE",
"AFRICA",
"ASIAPAC",
"SOUTH-AMERICA",
})
func makeMetaFields() []MetaField {
var specs []MetaFieldSpec = []MetaFieldSpec{
{"up", "Up", mtBool},
{"connections", "Connections", mtInt},
{"requests", "Requests", mtInt},
{"loadavg", "LoadAvg", mtFloat64},
{"pulsar", "Pulsar", mtInt},
{"latitude", "Latitude", mtFloat64},
{"longitude", "Longitude", mtFloat64},
{"georegion", "Georegion", mtList(mtStringEnum(georegionEnum))},
{"country", "Country", mtStringList},
{"us_state", "USState", mtStringList},
{"ca_province", "CAProvince", mtStringList},
{"note", "Note", mtString},
{"ip_prefixes", "IPPrefixes", mtStringList},
{"asn", "ASN", mtList(mtInt)},
{"priority", "Priority", mtInt},
{"weight", "Weight", mtFloat64},
{"low_watermark", "LowWatermark", mtInt},
{"high_watermark", "HighWatermark", mtInt},
}
// Figure out the field indexes (in data.Meta) for all the fields.
// This way we can later lookup by index, which should be faster than by name.
rt := reflect.TypeOf(data.Meta{})
fields := make([]MetaField, len(specs))
for i, spec := range specs {
rf, present := rt.FieldByName(spec.NameInStruct)
if !present {
panic(fmt.Sprintf("Field %q not present", spec.NameInStruct))
}
if len(rf.Index) != 1 {
panic(fmt.Sprintf("Expecting a single index, got %#v", rf.Index))
}
index := rf.Index[0]
fields[i] = MetaField{
MetaFieldSpec: spec,
StructIndex: index,
NameInDynamicForFeed: spec.NameInDynamic + "_feed",
StructGoType: rf.Type,
}
}
return fields
}
var metaFields []MetaField = makeMetaFields()
func makeMetaSchema() *schema.Schema {
fields := make(map[string]*schema.Schema)
for _, f := range metaFields {
fieldSchema := &schema.Schema{
Optional: true,
ForceNew: true,
// TODO: Fields that arent in configuration shouldnt show up in resource data
// ConflictsWith: []string{f.NameInDynamicForFeed},
}
f.SchemaBuilder(fieldSchema)
fields[f.NameInDynamic] = fieldSchema
// Add an "_feed"-suffixed field for the {"feed":...} value.
fields[f.NameInDynamicForFeed] = &schema.Schema{
Optional: true,
ForceNew: true,
// TODO: Fields that arent in configuration shouldnt show up in resource data
// ConflictsWith: []string{f.NameInDynamic},
Type: schema.TypeString,
}
}
metaSchemaInner := &schema.Resource{
Schema: fields,
}
// Wrap it in a list because that seems to be the only way to have nested structs.
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: metaSchemaInner,
}
}
var metaSchema *schema.Schema = makeMetaSchema()
func metaStructToDynamic(m *data.Meta) interface{} {
d := make(map[string]interface{})
mr := reflect.ValueOf(m).Elem()
for _, f := range metaFields {
fr := mr.Field(f.StructIndex)
fv := fr.Interface()
if fv == nil {
continue
}
if mapVal, isMap := fv.(map[string]interface{}); isMap {
if len(mapVal) == 1 {
if feedVal, ok := mapVal["feed"]; ok {
if feedStr, ok := feedVal.(string); ok {
d[f.NameInDynamicForFeed] = feedStr
continue
}
}
}
panic(fmt.Sprintf("expecting feed dict, got %+v", mapVal))
}
d[f.NameInDynamic] = fv
}
return []interface{}{d}
}
func metaDynamicToStruct(m *data.Meta, raw interface{}) {
l := raw.([]interface{})
if len(l) > 1 {
panic(fmt.Sprintf("list too long %#v", l))
}
if len(l) == 0 {
return
}
if l[0] == nil {
return
}
d := l[0].(map[string]interface{})
mr := reflect.ValueOf(m).Elem()
for _, f := range metaFields {
val, present := d[f.NameInDynamic]
if present {
fr := mr.Field(f.StructIndex)
fr.Set(reflect.ValueOf(val))
}
feed, present := d[f.NameInDynamicForFeed]
if present && feed != "" {
if feed == nil {
panic("unexpected nil")
}
fr := mr.Field(f.StructIndex)
fr.Set(reflect.ValueOf(map[string]interface{}{"feed": feed.(string)}))
}
}
}