package digitalocean import ( "context" "fmt" "strconv" "strings" "testing" "github.com/digitalocean/godo" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) func TestAccDigitalOceanDroplet_Basic(t *testing.T) { var droplet godo.Droplet rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckDigitalOceanDropletDestroy, Steps: []resource.TestStep{ { Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), testAccCheckDigitalOceanDropletAttributes(&droplet), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "size", "512mb"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "price_hourly", "0.00744"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "price_monthly", "5"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "image", "centos-7-x64"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "region", "nyc3"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "user_data", "foobar"), ), }, }, }) } func TestAccDigitalOceanDroplet_WithID(t *testing.T) { var droplet godo.Droplet rInt := acctest.RandInt() // TODO: not hardcode this as it will change over time centosID := 22995941 resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckDigitalOceanDropletDestroy, Steps: []resource.TestStep{ { Config: testAccCheckDigitalOceanDropletConfig_withID(centosID, rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), ), }, }, }) } func TestAccDigitalOceanDroplet_withSSH(t *testing.T) { var droplet godo.Droplet rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckDigitalOceanDropletDestroy, Steps: []resource.TestStep{ { Config: testAccCheckDigitalOceanDropletConfig_withSSH(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), testAccCheckDigitalOceanDropletAttributes(&droplet), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "size", "512mb"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "image", "centos-7-x64"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "region", "nyc3"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "user_data", "foobar"), ), }, }, }) } func TestAccDigitalOceanDroplet_Update(t *testing.T) { var droplet godo.Droplet rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckDigitalOceanDropletDestroy, Steps: []resource.TestStep{ { Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), testAccCheckDigitalOceanDropletAttributes(&droplet), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), ), }, { Config: testAccCheckDigitalOceanDropletConfig_RenameAndResize(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), testAccCheckDigitalOceanDropletRenamedAndResized(&droplet), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("baz-%d", rInt)), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "size", "1gb"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "disk", "30"), ), }, }, }) } func TestAccDigitalOceanDroplet_ResizeWithOutDisk(t *testing.T) { var droplet godo.Droplet rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckDigitalOceanDropletDestroy, Steps: []resource.TestStep{ { Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), testAccCheckDigitalOceanDropletAttributes(&droplet), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), ), }, { Config: testAccCheckDigitalOceanDropletConfig_resize_without_disk(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), testAccCheckDigitalOceanDropletResizeWithOutDisk(&droplet), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "size", "1gb"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "disk", "20"), ), }, }, }) } func TestAccDigitalOceanDroplet_ResizeOnlyDisk(t *testing.T) { var droplet godo.Droplet rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckDigitalOceanDropletDestroy, Steps: []resource.TestStep{ { Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), testAccCheckDigitalOceanDropletAttributes(&droplet), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), ), }, { Config: testAccCheckDigitalOceanDropletConfig_resize_without_disk(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), testAccCheckDigitalOceanDropletResizeWithOutDisk(&droplet), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "size", "1gb"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "disk", "20"), ), }, { Config: testAccCheckDigitalOceanDropletConfig_resize_only_disk(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), testAccCheckDigitalOceanDropletResizeOnlyDisk(&droplet), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "size", "1gb"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "disk", "30"), ), }, }, }) } func TestAccDigitalOceanDroplet_UpdateUserData(t *testing.T) { var afterCreate, afterUpdate godo.Droplet rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckDigitalOceanDropletDestroy, Steps: []resource.TestStep{ { Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &afterCreate), testAccCheckDigitalOceanDropletAttributes(&afterCreate), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), ), }, { Config: testAccCheckDigitalOceanDropletConfig_userdata_update(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &afterUpdate), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "user_data", "foobar foobar"), testAccCheckDigitalOceanDropletRecreated( t, &afterCreate, &afterUpdate), ), }, }, }) } func TestAccDigitalOceanDroplet_UpdateTags(t *testing.T) { var afterCreate, afterUpdate godo.Droplet rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckDigitalOceanDropletDestroy, Steps: []resource.TestStep{ { Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &afterCreate), testAccCheckDigitalOceanDropletAttributes(&afterCreate), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), ), }, { Config: testAccCheckDigitalOceanDropletConfig_tag_update(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &afterUpdate), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "tags.#", "1"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "tags.0", "barbaz"), ), }, }, }) } func TestAccDigitalOceanDroplet_PrivateNetworkingIpv6(t *testing.T) { var droplet godo.Droplet rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckDigitalOceanDropletDestroy, Steps: []resource.TestStep{ { Config: testAccCheckDigitalOceanDropletConfig_PrivateNetworkingIpv6(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), testAccCheckDigitalOceanDropletAttributes_PrivateNetworkingIpv6(&droplet), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "private_networking", "true"), resource.TestCheckResourceAttr( "digitalocean_droplet.foobar", "ipv6", "true"), ), }, }, }) } func testAccCheckDigitalOceanDropletDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*godo.Client) for _, rs := range s.RootModule().Resources { if rs.Type != "digitalocean_droplet" { continue } id, err := strconv.Atoi(rs.Primary.ID) if err != nil { return err } // Try to find the Droplet _, _, err = client.Droplets.Get(context.Background(), id) // Wait if err != nil && !strings.Contains(err.Error(), "404") { return fmt.Errorf( "Error waiting for droplet (%s) to be destroyed: %s", rs.Primary.ID, err) } } return nil } func testAccCheckDigitalOceanDropletAttributes(droplet *godo.Droplet) resource.TestCheckFunc { return func(s *terraform.State) error { if droplet.Image.Slug != "centos-7-x64" { return fmt.Errorf("Bad image_slug: %s", droplet.Image.Slug) } if droplet.Size.Slug != "512mb" { return fmt.Errorf("Bad size_slug: %s", droplet.Size.Slug) } if droplet.Size.PriceHourly != 0.00744 { return fmt.Errorf("Bad price_hourly: %v", droplet.Size.PriceHourly) } if droplet.Size.PriceMonthly != 5.0 { return fmt.Errorf("Bad price_monthly: %v", droplet.Size.PriceMonthly) } if droplet.Region.Slug != "nyc3" { return fmt.Errorf("Bad region_slug: %s", droplet.Region.Slug) } return nil } } func testAccCheckDigitalOceanDropletRenamedAndResized(droplet *godo.Droplet) resource.TestCheckFunc { return func(s *terraform.State) error { if droplet.Size.Slug != "1gb" { return fmt.Errorf("Bad size_slug: %s", droplet.SizeSlug) } if droplet.Disk != 30 { return fmt.Errorf("Bad disk: %d", droplet.Disk) } return nil } } func testAccCheckDigitalOceanDropletResizeWithOutDisk(droplet *godo.Droplet) resource.TestCheckFunc { return func(s *terraform.State) error { if droplet.Size.Slug != "1gb" { return fmt.Errorf("Bad size_slug: %s", droplet.SizeSlug) } if droplet.Disk != 20 { return fmt.Errorf("Bad disk: %d", droplet.Disk) } return nil } } func testAccCheckDigitalOceanDropletResizeOnlyDisk(droplet *godo.Droplet) resource.TestCheckFunc { return func(s *terraform.State) error { if droplet.Size.Slug != "1gb" { return fmt.Errorf("Bad size_slug: %s", droplet.SizeSlug) } if droplet.Disk != 30 { return fmt.Errorf("Bad disk: %d", droplet.Disk) } return nil } } func testAccCheckDigitalOceanDropletAttributes_PrivateNetworkingIpv6(droplet *godo.Droplet) resource.TestCheckFunc { return func(s *terraform.State) error { if droplet.Image.Slug != "centos-7-x64" { return fmt.Errorf("Bad image_slug: %s", droplet.Image.Slug) } if droplet.Size.Slug != "1gb" { return fmt.Errorf("Bad size_slug: %s", droplet.Size.Slug) } if droplet.Region.Slug != "sgp1" { return fmt.Errorf("Bad region_slug: %s", droplet.Region.Slug) } if findIPv4AddrByType(droplet, "private") == "" { return fmt.Errorf("No ipv4 private: %s", findIPv4AddrByType(droplet, "private")) } // if droplet.IPV6Address("private") == "" { // return fmt.Errorf("No ipv6 private: %s", droplet.IPV6Address("private")) // } if findIPv4AddrByType(droplet, "public") == "" { return fmt.Errorf("No ipv4 public: %s", findIPv4AddrByType(droplet, "public")) } if findIPv6AddrByType(droplet, "public") == "" { return fmt.Errorf("No ipv6 public: %s", findIPv6AddrByType(droplet, "public")) } for _, rs := range s.RootModule().Resources { if rs.Type != "digitalocean_droplet" { continue } if rs.Primary.Attributes["ipv6_address"] != strings.ToLower(findIPv6AddrByType(droplet, "public")) { return fmt.Errorf("IPV6 Address should be lowercase") } } return nil } } func testAccCheckDigitalOceanDropletExists(n string, droplet *godo.Droplet) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } if rs.Primary.ID == "" { return fmt.Errorf("No Droplet ID is set") } client := testAccProvider.Meta().(*godo.Client) id, err := strconv.Atoi(rs.Primary.ID) if err != nil { return err } // Try to find the Droplet retrieveDroplet, _, err := client.Droplets.Get(context.Background(), id) if err != nil { return err } if strconv.Itoa(retrieveDroplet.ID) != rs.Primary.ID { return fmt.Errorf("Droplet not found") } *droplet = *retrieveDroplet return nil } } func testAccCheckDigitalOceanDropletRecreated(t *testing.T, before, after *godo.Droplet) resource.TestCheckFunc { return func(s *terraform.State) error { if before.ID == after.ID { t.Fatalf("Expected change of droplet IDs, but both were %v", before.ID) } return nil } } func testAccCheckDigitalOceanDropletConfig_basic(rInt int) string { return fmt.Sprintf(` resource "digitalocean_droplet" "foobar" { name = "foo-%d" size = "512mb" image = "centos-7-x64" region = "nyc3" user_data = "foobar" }`, rInt) } func testAccCheckDigitalOceanDropletConfig_withID(imageID, rInt int) string { return fmt.Sprintf(` resource "digitalocean_droplet" "foobar" { name = "foo-%d" size = "512mb" image = "%d" region = "nyc3" user_data = "foobar" }`, rInt, imageID) } func testAccCheckDigitalOceanDropletConfig_withSSH(rInt int) string { return fmt.Sprintf(` resource "digitalocean_ssh_key" "foobar" { name = "foobar-%d" public_key = "%s" } resource "digitalocean_droplet" "foobar" { name = "foo-%d" size = "512mb" image = "centos-7-x64" region = "nyc3" user_data = "foobar" ssh_keys = ["${digitalocean_ssh_key.foobar.id}"] }`, rInt, testAccValidPublicKey, rInt) } func testAccCheckDigitalOceanDropletConfig_tag_update(rInt int) string { return fmt.Sprintf(` resource "digitalocean_tag" "barbaz" { name = "barbaz" } resource "digitalocean_droplet" "foobar" { name = "foo-%d" size = "512mb" image = "centos-7-x64" region = "nyc3" user_data = "foobar" tags = ["${digitalocean_tag.barbaz.id}"] } `, rInt) } func testAccCheckDigitalOceanDropletConfig_userdata_update(rInt int) string { return fmt.Sprintf(` resource "digitalocean_droplet" "foobar" { name = "foo-%d" size = "512mb" image = "centos-7-x64" region = "nyc3" user_data = "foobar foobar" } `, rInt) } func testAccCheckDigitalOceanDropletConfig_RenameAndResize(rInt int) string { return fmt.Sprintf(` resource "digitalocean_droplet" "foobar" { name = "baz-%d" size = "1gb" image = "centos-7-x64" region = "nyc3" } `, rInt) } func testAccCheckDigitalOceanDropletConfig_resize_without_disk(rInt int) string { return fmt.Sprintf(` resource "digitalocean_droplet" "foobar" { name = "foo-%d" size = "1gb" image = "centos-7-x64" region = "nyc3" user_data = "foobar" resize_disk = false } `, rInt) } func testAccCheckDigitalOceanDropletConfig_resize_only_disk(rInt int) string { return fmt.Sprintf(` resource "digitalocean_droplet" "foobar" { name = "foo-%d" size = "1gb" image = "centos-7-x64" region = "nyc3" user_data = "foobar" resize_disk = true } `, rInt) } // IPV6 only in singapore func testAccCheckDigitalOceanDropletConfig_PrivateNetworkingIpv6(rInt int) string { return fmt.Sprintf(` resource "digitalocean_droplet" "foobar" { name = "baz-%d" size = "1gb" image = "centos-7-x64" region = "sgp1" ipv6 = true private_networking = true } `, rInt) } var testAccValidPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR`