diff --git a/builtin/providers/digitalocean/resource_digitalocean_record.go b/builtin/providers/digitalocean/resource_digitalocean_record.go new file mode 100644 index 000000000..011f5875d --- /dev/null +++ b/builtin/providers/digitalocean/resource_digitalocean_record.go @@ -0,0 +1,184 @@ +package digitalocean + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/config" + "github.com/hashicorp/terraform/helper/diff" + "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) { + 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"], + } + + log.Printf("[DEBUG] record create configuration: %#v", newRecord) + + recId, err := client.CreateRecord(rs.Attributes["domain"], &newRecord) + + if err != nil { + return nil, fmt.Errorf("Failed to create record: %s", err) + } + + rs.ID = recId + log.Printf("[INFO] Record ID: %s", rs.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) +} + +func resource_digitalocean_record_update( + s *terraform.ResourceState, + d *terraform.ResourceDiff, + meta interface{}) (*terraform.ResourceState, 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 + } + + log.Printf("[DEBUG] record update configuration: %#v", updateRecord) + + err := client.UpdateRecord(rs.Attributes["domain"], rs.ID, &updateRecord) + if err != nil { + return rs, 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) +} + +func resource_digitalocean_record_destroy( + s *terraform.ResourceState, + 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) + + if err != nil { + return fmt.Errorf("Error deleting record: %s", err) + } + + return nil +} + +func resource_digitalocean_record_refresh( + s *terraform.ResourceState, + meta interface{}) (*terraform.ResourceState, error) { + p := meta.(*ResourceProvider) + client := p.client + + rec, err := resource_digitalocean_record_retrieve(s.Attributes["domain"], s.ID, client) + if err != nil { + return nil, 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() + + + // We belong to a Domain + s.Dependencies = []terraform.ResourceDependency{ + terraform.ResourceDependency{ID: s.Attributes["domain"]}, + } + + 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", + }, + } +} diff --git a/builtin/providers/digitalocean/resource_digitalocean_record_test.go b/builtin/providers/digitalocean/resource_digitalocean_record_test.go new file mode 100644 index 000000000..4b1634bbe --- /dev/null +++ b/builtin/providers/digitalocean/resource_digitalocean_record_test.go @@ -0,0 +1,175 @@ +package digitalocean + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/pearkes/digitalocean" +) + +func TestAccDigitalOceanRecord_Basic(t *testing.T) { + var record digitalocean.Record + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDigitalOceanRecordDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckDigitalOceanRecordConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckDigitalOceanRecordExists("digitalocean_record.foobar", &record), + testAccCheckDigitalOceanRecordAttributes(&record), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "name", "terraform"), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "domain", "foobar-test-terraform.com"), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "value", "192.168.0.10"), + ), + }, + }, + }) +} + +func TestAccDigitalOceanRecord_Updated(t *testing.T) { + var record digitalocean.Record + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDigitalOceanRecordDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckDigitalOceanRecordConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckDigitalOceanRecordExists("digitalocean_record.foobar", &record), + testAccCheckDigitalOceanRecordAttributes(&record), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "name", "terraform"), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "domain", "foobar-test-terraform.com"), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "value", "192.168.0.10"), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "type", "A"), + ), + }, + resource.TestStep{ + Config: testAccCheckDigitalOceanRecordConfig_new_value, + Check: resource.ComposeTestCheckFunc( + testAccCheckDigitalOceanRecordExists("digitalocean_record.foobar", &record), + testAccCheckDigitalOceanRecordAttributesUpdated(&record), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "name", "terraform"), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "domain", "foobar-test-terraform.com"), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "value", "192.168.0.11"), + resource.TestCheckResourceAttr( + "digitalocean_record.foobar", "type", "A"), + ), + }, + }, + }) +} + +func testAccCheckDigitalOceanRecordDestroy(s *terraform.State) error { + client := testAccProvider.client + + for _, rs := range s.Resources { + if rs.Type != "digitalocean_record" { + continue + } + + _, err := client.RetrieveRecord(rs.Attributes["domain"], rs.ID) + + if err == nil { + return fmt.Errorf("Record still exists") + } + } + + return nil +} + +func testAccCheckDigitalOceanRecordAttributes(record *digitalocean.Record) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if record.Data != "192.168.0.10" { + return fmt.Errorf("Bad value: %s", record.Data) + } + + return nil + } +} + +func testAccCheckDigitalOceanRecordAttributesUpdated(record *digitalocean.Record) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if record.Data != "192.168.0.11" { + return fmt.Errorf("Bad value: %s", record.Data) + } + + return nil + } +} + +func testAccCheckDigitalOceanRecordExists(n string, record *digitalocean.Record) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + client := testAccProvider.client + + foundRecord, err := client.RetrieveRecord(rs.Attributes["domain"], rs.ID) + + if err != nil { + return err + } + + if foundRecord.StringId() != rs.ID { + return fmt.Errorf("Record not found") + } + + *record = foundRecord + + return nil + } +} + +const testAccCheckDigitalOceanRecordConfig_basic = ` +resource "digitalocean_domain" "foobar" { + name = "foobar-test-terraform.com" + ip_address = "192.168.0.10" +} + +resource "digitalocean_record" "foobar" { + domain = "${digitalocean_domain.foobar.name}" + + name = "terraform" + value = "192.168.0.10" + type = "A" +}` + +const testAccCheckDigitalOceanRecordConfig_new_value = ` +resource "digitalocean_domain" "foobar" { + name = "foobar-test-terraform.com" + ip_address = "192.168.0.10" +} + +resource "digitalocean_record" "foobar" { + domain = "${digitalocean_domain.foobar.name}" + + name = "terraform" + value = "192.168.0.11" + type = "A" +}` diff --git a/builtin/providers/digitalocean/resources.go b/builtin/providers/digitalocean/resources.go index 4ca1490bd..87cdacbc0 100644 --- a/builtin/providers/digitalocean/resources.go +++ b/builtin/providers/digitalocean/resources.go @@ -27,6 +27,15 @@ 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, + }, }, } }