Merge pull request #570 from svanharmelen/f-refactor-digitalocean-provider
Refactor to use the schema.Provider approach
This commit is contained in:
commit
fc5a13a1c1
|
@ -3,13 +3,10 @@ package main
|
|||
import (
|
||||
"github.com/hashicorp/terraform/builtin/providers/digitalocean"
|
||||
"github.com/hashicorp/terraform/plugin"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func main() {
|
||||
plugin.Serve(&plugin.ServeOpts{
|
||||
ProviderFunc: func() terraform.ResourceProvider {
|
||||
return new(digitalocean.ResourceProvider)
|
||||
},
|
||||
ProviderFunc: digitalocean.Provider,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,26 +2,16 @@ package digitalocean
|
|||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/pearkes/digitalocean"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Token string `mapstructure:"token"`
|
||||
Token string
|
||||
}
|
||||
|
||||
// Client() returns a new client for accessing digital
|
||||
// ocean.
|
||||
//
|
||||
// Client() returns a new client for accessing digital ocean.
|
||||
func (c *Config) Client() (*digitalocean.Client, error) {
|
||||
|
||||
// If we have env vars set (like in the acc) tests,
|
||||
// we need to override the values passed in here.
|
||||
if v := os.Getenv("DIGITALOCEAN_TOKEN"); v != "" {
|
||||
c.Token = v
|
||||
}
|
||||
|
||||
client, err := digitalocean.NewClient(c.Token)
|
||||
|
||||
log.Printf("[INFO] DigitalOcean Client configured for URL: %s", client.URL)
|
||||
|
|
|
@ -1,29 +1,48 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
func Provider() terraform.ResourceProvider {
|
||||
return &schema.Provider{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"token": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: envDefaultFunc("DIGITALOCEAN_TOKEN"),
|
||||
Description: "The token key for API operations.",
|
||||
},
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"digitalocean_domain": resourceDomain(),
|
||||
"digitalocean_record": resourceRecord(),
|
||||
"digitalocean_domain": resourceDigitalOceanDomain(),
|
||||
"digitalocean_droplet": resourceDigitalOceanDroplet(),
|
||||
"digitalocean_record": resourceDigitalOceanRecord(),
|
||||
},
|
||||
|
||||
ConfigureFunc: providerConfigure,
|
||||
}
|
||||
}
|
||||
|
||||
func envDefaultFunc(k string) schema.SchemaDefaultFunc {
|
||||
return func() (interface{}, error) {
|
||||
if v := os.Getenv(k); v != "" {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||
config := Config{
|
||||
Token: d.Get("token").(string),
|
||||
}
|
||||
|
||||
return config.Client()
|
||||
}
|
||||
|
|
|
@ -1,11 +1,35 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccProviders map[string]terraform.ResourceProvider
|
||||
var testAccProvider *schema.Provider
|
||||
|
||||
func init() {
|
||||
testAccProvider = Provider().(*schema.Provider)
|
||||
testAccProviders = map[string]terraform.ResourceProvider{
|
||||
"digitalocean": testAccProvider,
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider(t *testing.T) {
|
||||
if err := Provider().InternalValidate(); err != nil {
|
||||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider_impl(t *testing.T) {
|
||||
var _ terraform.ResourceProvider = Provider()
|
||||
}
|
||||
|
||||
func testAccPreCheck(t *testing.T) {
|
||||
if v := os.Getenv("DIGITALOCEAN_TOKEN"); v == "" {
|
||||
t.Fatal("DIGITALOCEAN_TOKEN must be set for acceptance tests")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ import (
|
|||
"github.com/pearkes/digitalocean"
|
||||
)
|
||||
|
||||
func resourceDomain() *schema.Resource {
|
||||
func resourceDigitalOceanDomain() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceDomainCreate,
|
||||
Read: resourceDomainRead,
|
||||
Delete: resourceDomainDelete,
|
||||
Create: resourceDigitalOceanDomainCreate,
|
||||
Read: resourceDigitalOceanDomainRead,
|
||||
Delete: resourceDigitalOceanDomainDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
|
@ -31,18 +31,17 @@ func resourceDomain() *schema.Resource {
|
|||
}
|
||||
}
|
||||
|
||||
func resourceDomainCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
func resourceDigitalOceanDomainCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
// Build up our creation options
|
||||
opts := digitalocean.CreateDomain{
|
||||
opts := &digitalocean.CreateDomain{
|
||||
Name: d.Get("name").(string),
|
||||
IPAddress: d.Get("ip_address").(string),
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Domain create configuration: %#v", opts)
|
||||
name, err := client.CreateDomain(&opts)
|
||||
name, err := client.CreateDomain(opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Domain: %s", err)
|
||||
}
|
||||
|
@ -50,26 +49,11 @@ func resourceDomainCreate(d *schema.ResourceData, meta interface{}) error {
|
|||
d.SetId(name)
|
||||
log.Printf("[INFO] Domain Name: %s", name)
|
||||
|
||||
return nil
|
||||
return resourceDigitalOceanDomainRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDomainDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
|
||||
log.Printf("[INFO] Deleting Domain: %s", d.Id())
|
||||
err := client.DestroyDomain(d.Id())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting Domain: %s", err)
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceDomainRead(d *schema.ResourceData, meta interface{}) error {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
func resourceDigitalOceanDomainRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
domain, err := client.RetrieveDomain(d.Id())
|
||||
if err != nil {
|
||||
|
@ -87,3 +71,16 @@ func resourceDomainRead(d *schema.ResourceData, meta interface{}) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceDigitalOceanDomainDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
log.Printf("[INFO] Deleting Domain: %s", d.Id())
|
||||
err := client.DestroyDomain(d.Id())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting Domain: %s", err)
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ func TestAccDigitalOceanDomain_Basic(t *testing.T) {
|
|||
}
|
||||
|
||||
func testAccCheckDigitalOceanDomainDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.client
|
||||
client := testAccProvider.Meta().(*digitalocean.Client)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "digitalocean_domain" {
|
||||
|
@ -74,7 +74,7 @@ func testAccCheckDigitalOceanDomainExists(n string, domain *digitalocean.Domain)
|
|||
return fmt.Errorf("No Record ID is set")
|
||||
}
|
||||
|
||||
client := testAccProvider.client
|
||||
client := testAccProvider.Meta().(*digitalocean.Client)
|
||||
|
||||
foundDomain, err := client.RetrieveDomain(rs.Primary.ID)
|
||||
|
||||
|
|
|
@ -6,202 +6,335 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/flatmap"
|
||||
"github.com/hashicorp/terraform/helper/config"
|
||||
"github.com/hashicorp/terraform/helper/diff"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/pearkes/digitalocean"
|
||||
)
|
||||
|
||||
func resource_digitalocean_droplet_create(
|
||||
s *terraform.InstanceState,
|
||||
d *terraform.InstanceDiff,
|
||||
meta interface{}) (*terraform.InstanceState, error) {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
func resourceDigitalOceanDroplet() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceDigitalOceanDropletCreate,
|
||||
Read: resourceDigitalOceanDropletRead,
|
||||
Update: resourceDigitalOceanDropletUpdate,
|
||||
Delete: resourceDigitalOceanDropletDelete,
|
||||
|
||||
// Merge the diff into the state so that we have all the attributes
|
||||
// properly.
|
||||
rs := s.MergeDiff(d)
|
||||
Schema: map[string]*schema.Schema{
|
||||
"image": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"region": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"size": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"status": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"locked": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"backups": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"ipv6": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"ipv6_address": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"ipv6_address_private": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"private_networking": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"ipv4_address": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"ipv4_address_private": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"ssh_keys": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
|
||||
"user_data": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDigitalOceanDropletCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
// Build up our creation options
|
||||
opts := digitalocean.CreateDroplet{
|
||||
Backups: rs.Attributes["backups"],
|
||||
Image: rs.Attributes["image"],
|
||||
IPV6: rs.Attributes["ipv6"],
|
||||
Name: rs.Attributes["name"],
|
||||
PrivateNetworking: rs.Attributes["private_networking"],
|
||||
Region: rs.Attributes["region"],
|
||||
Size: rs.Attributes["size"],
|
||||
UserData: rs.Attributes["user_data"],
|
||||
opts := &digitalocean.CreateDroplet{
|
||||
Image: d.Get("image").(string),
|
||||
Name: d.Get("name").(string),
|
||||
Region: d.Get("region").(string),
|
||||
Size: d.Get("size").(string),
|
||||
}
|
||||
|
||||
// Only expand ssh_keys if we have them
|
||||
if _, ok := rs.Attributes["ssh_keys.#"]; ok {
|
||||
v := flatmap.Expand(rs.Attributes, "ssh_keys").([]interface{})
|
||||
if len(v) > 0 {
|
||||
vs := make([]string, 0, len(v))
|
||||
|
||||
// here we special case the * expanded lists. For example:
|
||||
//
|
||||
// ssh_keys = ["${digitalocean_key.foo.*.id}"]
|
||||
//
|
||||
if len(v) == 1 && strings.Contains(v[0].(string), ",") {
|
||||
vs = strings.Split(v[0].(string), ",")
|
||||
if attr, ok := d.GetOk("backups"); ok {
|
||||
opts.Backups = attr.(string)
|
||||
}
|
||||
|
||||
for _, v := range v {
|
||||
vs = append(vs, v.(string))
|
||||
if attr, ok := d.GetOk("ipv6"); ok && attr.(bool) {
|
||||
opts.IPV6 = "true"
|
||||
}
|
||||
|
||||
opts.SSHKeys = vs
|
||||
if attr, ok := d.GetOk("private_networking"); ok && attr.(bool) {
|
||||
opts.PrivateNetworking = "true"
|
||||
}
|
||||
|
||||
if attr, ok := d.GetOk("user_data"); ok {
|
||||
opts.UserData = attr.(string)
|
||||
}
|
||||
|
||||
// Get configured ssh_keys
|
||||
ssh_keys := d.Get("ssh_keys.#").(int)
|
||||
if ssh_keys > 0 {
|
||||
opts.SSHKeys = make([]string, 0, ssh_keys)
|
||||
for i := 0; i < ssh_keys; i++ {
|
||||
key := fmt.Sprintf("ssh_keys.%d", i)
|
||||
opts.SSHKeys = append(opts.SSHKeys, d.Get(key).(string))
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Droplet create configuration: %#v", opts)
|
||||
|
||||
id, err := client.CreateDroplet(&opts)
|
||||
id, err := client.CreateDroplet(opts)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating Droplet: %s", err)
|
||||
return fmt.Errorf("Error creating droplet: %s", err)
|
||||
}
|
||||
|
||||
// Assign the droplets id
|
||||
rs.ID = id
|
||||
d.SetId(id)
|
||||
|
||||
log.Printf("[INFO] Droplet ID: %s", id)
|
||||
log.Printf("[INFO] Droplet ID: %s", d.Id())
|
||||
|
||||
dropletRaw, err := WaitForDropletAttribute(id, "active", []string{"new"}, "status", client)
|
||||
_, err = WaitForDropletAttribute(d, "active", []string{"new"}, "status", meta)
|
||||
|
||||
if err != nil {
|
||||
return rs, fmt.Errorf(
|
||||
"Error waiting for droplet (%s) to become ready: %s",
|
||||
id, err)
|
||||
return fmt.Errorf(
|
||||
"Error waiting for droplet (%s) to become ready: %s", d.Id(), err)
|
||||
}
|
||||
|
||||
droplet := dropletRaw.(*digitalocean.Droplet)
|
||||
return resourceDigitalOceanDropletRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDigitalOceanDropletRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
// Retrieve the droplet properties for updating the state
|
||||
droplet, err := client.RetrieveDroplet(d.Id())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error retrieving droplet: %s", err)
|
||||
}
|
||||
|
||||
if droplet.ImageSlug() == "" && droplet.ImageId() != "" {
|
||||
d.Set("image", droplet.ImageId())
|
||||
} else {
|
||||
d.Set("image", droplet.ImageSlug())
|
||||
}
|
||||
|
||||
d.Set("name", droplet.Name)
|
||||
d.Set("region", droplet.RegionSlug())
|
||||
d.Set("size", droplet.SizeSlug)
|
||||
d.Set("status", droplet.Status)
|
||||
d.Set("locked", droplet.IsLocked())
|
||||
|
||||
if droplet.IPV6Address("public") != "" {
|
||||
d.Set("ipv6", true)
|
||||
d.Set("ipv6_address", droplet.IPV6Address("public"))
|
||||
d.Set("ipv6_address_private", droplet.IPV6Address("private"))
|
||||
}
|
||||
|
||||
d.Set("ipv4_address", droplet.IPV4Address("public"))
|
||||
|
||||
if droplet.NetworkingType() == "private" {
|
||||
d.Set("private_networking", true)
|
||||
d.Set("ipv4_address_private", droplet.IPV4Address("private"))
|
||||
}
|
||||
|
||||
// Initialize the connection info
|
||||
rs.Ephemeral.ConnInfo["type"] = "ssh"
|
||||
rs.Ephemeral.ConnInfo["host"] = droplet.IPV4Address("public")
|
||||
d.SetConnInfo(map[string]string{
|
||||
"type": "ssh",
|
||||
"host": droplet.IPV4Address("public"),
|
||||
})
|
||||
|
||||
return resource_digitalocean_droplet_update_state(rs, droplet)
|
||||
return nil
|
||||
}
|
||||
|
||||
func resource_digitalocean_droplet_update(
|
||||
s *terraform.InstanceState,
|
||||
d *terraform.InstanceDiff,
|
||||
meta interface{}) (*terraform.InstanceState, error) {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
rs := s.MergeDiff(d)
|
||||
func resourceDigitalOceanDropletUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
var err error
|
||||
if d.HasChange("size") {
|
||||
oldSize, newSize := d.GetChange("size")
|
||||
|
||||
if attr, ok := d.Attributes["size"]; ok {
|
||||
err = client.PowerOff(rs.ID)
|
||||
err := client.PowerOff(d.Id())
|
||||
|
||||
if err != nil && !strings.Contains(err.Error(), "Droplet is already powered off") {
|
||||
return s, err
|
||||
return fmt.Errorf(
|
||||
"Error powering off droplet (%s): %s", d.Id(), err)
|
||||
}
|
||||
|
||||
// Wait for power off
|
||||
_, err = WaitForDropletAttribute(
|
||||
rs.ID, "off", []string{"active"}, "status", client)
|
||||
|
||||
err = client.Resize(rs.ID, attr.New)
|
||||
_, err = WaitForDropletAttribute(d, "off", []string{"active"}, "status", client)
|
||||
|
||||
if err != nil {
|
||||
newErr := power_on_and_wait(rs.ID, client)
|
||||
if newErr != nil {
|
||||
return rs, newErr
|
||||
return fmt.Errorf(
|
||||
"Error waiting for droplet (%s) to become powered off: %s", d.Id(), err)
|
||||
}
|
||||
return rs, err
|
||||
|
||||
// Resize the droplet
|
||||
err = client.Resize(d.Id(), newSize.(string))
|
||||
|
||||
if err != nil {
|
||||
newErr := power_on_and_wait(d, meta)
|
||||
if newErr != nil {
|
||||
return fmt.Errorf(
|
||||
"Error powering on droplet (%s) after failed resize: %s", d.Id(), err)
|
||||
}
|
||||
return fmt.Errorf(
|
||||
"Error resizing droplet (%s): %s", d.Id(), err)
|
||||
}
|
||||
|
||||
// Wait for the size to change
|
||||
_, err = WaitForDropletAttribute(
|
||||
rs.ID, attr.New, []string{"", attr.Old}, "size", client)
|
||||
d, newSize.(string), []string{"", oldSize.(string)}, "size", meta)
|
||||
|
||||
if err != nil {
|
||||
newErr := power_on_and_wait(rs.ID, client)
|
||||
newErr := power_on_and_wait(d, meta)
|
||||
if newErr != nil {
|
||||
return rs, newErr
|
||||
return fmt.Errorf(
|
||||
"Error powering on droplet (%s) after waiting for resize to finish: %s", d.Id(), err)
|
||||
}
|
||||
return s, err
|
||||
return fmt.Errorf(
|
||||
"Error waiting for resize droplet (%s) to finish: %s", d.Id(), err)
|
||||
}
|
||||
|
||||
err = client.PowerOn(rs.ID)
|
||||
err = client.PowerOn(d.Id())
|
||||
|
||||
if err != nil {
|
||||
return s, err
|
||||
return fmt.Errorf(
|
||||
"Error powering on droplet (%s) after resize: %s", d.Id(), err)
|
||||
}
|
||||
|
||||
// Wait for power off
|
||||
_, err = WaitForDropletAttribute(
|
||||
rs.ID, "active", []string{"off"}, "status", client)
|
||||
_, err = WaitForDropletAttribute(d, "active", []string{"off"}, "status", meta)
|
||||
|
||||
if err != nil {
|
||||
return s, err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if attr, ok := d.Attributes["name"]; ok {
|
||||
err = client.Rename(rs.ID, attr.New)
|
||||
if d.HasChange("name") {
|
||||
oldName, newName := d.GetChange("name")
|
||||
|
||||
// Rename the droplet
|
||||
err := client.Rename(d.Id(), newName.(string))
|
||||
|
||||
if err != nil {
|
||||
return s, err
|
||||
return fmt.Errorf(
|
||||
"Error renaming droplet (%s): %s", d.Id(), err)
|
||||
}
|
||||
|
||||
// Wait for the name to change
|
||||
_, err = WaitForDropletAttribute(
|
||||
rs.ID, attr.New, []string{"", attr.Old}, "name", client)
|
||||
}
|
||||
|
||||
if attr, ok := d.Attributes["private_networking"]; ok {
|
||||
err = client.Rename(rs.ID, attr.New)
|
||||
d, newName.(string), []string{"", oldName.(string)}, "name", meta)
|
||||
|
||||
if err != nil {
|
||||
return s, err
|
||||
return fmt.Errorf(
|
||||
"Error waiting for rename droplet (%s) to finish: %s", d.Id(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the private_networking to turn on/off
|
||||
// As there is no way to disable private networking,
|
||||
// we only check if it needs to be enabled
|
||||
if d.HasChange("private_networking") && d.Get("private_networking").(bool) {
|
||||
err := client.EnablePrivateNetworking(d.Id())
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Error enabling private networking for droplet (%s): %s", d.Id(), err)
|
||||
}
|
||||
|
||||
// Wait for the private_networking to turn on
|
||||
_, err = WaitForDropletAttribute(
|
||||
rs.ID, attr.New, []string{"", attr.Old}, "private_networking", client)
|
||||
d, "true", []string{"", "false"}, "private_networking", meta)
|
||||
|
||||
return fmt.Errorf(
|
||||
"Error waiting for private networking to be enabled on for droplet (%s): %s", d.Id(), err)
|
||||
}
|
||||
|
||||
if attr, ok := d.Attributes["ipv6"]; ok {
|
||||
err = client.Rename(rs.ID, attr.New)
|
||||
// As there is no way to disable IPv6, we only check if it needs to be enabled
|
||||
if d.HasChange("ipv6") && d.Get("ipv6").(bool) {
|
||||
err := client.EnableIPV6s(d.Id())
|
||||
|
||||
if err != nil {
|
||||
return s, err
|
||||
return fmt.Errorf(
|
||||
"Error turning on ipv6 for droplet (%s): %s", d.Id(), err)
|
||||
}
|
||||
|
||||
// Wait for ipv6 to turn on/off
|
||||
// Wait for ipv6 to turn on
|
||||
_, err = WaitForDropletAttribute(
|
||||
rs.ID, attr.New, []string{"", attr.Old}, "ipv6", client)
|
||||
}
|
||||
|
||||
droplet, err := resource_digitalocean_droplet_retrieve(rs.ID, client)
|
||||
d, "true", []string{"", "false"}, "ipv6", meta)
|
||||
|
||||
if err != nil {
|
||||
return s, err
|
||||
return fmt.Errorf(
|
||||
"Error waiting for ipv6 to be turned on for droplet (%s): %s", d.Id(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return resource_digitalocean_droplet_update_state(rs, droplet)
|
||||
return resourceDigitalOceanDropletRead(d, meta)
|
||||
}
|
||||
|
||||
func resource_digitalocean_droplet_destroy(
|
||||
s *terraform.InstanceState,
|
||||
meta interface{}) error {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
func resourceDigitalOceanDropletDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
log.Printf("[INFO] Deleting Droplet: %s", s.ID)
|
||||
log.Printf("[INFO] Deleting droplet: %s", d.Id())
|
||||
|
||||
// Destroy the droplet
|
||||
err := client.DestroyDroplet(s.ID)
|
||||
err := client.DestroyDroplet(d.Id())
|
||||
|
||||
// Handle remotely destroyed droplets
|
||||
if err != nil && strings.Contains(err.Error(), "404 Not Found") {
|
||||
|
@ -209,140 +342,24 @@ func resource_digitalocean_droplet_destroy(
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting Droplet: %s", err)
|
||||
return fmt.Errorf("Error deleting droplet: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resource_digitalocean_droplet_refresh(
|
||||
s *terraform.InstanceState,
|
||||
meta interface{}) (*terraform.InstanceState, error) {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
|
||||
droplet, err := resource_digitalocean_droplet_retrieve(s.ID, client)
|
||||
|
||||
// Handle remotely destroyed droplets
|
||||
if err != nil && strings.Contains(err.Error(), "404 Not Found") {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resource_digitalocean_droplet_update_state(s, droplet)
|
||||
}
|
||||
|
||||
func resource_digitalocean_droplet_diff(
|
||||
s *terraform.InstanceState,
|
||||
c *terraform.ResourceConfig,
|
||||
meta interface{}) (*terraform.InstanceDiff, error) {
|
||||
|
||||
b := &diff.ResourceBuilder{
|
||||
Attrs: map[string]diff.AttrType{
|
||||
"backups": diff.AttrTypeUpdate,
|
||||
"image": diff.AttrTypeCreate,
|
||||
"ipv6": diff.AttrTypeUpdate,
|
||||
"name": diff.AttrTypeUpdate,
|
||||
"private_networking": diff.AttrTypeUpdate,
|
||||
"region": diff.AttrTypeCreate,
|
||||
"size": diff.AttrTypeUpdate,
|
||||
"ssh_keys": diff.AttrTypeCreate,
|
||||
"user_data": diff.AttrTypeCreate,
|
||||
},
|
||||
|
||||
ComputedAttrs: []string{
|
||||
"backups",
|
||||
"ipv4_address",
|
||||
"ipv4_address_private",
|
||||
"ipv6",
|
||||
"ipv6_address",
|
||||
"ipv6_address_private",
|
||||
"locked",
|
||||
"private_networking",
|
||||
"status",
|
||||
},
|
||||
}
|
||||
|
||||
return b.Diff(s, c)
|
||||
}
|
||||
|
||||
func resource_digitalocean_droplet_update_state(
|
||||
s *terraform.InstanceState,
|
||||
droplet *digitalocean.Droplet) (*terraform.InstanceState, error) {
|
||||
|
||||
s.Attributes["name"] = droplet.Name
|
||||
s.Attributes["region"] = droplet.RegionSlug()
|
||||
|
||||
if droplet.ImageSlug() == "" && droplet.ImageId() != "" {
|
||||
s.Attributes["image"] = droplet.ImageId()
|
||||
} else {
|
||||
s.Attributes["image"] = droplet.ImageSlug()
|
||||
}
|
||||
|
||||
if droplet.IPV6Address("public") != "" {
|
||||
s.Attributes["ipv6"] = "true"
|
||||
s.Attributes["ipv6_address"] = droplet.IPV6Address("public")
|
||||
s.Attributes["ipv6_address_private"] = droplet.IPV6Address("private")
|
||||
}
|
||||
|
||||
s.Attributes["ipv4_address"] = droplet.IPV4Address("public")
|
||||
s.Attributes["locked"] = droplet.IsLocked()
|
||||
|
||||
if droplet.NetworkingType() == "private" {
|
||||
s.Attributes["private_networking"] = "true"
|
||||
s.Attributes["ipv4_address_private"] = droplet.IPV4Address("private")
|
||||
}
|
||||
|
||||
s.Attributes["size"] = droplet.SizeSlug
|
||||
s.Attributes["status"] = droplet.Status
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// retrieves an ELB by its ID
|
||||
func resource_digitalocean_droplet_retrieve(id string, client *digitalocean.Client) (*digitalocean.Droplet, error) {
|
||||
// Retrieve the ELB properties for updating the state
|
||||
droplet, err := client.RetrieveDroplet(id)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error retrieving droplet: %s", err)
|
||||
}
|
||||
|
||||
return &droplet, nil
|
||||
}
|
||||
|
||||
func resource_digitalocean_droplet_validation() *config.Validator {
|
||||
return &config.Validator{
|
||||
Required: []string{
|
||||
"image",
|
||||
"name",
|
||||
"region",
|
||||
"size",
|
||||
},
|
||||
Optional: []string{
|
||||
"backups",
|
||||
"user_data",
|
||||
"ipv6",
|
||||
"private_networking",
|
||||
"ssh_keys.*",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func WaitForDropletAttribute(id string, target string, pending []string, attribute string, client *digitalocean.Client) (interface{}, error) {
|
||||
func WaitForDropletAttribute(
|
||||
d *schema.ResourceData, target string, pending []string, attribute string, meta interface{}) (interface{}, error) {
|
||||
// Wait for the droplet so we can get the networking attributes
|
||||
// that show up after a while
|
||||
log.Printf(
|
||||
"[INFO] Waiting for Droplet (%s) to have %s of %s",
|
||||
id, attribute, target)
|
||||
d.Id(), attribute, target)
|
||||
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: pending,
|
||||
Target: target,
|
||||
Refresh: new_droplet_state_refresh_func(id, attribute, client),
|
||||
Refresh: new_droplet_state_refresh_func(d, attribute, meta),
|
||||
Timeout: 10 * time.Minute,
|
||||
Delay: 10 * time.Second,
|
||||
MinTimeout: 3 * time.Second,
|
||||
|
@ -351,37 +368,36 @@ func WaitForDropletAttribute(id string, target string, pending []string, attribu
|
|||
return stateConf.WaitForState()
|
||||
}
|
||||
|
||||
func new_droplet_state_refresh_func(id string, attribute string, client *digitalocean.Client) resource.StateRefreshFunc {
|
||||
// TODO This function still needs a little more refactoring to make it
|
||||
// cleaner and more efficient
|
||||
func new_droplet_state_refresh_func(
|
||||
d *schema.ResourceData, attribute string, meta interface{}) resource.StateRefreshFunc {
|
||||
client := meta.(*digitalocean.Client)
|
||||
return func() (interface{}, string, error) {
|
||||
// Retrieve the ELB properties for updating the state
|
||||
droplet, err := client.RetrieveDroplet(id)
|
||||
err := resourceDigitalOceanDropletRead(d, meta)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error on retrieving droplet when waiting: %s", err)
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// If the droplet is locked, continue waiting. We can
|
||||
// only perform actions on unlocked droplets, so it's
|
||||
// pointless to look at that status
|
||||
if droplet.IsLocked() == "true" {
|
||||
if d.Get("locked").(string) == "true" {
|
||||
log.Println("[DEBUG] Droplet is locked, skipping status check and retrying")
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
// Use our mapping to get back a map of the
|
||||
// droplet properties
|
||||
resourceMap, err := resource_digitalocean_droplet_update_state(
|
||||
&terraform.InstanceState{Attributes: map[string]string{}}, &droplet)
|
||||
// See if we can access our attribute
|
||||
if attr, ok := d.GetOk(attribute); ok {
|
||||
// Retrieve the droplet properties
|
||||
droplet, err := client.RetrieveDroplet(d.Id())
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error creating map from droplet: %s", err)
|
||||
return nil, "", err
|
||||
return nil, "", fmt.Errorf("Error retrieving droplet: %s", err)
|
||||
}
|
||||
|
||||
// See if we can access our attribute
|
||||
if attr, ok := resourceMap.Attributes[attribute]; ok {
|
||||
return &droplet, attr, nil
|
||||
return &droplet, attr.(string), nil
|
||||
}
|
||||
|
||||
return nil, "", nil
|
||||
|
@ -389,16 +405,16 @@ func new_droplet_state_refresh_func(id string, attribute string, client *digital
|
|||
}
|
||||
|
||||
// Powers on the droplet and waits for it to be active
|
||||
func power_on_and_wait(id string, client *digitalocean.Client) error {
|
||||
err := client.PowerOn(id)
|
||||
func power_on_and_wait(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
err := client.PowerOn(d.Id())
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for power on
|
||||
_, err = WaitForDropletAttribute(
|
||||
id, "active", []string{"off"}, "status", client)
|
||||
_, err = WaitForDropletAttribute(d, "active", []string{"off"}, "status", client)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -94,7 +94,7 @@ func TestAccDigitalOceanDroplet_PrivateNetworkingIpv6(t *testing.T) {
|
|||
}
|
||||
|
||||
func testAccCheckDigitalOceanDropletDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.client
|
||||
client := testAccProvider.Meta().(*digitalocean.Client)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "digitalocean_droplet" {
|
||||
|
@ -207,7 +207,7 @@ func testAccCheckDigitalOceanDropletExists(n string, droplet *digitalocean.Dropl
|
|||
return fmt.Errorf("No Droplet ID is set")
|
||||
}
|
||||
|
||||
client := testAccProvider.client
|
||||
client := testAccProvider.Meta().(*digitalocean.Client)
|
||||
|
||||
retrieveDroplet, err := client.RetrieveDroplet(rs.Primary.ID)
|
||||
|
||||
|
@ -225,19 +225,23 @@ func testAccCheckDigitalOceanDropletExists(n string, droplet *digitalocean.Dropl
|
|||
}
|
||||
}
|
||||
|
||||
func Test_new_droplet_state_refresh_func(t *testing.T) {
|
||||
droplet := digitalocean.Droplet{
|
||||
Name: "foobar",
|
||||
}
|
||||
resourceMap, _ := resource_digitalocean_droplet_update_state(
|
||||
&terraform.InstanceState{Attributes: map[string]string{}}, &droplet)
|
||||
|
||||
// See if we can access our attribute
|
||||
if _, ok := resourceMap.Attributes["name"]; !ok {
|
||||
t.Fatalf("bad name: %s", resourceMap.Attributes)
|
||||
}
|
||||
|
||||
}
|
||||
// Not sure if this check should remain here as the underlaying
|
||||
// function is changed and is tested indirectly by almost all
|
||||
// other test already
|
||||
//
|
||||
//func Test_new_droplet_state_refresh_func(t *testing.T) {
|
||||
// droplet := digitalocean.Droplet{
|
||||
// Name: "foobar",
|
||||
// }
|
||||
// resourceMap, _ := resource_digitalocean_droplet_update_state(
|
||||
// &terraform.InstanceState{Attributes: map[string]string{}}, &droplet)
|
||||
//
|
||||
// // See if we can access our attribute
|
||||
// if _, ok := resourceMap.Attributes["name"]; !ok {
|
||||
// t.Fatalf("bad name: %s", resourceMap.Attributes)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
const testAccCheckDigitalOceanDropletConfig_basic = `
|
||||
resource "digitalocean_droplet" "foobar" {
|
||||
|
|
|
@ -9,12 +9,12 @@ import (
|
|||
"github.com/pearkes/digitalocean"
|
||||
)
|
||||
|
||||
func resourceRecord() *schema.Resource {
|
||||
func resourceDigitalOceanRecord() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceRecordCreate,
|
||||
Read: resourceRecordRead,
|
||||
Update: resourceRecordUpdate,
|
||||
Delete: resourceRecordDelete,
|
||||
Create: resourceDigitalOceanRecordCreate,
|
||||
Read: resourceDigitalOceanRecordRead,
|
||||
Update: resourceDigitalOceanRecordUpdate,
|
||||
Delete: resourceDigitalOceanRecordDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"type": &schema.Schema{
|
||||
|
@ -65,9 +65,8 @@ func resourceRecord() *schema.Resource {
|
|||
}
|
||||
}
|
||||
|
||||
func resourceRecordCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
func resourceDigitalOceanRecordCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
newRecord := digitalocean.CreateRecord{
|
||||
Type: d.Get("type").(string),
|
||||
|
@ -87,50 +86,11 @@ func resourceRecordCreate(d *schema.ResourceData, meta interface{}) error {
|
|||
d.SetId(recId)
|
||||
log.Printf("[INFO] Record ID: %s", d.Id())
|
||||
|
||||
return resourceRecordRead(d, meta)
|
||||
return resourceDigitalOceanRecordRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceRecordUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
|
||||
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(d.Get("domain").(string), d.Id(), &updateRecord)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to update record: %s", err)
|
||||
}
|
||||
|
||||
return resourceRecordRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceRecordDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
|
||||
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") {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Error deleting record: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceRecordRead(d *schema.ResourceData, meta interface{}) error {
|
||||
p := meta.(*ResourceProvider)
|
||||
client := p.client
|
||||
func resourceDigitalOceanRecordRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
rec, err := client.RetrieveRecord(d.Get("domain").(string), d.Id())
|
||||
if err != nil {
|
||||
|
@ -153,3 +113,39 @@ func resourceRecordRead(d *schema.ResourceData, meta interface{}) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceDigitalOceanRecordUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
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(d.Get("domain").(string), d.Id(), &updateRecord)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to update record: %s", err)
|
||||
}
|
||||
|
||||
return resourceDigitalOceanRecordRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDigitalOceanRecordDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*digitalocean.Client)
|
||||
|
||||
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") {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Error deleting record: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ func TestAccDigitalOceanRecord_Updated(t *testing.T) {
|
|||
}
|
||||
|
||||
func testAccCheckDigitalOceanRecordDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.client
|
||||
client := testAccProvider.Meta().(*digitalocean.Client)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "digitalocean_record" {
|
||||
|
@ -128,7 +128,7 @@ func testAccCheckDigitalOceanRecordExists(n string, record *digitalocean.Record)
|
|||
return fmt.Errorf("No Record ID is set")
|
||||
}
|
||||
|
||||
client := testAccProvider.client
|
||||
client := testAccProvider.Meta().(*digitalocean.Client)
|
||||
|
||||
foundRecord, err := client.RetrieveRecord(rs.Primary.Attributes["domain"], rs.Primary.ID)
|
||||
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/config"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/pearkes/digitalocean"
|
||||
)
|
||||
|
||||
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) Input(
|
||||
input terraform.UIInput,
|
||||
c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) {
|
||||
return Provider().Input(input, c)
|
||||
}
|
||||
|
||||
func (p *ResourceProvider) Validate(c *terraform.ResourceConfig) ([]string, []error) {
|
||||
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)
|
||||
}
|
||||
|
||||
func (p *ResourceProvider) Configure(c *terraform.ResourceConfig) error {
|
||||
if _, err := config.Decode(&p.Config, c.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("[INFO] Initializing DigitalOcean client")
|
||||
var err error
|
||||
p.client, err = p.Config.Client()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the provider, set the meta
|
||||
p.p = Provider()
|
||||
p.p.SetMeta(p)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ResourceProvider) Apply(
|
||||
info *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState,
|
||||
d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
|
||||
if _, ok := p.p.ResourcesMap[info.Type]; ok {
|
||||
return p.p.Apply(info, s, d)
|
||||
}
|
||||
|
||||
return resourceMap.Apply(info, s, d, p)
|
||||
}
|
||||
|
||||
func (p *ResourceProvider) Diff(
|
||||
info *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState,
|
||||
c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
|
||||
if _, ok := p.p.ResourcesMap[info.Type]; ok {
|
||||
return p.p.Diff(info, s, c)
|
||||
}
|
||||
|
||||
return resourceMap.Diff(info, s, c, p)
|
||||
}
|
||||
|
||||
func (p *ResourceProvider) Refresh(
|
||||
info *terraform.InstanceInfo,
|
||||
s *terraform.InstanceState) (*terraform.InstanceState, error) {
|
||||
if _, ok := p.p.ResourcesMap[info.Type]; ok {
|
||||
return p.p.Refresh(info, s)
|
||||
}
|
||||
|
||||
return resourceMap.Refresh(info, s, p)
|
||||
}
|
||||
|
||||
func (p *ResourceProvider) Resources() []terraform.ResourceType {
|
||||
result := resourceMap.Resources()
|
||||
result = append(result, Provider().Resources()...)
|
||||
return result
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccProviders map[string]terraform.ResourceProvider
|
||||
var testAccProvider *ResourceProvider
|
||||
|
||||
func init() {
|
||||
testAccProvider = new(ResourceProvider)
|
||||
testAccProviders = map[string]terraform.ResourceProvider{
|
||||
"digitalocean": testAccProvider,
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceProvider_impl(t *testing.T) {
|
||||
var _ terraform.ResourceProvider = new(ResourceProvider)
|
||||
}
|
||||
|
||||
func TestResourceProvider_Configure(t *testing.T) {
|
||||
rp := new(ResourceProvider)
|
||||
var expectedToken string
|
||||
|
||||
if v := os.Getenv("DIGITALOCEAN_TOKEN"); v != "foo" {
|
||||
expectedToken = v
|
||||
} else {
|
||||
expectedToken = "foo"
|
||||
}
|
||||
|
||||
raw := map[string]interface{}{
|
||||
"token": expectedToken,
|
||||
}
|
||||
|
||||
rawConfig, err := config.NewRawConfig(raw)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
err = rp.Configure(terraform.NewResourceConfig(rawConfig))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
expected := Config{
|
||||
Token: expectedToken,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(rp.Config, expected) {
|
||||
t.Fatalf("bad: %#v", rp.Config)
|
||||
}
|
||||
}
|
||||
|
||||
func testAccPreCheck(t *testing.T) {
|
||||
if v := os.Getenv("DIGITALOCEAN_TOKEN"); v == "" {
|
||||
t.Fatal("DIGITALOCEAN_TOKEN must be set for acceptance tests")
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
// resourceMap is the mapping of resources we support to their basic
|
||||
// operations. This makes it easy to implement new resource types.
|
||||
var resourceMap *resource.Map
|
||||
|
||||
func init() {
|
||||
resourceMap = &resource.Map{
|
||||
Mapping: map[string]resource.Resource{
|
||||
"digitalocean_droplet": resource.Resource{
|
||||
ConfigValidator: resource_digitalocean_droplet_validation(),
|
||||
Create: resource_digitalocean_droplet_create,
|
||||
Destroy: resource_digitalocean_droplet_destroy,
|
||||
Diff: resource_digitalocean_droplet_diff,
|
||||
Refresh: resource_digitalocean_droplet_refresh,
|
||||
Update: resource_digitalocean_droplet_update,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue