From 6a1a8b9487d62d46ea0cf5207872c672afc9cb0a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 9 Sep 2014 13:13:11 -0700 Subject: [PATCH 1/2] providers/digitalocean: schema framework, validation --- builtin/providers/digitalocean/provider.go | 26 +++++++++++++ .../providers/digitalocean/provider_test.go | 11 ++++++ .../digitalocean/resource_provider.go | 39 +++++++++++++++---- 3 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 builtin/providers/digitalocean/provider.go create mode 100644 builtin/providers/digitalocean/provider_test.go diff --git a/builtin/providers/digitalocean/provider.go b/builtin/providers/digitalocean/provider.go new file mode 100644 index 000000000..3f3c145ea --- /dev/null +++ b/builtin/providers/digitalocean/provider.go @@ -0,0 +1,26 @@ +package digitalocean + +import ( + "github.com/hashicorp/terraform/helper/schema" +) + +// Provider returns a schema.Provider for DigitalOcean. +// +// NOTE: schema.Provider became available long after the DO provider +// was started, so resources may not be converted to this new structure +// yet. This is a WIP. To assist with the migration, make sure any resources +// you migrate are acceptance tested, then perform the migration. +func Provider() *schema.Provider { + // TODO: Move the configuration to this, requires validation + + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "token": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + + ResourcesMap: map[string]*schema.Resource{}, + } +} diff --git a/builtin/providers/digitalocean/provider_test.go b/builtin/providers/digitalocean/provider_test.go new file mode 100644 index 000000000..b1751e54f --- /dev/null +++ b/builtin/providers/digitalocean/provider_test.go @@ -0,0 +1,11 @@ +package digitalocean + +import ( + "testing" +) + +func TestProvider(t *testing.T) { + if err := Provider().InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} diff --git a/builtin/providers/digitalocean/resource_provider.go b/builtin/providers/digitalocean/resource_provider.go index 181a5264e..a54e13e4c 100644 --- a/builtin/providers/digitalocean/resource_provider.go +++ b/builtin/providers/digitalocean/resource_provider.go @@ -4,6 +4,7 @@ import ( "log" "github.com/hashicorp/terraform/helper/config" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" "github.com/pearkes/digitalocean" ) @@ -12,20 +13,24 @@ type ResourceProvider struct { Config Config client *digitalocean.Client + + // This is the schema.Provider. Eventually this will replace much + // of this structure. For now it is an element of it for compatiblity. + p *schema.Provider } func (p *ResourceProvider) Validate(c *terraform.ResourceConfig) ([]string, []error) { - v := &config.Validator{ - Required: []string{ - "token", - }, - } - - return v.Validate(c) + prov := Provider() + return prov.Validate(c) } func (p *ResourceProvider) ValidateResource( t string, c *terraform.ResourceConfig) ([]string, []error) { + prov := Provider() + if _, ok := prov.ResourcesMap[t]; ok { + return prov.ValidateResource(t, c) + } + return resourceMap.Validate(t, c) } @@ -42,26 +47,44 @@ func (p *ResourceProvider) Configure(c *terraform.ResourceConfig) error { return err } + // Create the provider, set the meta + p.p = Provider() + p.p.SetMeta(p) + return nil } func (p *ResourceProvider) Apply( s *terraform.ResourceState, d *terraform.ResourceDiff) (*terraform.ResourceState, error) { + if _, ok := p.p.ResourcesMap[s.Type]; ok { + return p.p.Apply(s, d) + } + return resourceMap.Apply(s, d, p) } func (p *ResourceProvider) Diff( s *terraform.ResourceState, c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) { + if _, ok := p.p.ResourcesMap[s.Type]; ok { + return p.p.Diff(s, c) + } + return resourceMap.Diff(s, c, p) } func (p *ResourceProvider) Refresh( s *terraform.ResourceState) (*terraform.ResourceState, error) { + if _, ok := p.p.ResourcesMap[s.Type]; ok { + return p.p.Refresh(s) + } + return resourceMap.Refresh(s, p) } func (p *ResourceProvider) Resources() []terraform.ResourceType { - return resourceMap.Resources() + result := resourceMap.Resources() + result = append(result, Provider().Resources()...) + return result } From 0a1397b374911ddd7ab65cb59f05812132a5b871 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 9 Sep 2014 13:29:36 -0700 Subject: [PATCH 2/2] providers/digitalocean: convert record to schema --- builtin/providers/digitalocean/provider.go | 6 +- .../resource_digitalocean_record.go | 223 ++++++++---------- builtin/providers/digitalocean/resources.go | 9 - 3 files changed, 97 insertions(+), 141 deletions(-) diff --git a/builtin/providers/digitalocean/provider.go b/builtin/providers/digitalocean/provider.go index 3f3c145ea..ee1327ed8 100644 --- a/builtin/providers/digitalocean/provider.go +++ b/builtin/providers/digitalocean/provider.go @@ -11,7 +11,7 @@ import ( // yet. This is a WIP. To assist with the migration, make sure any resources // you migrate are acceptance tested, then perform the migration. func Provider() *schema.Provider { - // TODO: Move the configuration to this, requires validation + // TODO: Move the configuration to this return &schema.Provider{ Schema: map[string]*schema.Schema{ @@ -21,6 +21,8 @@ func Provider() *schema.Provider { }, }, - ResourcesMap: map[string]*schema.Resource{}, + ResourcesMap: map[string]*schema.Resource{ + "digitalocean_record": resourceRecord(), + }, } } diff --git a/builtin/providers/digitalocean/resource_digitalocean_record.go b/builtin/providers/digitalocean/resource_digitalocean_record.go index 8bf7acdfe..346390c88 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_record.go +++ b/builtin/providers/digitalocean/resource_digitalocean_record.go @@ -5,94 +5,118 @@ import ( "log" "strings" - "github.com/hashicorp/terraform/helper/config" - "github.com/hashicorp/terraform/helper/diff" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" "github.com/pearkes/digitalocean" ) -func resource_digitalocean_record_create( - s *terraform.ResourceState, - d *terraform.ResourceDiff, - meta interface{}) (*terraform.ResourceState, error) { +func resourceRecord() *schema.Resource { + return &schema.Resource{ + Create: resourceRecordCreate, + Read: resourceRecordRead, + Update: resourceRecordUpdate, + Delete: resourceRecordDelete, + + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "domain": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "port": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "priority": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "weight": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "value": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + } +} + +func resourceRecordCreate(d *schema.ResourceData, meta interface{}) error { p := meta.(*ResourceProvider) client := p.client - // Merge the diff into the state so that we have all the attributes - // properly. - rs := s.MergeDiff(d) - - var err error - newRecord := digitalocean.CreateRecord{ - Type: rs.Attributes["type"], - Name: rs.Attributes["name"], - Data: rs.Attributes["value"], - Priority: rs.Attributes["priority"], - Port: rs.Attributes["port"], - Weight: rs.Attributes["weight"], + Type: d.Get("type").(string), + Name: d.Get("name").(string), + Data: d.Get("value").(string), + Priority: d.Get("priority").(string), + Port: d.Get("port").(string), + Weight: d.Get("weight").(string), } log.Printf("[DEBUG] record create configuration: %#v", newRecord) - - recId, err := client.CreateRecord(rs.Attributes["domain"], &newRecord) - + recId, err := client.CreateRecord(d.Get("domain").(string), &newRecord) if err != nil { - return nil, fmt.Errorf("Failed to create record: %s", err) + return fmt.Errorf("Failed to create record: %s", err) } - rs.ID = recId - log.Printf("[INFO] Record ID: %s", rs.ID) + d.SetId(recId) + log.Printf("[INFO] Record ID: %s", d.Id()) - record, err := resource_digitalocean_record_retrieve(rs.Attributes["domain"], rs.ID, client) - if err != nil { - return nil, fmt.Errorf("Couldn't find record: %s", err) - } - - return resource_digitalocean_record_update_state(rs, record) + return resourceRecordRead(d, meta) } -func resource_digitalocean_record_update( - s *terraform.ResourceState, - d *terraform.ResourceDiff, - meta interface{}) (*terraform.ResourceState, error) { +func resourceRecordUpdate(d *schema.ResourceData, meta interface{}) error { p := meta.(*ResourceProvider) client := p.client - rs := s.MergeDiff(d) - updateRecord := digitalocean.UpdateRecord{} - - if attr, ok := d.Attributes["name"]; ok { - updateRecord.Name = attr.New + var updateRecord digitalocean.UpdateRecord + if v, ok := d.GetOk("name"); ok { + updateRecord.Name = v.(string) } log.Printf("[DEBUG] record update configuration: %#v", updateRecord) - - err := client.UpdateRecord(rs.Attributes["domain"], rs.ID, &updateRecord) + err := client.UpdateRecord(d.Get("domain").(string), d.Id(), &updateRecord) if err != nil { - return rs, fmt.Errorf("Failed to update record: %s", err) + return fmt.Errorf("Failed to update record: %s", err) } - record, err := resource_digitalocean_record_retrieve(rs.Attributes["domain"], rs.ID, client) - if err != nil { - return rs, fmt.Errorf("Couldn't find record: %s", err) - } - - return resource_digitalocean_record_update_state(rs, record) + return resourceRecordRead(d, meta) } -func resource_digitalocean_record_destroy( - s *terraform.ResourceState, - meta interface{}) error { +func resourceRecordDelete(d *schema.ResourceData, meta interface{}) error { p := meta.(*ResourceProvider) client := p.client - log.Printf("[INFO] Deleting record: %s, %s", s.Attributes["domain"], s.ID) - - err := client.DestroyRecord(s.Attributes["domain"], s.ID) - + log.Printf( + "[INFO] Deleting record: %s, %s", d.Get("domain").(string), d.Id()) + err := client.DestroyRecord(d.Get("domain").(string), d.Id()) if err != nil { - // If the record is somehow already destroyed, mark as // succesfully gone if strings.Contains(err.Error(), "404 Not Found") { @@ -105,87 +129,26 @@ func resource_digitalocean_record_destroy( return nil } -func resource_digitalocean_record_refresh( - s *terraform.ResourceState, - meta interface{}) (*terraform.ResourceState, error) { +func resourceRecordRead(d *schema.ResourceData, meta interface{}) error { p := meta.(*ResourceProvider) client := p.client - rec, err := resource_digitalocean_record_retrieve(s.Attributes["domain"], s.ID, client) + rec, err := client.RetrieveRecord(d.Get("domain").(string), d.Id()) if err != nil { - return nil, err + return err } - return resource_digitalocean_record_update_state(s, rec) -} - -func resource_digitalocean_record_diff( - s *terraform.ResourceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.ResourceDiff, error) { - - b := &diff.ResourceBuilder{ - Attrs: map[string]diff.AttrType{ - "domain": diff.AttrTypeCreate, - "name": diff.AttrTypeUpdate, - "type": diff.AttrTypeCreate, - "value": diff.AttrTypeCreate, - "priority": diff.AttrTypeCreate, - "port": diff.AttrTypeCreate, - "weight": diff.AttrTypeCreate, - }, - - ComputedAttrs: []string{ - "value", - "priority", - "weight", - "port", - }, - } - - return b.Diff(s, c) -} - -func resource_digitalocean_record_update_state( - s *terraform.ResourceState, - rec *digitalocean.Record) (*terraform.ResourceState, error) { - - s.Attributes["name"] = rec.Name - s.Attributes["type"] = rec.Type - s.Attributes["value"] = rec.Data - s.Attributes["weight"] = rec.StringWeight() - s.Attributes["priority"] = rec.StringPriority() - s.Attributes["port"] = rec.StringPort() + d.Set("name", rec.Name) + d.Set("type", rec.Type) + d.Set("value", rec.Data) + d.Set("weight", rec.StringWeight()) + d.Set("priority", rec.StringPriority()) + d.Set("port", rec.StringPort()) // We belong to a Domain - s.Dependencies = []terraform.ResourceDependency{ - terraform.ResourceDependency{ID: s.Attributes["domain"]}, - } + d.SetDependencies([]terraform.ResourceDependency{ + terraform.ResourceDependency{ID: d.Get("domain").(string)}, + }) - return s, nil -} - -func resource_digitalocean_record_retrieve(domain string, id string, client *digitalocean.Client) (*digitalocean.Record, error) { - record, err := client.RetrieveRecord(domain, id) - if err != nil { - return nil, err - } - - return &record, nil -} - -func resource_digitalocean_record_validation() *config.Validator { - return &config.Validator{ - Required: []string{ - "type", - "domain", - }, - Optional: []string{ - "value", - "name", - "weight", - "port", - "priority", - }, - } + return nil } diff --git a/builtin/providers/digitalocean/resources.go b/builtin/providers/digitalocean/resources.go index 87cdacbc0..4ca1490bd 100644 --- a/builtin/providers/digitalocean/resources.go +++ b/builtin/providers/digitalocean/resources.go @@ -27,15 +27,6 @@ func init() { Refresh: resource_digitalocean_droplet_refresh, Update: resource_digitalocean_droplet_update, }, - - "digitalocean_record": resource.Resource{ - ConfigValidator: resource_digitalocean_record_validation(), - Create: resource_digitalocean_record_create, - Destroy: resource_digitalocean_record_destroy, - Update: resource_digitalocean_record_update, - Diff: resource_digitalocean_record_diff, - Refresh: resource_digitalocean_record_refresh, - }, }, } }