opentofu/builtin/providers/digitalocean/resource_digitalocean_droplet_test.go
Julien Pivotto ccb34b702d provider/digitalocean: prevent new resources when using ID's of images with slugs (#13879)
When you specify the ID of an image that has a slug, terraform would
store its slug to the state, hence it would always recreate the image.

This commit fixes it by storing the image as an ID when it is specified
by and ID by the user, ignoring the slug.

Closes #12751.
Fixes #12255.

Signed-off-by: Julien Pivotto <roidelapluie@inuits.eu>
2017-04-27 18:48:58 +01:00

631 lines
18 KiB
Go

package digitalocean
import (
"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(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(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`