2017-08-14 16:30:41 +00:00
|
|
|
package google
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"regexp"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/helper/acctest"
|
|
|
|
"github.com/hashicorp/terraform/helper/resource"
|
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
|
|
|
|
"google.golang.org/api/googleapi"
|
|
|
|
"google.golang.org/api/spanner/v1"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Unit Tests
|
|
|
|
|
|
|
|
func TestSpannerInstanceId_instanceUri(t *testing.T) {
|
|
|
|
id := spannerInstanceId{
|
|
|
|
Project: "project123",
|
|
|
|
Instance: "instance456",
|
|
|
|
}
|
|
|
|
actual := id.instanceUri()
|
|
|
|
expected := "projects/project123/instances/instance456"
|
|
|
|
expectEquals(t, expected, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSpannerInstanceId_instanceConfigUri(t *testing.T) {
|
|
|
|
id := spannerInstanceId{
|
|
|
|
Project: "project123",
|
|
|
|
Instance: "instance456",
|
|
|
|
}
|
|
|
|
actual := id.instanceConfigUri("conf987")
|
|
|
|
expected := "projects/project123/instanceConfigs/conf987"
|
|
|
|
expectEquals(t, expected, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSpannerInstanceId_parentProjectUri(t *testing.T) {
|
|
|
|
id := spannerInstanceId{
|
|
|
|
Project: "project123",
|
|
|
|
Instance: "instance456",
|
|
|
|
}
|
|
|
|
actual := id.parentProjectUri()
|
|
|
|
expected := "projects/project123"
|
|
|
|
expectEquals(t, expected, actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGenSpannerInstanceName(t *testing.T) {
|
|
|
|
s := genSpannerInstanceName()
|
|
|
|
if len(s) != 30 {
|
|
|
|
t.Fatalf("Expected a 30 char ID to be generated, instead found %d chars", len(s))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestImportSpannerInstanceId(t *testing.T) {
|
|
|
|
sid, e := importSpannerInstanceId("instance456")
|
|
|
|
if e != nil {
|
|
|
|
t.Errorf("Error should have been nil")
|
|
|
|
}
|
|
|
|
expectEquals(t, "", sid.Project)
|
|
|
|
expectEquals(t, "instance456", sid.Instance)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestImportSpannerInstanceId_projectAndInstance(t *testing.T) {
|
|
|
|
sid, e := importSpannerInstanceId("project123/instance456")
|
|
|
|
if e != nil {
|
|
|
|
t.Errorf("Error should have been nil")
|
|
|
|
}
|
|
|
|
expectEquals(t, "project123", sid.Project)
|
|
|
|
expectEquals(t, "instance456", sid.Instance)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestImportSpannerInstanceId_invalidLeadingSlash(t *testing.T) {
|
|
|
|
sid, e := importSpannerInstanceId("/instance456")
|
|
|
|
expectInvalidSpannerInstanceImport(t, sid, e)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestImportSpannerInstanceId_invalidTrailingSlash(t *testing.T) {
|
|
|
|
sid, e := importSpannerInstanceId("project123/")
|
|
|
|
expectInvalidSpannerInstanceImport(t, sid, e)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestImportSpannerInstanceId_invalidSingleSlash(t *testing.T) {
|
|
|
|
sid, e := importSpannerInstanceId("/")
|
|
|
|
expectInvalidSpannerInstanceImport(t, sid, e)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestImportSpannerInstanceId_invalidMultiSlash(t *testing.T) {
|
|
|
|
sid, e := importSpannerInstanceId("project123/instance456/db789")
|
|
|
|
expectInvalidSpannerInstanceImport(t, sid, e)
|
|
|
|
}
|
|
|
|
|
2018-03-05 21:24:55 +00:00
|
|
|
func TestImportSpannerInstanceId_projectId(t *testing.T) {
|
|
|
|
shouldPass := []string{
|
|
|
|
"project-id/instance",
|
|
|
|
"123123/instance",
|
|
|
|
"hashicorptest.net:project-123/instance",
|
|
|
|
"123/456",
|
|
|
|
}
|
|
|
|
|
|
|
|
shouldFail := []string{
|
|
|
|
"project-id#/instance",
|
|
|
|
"project-id/instance#",
|
|
|
|
"hashicorptest.net:project-123:invalid:project/instance",
|
|
|
|
"hashicorptest.net:/instance",
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, element := range shouldPass {
|
|
|
|
_, e := importSpannerInstanceId(element)
|
|
|
|
if e != nil {
|
|
|
|
t.Error("importSpannerInstanceId should pass on '" + element + "' but doesn't")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, element := range shouldFail {
|
|
|
|
_, e := importSpannerInstanceId(element)
|
|
|
|
if e == nil {
|
|
|
|
t.Error("importSpannerInstanceId should fail on '" + element + "' but doesn't")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-14 16:30:41 +00:00
|
|
|
func expectInvalidSpannerInstanceImport(t *testing.T, sid *spannerInstanceId, e error) {
|
|
|
|
if sid != nil {
|
|
|
|
t.Errorf("Expected spannerInstanceId to be nil")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if e == nil {
|
|
|
|
t.Errorf("Expected an Error but did not get one")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !strings.HasPrefix(e.Error(), "Invalid spanner instance specifier") {
|
|
|
|
t.Errorf("Expecting Error starting with 'Invalid spanner instance specifier'")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func expectEquals(t *testing.T, expected, actual string) {
|
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("Expected %s, but got %s", expected, actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Acceptance Tests
|
|
|
|
|
|
|
|
func TestAccSpannerInstance_basic(t *testing.T) {
|
2017-10-12 22:07:29 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2017-08-14 16:30:41 +00:00
|
|
|
var instance spanner.Instance
|
|
|
|
rnd := acctest.RandString(10)
|
|
|
|
idName := fmt.Sprintf("spanner-test-%s", rnd)
|
|
|
|
resource.Test(t, resource.TestCase{
|
|
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckSpannerInstanceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
{
|
|
|
|
Config: testAccSpannerInstance_basic(idName),
|
|
|
|
Check: resource.ComposeTestCheckFunc(
|
|
|
|
testAccCheckSpannerInstanceExists("google_spanner_instance.basic", &instance),
|
|
|
|
|
|
|
|
resource.TestCheckResourceAttr("google_spanner_instance.basic", "name", idName),
|
|
|
|
resource.TestCheckResourceAttr("google_spanner_instance.basic", "display_name", idName+"-dname"),
|
|
|
|
resource.TestCheckResourceAttr("google_spanner_instance.basic", "num_nodes", "1"),
|
|
|
|
resource.TestCheckResourceAttrSet("google_spanner_instance.basic", "state"),
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAccSpannerInstance_basicWithAutogenName(t *testing.T) {
|
2017-10-12 22:07:29 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2017-08-14 16:30:41 +00:00
|
|
|
var instance spanner.Instance
|
|
|
|
rnd := acctest.RandString(10)
|
|
|
|
displayName := fmt.Sprintf("spanner-test-%s-dname", rnd)
|
|
|
|
resource.Test(t, resource.TestCase{
|
|
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckSpannerInstanceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
{
|
|
|
|
Config: testAccSpannerInstance_basicWithAutogenName(displayName),
|
|
|
|
Check: resource.ComposeTestCheckFunc(
|
|
|
|
testAccCheckSpannerInstanceExists("google_spanner_instance.basic", &instance),
|
|
|
|
|
|
|
|
resource.TestCheckResourceAttr("google_spanner_instance.basic", "display_name", displayName),
|
|
|
|
resource.TestCheckResourceAttrSet("google_spanner_instance.basic", "name"),
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAccSpannerInstance_duplicateNameError(t *testing.T) {
|
2017-10-12 22:07:29 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2017-08-14 16:30:41 +00:00
|
|
|
var instance spanner.Instance
|
|
|
|
rnd := acctest.RandString(10)
|
|
|
|
idName := fmt.Sprintf("spanner-test-%s", rnd)
|
|
|
|
resource.Test(t, resource.TestCase{
|
|
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckSpannerInstanceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
{
|
|
|
|
Config: testAccSpannerInstance_duplicateNameError_part1(idName),
|
|
|
|
Check: resource.ComposeTestCheckFunc(
|
|
|
|
testAccCheckSpannerInstanceExists("google_spanner_instance.basic1", &instance),
|
|
|
|
),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Config: testAccSpannerInstance_duplicateNameError_part2(idName),
|
|
|
|
ExpectError: regexp.MustCompile(
|
|
|
|
fmt.Sprintf("Error, the name %s is not unique within project", idName)),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAccSpannerInstance_update(t *testing.T) {
|
2017-10-12 22:07:29 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2017-08-14 16:30:41 +00:00
|
|
|
var instance spanner.Instance
|
|
|
|
rnd := acctest.RandString(10)
|
|
|
|
dName1 := fmt.Sprintf("spanner-dname1-%s", rnd)
|
|
|
|
dName2 := fmt.Sprintf("spanner-dname2-%s", rnd)
|
|
|
|
resource.Test(t, resource.TestCase{
|
|
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
|
|
Providers: testAccProviders,
|
|
|
|
CheckDestroy: testAccCheckSpannerInstanceDestroy,
|
|
|
|
Steps: []resource.TestStep{
|
|
|
|
{
|
|
|
|
Config: testAccSpannerInstance_update(dName1, 1, false),
|
|
|
|
Check: resource.ComposeTestCheckFunc(
|
|
|
|
testAccCheckSpannerInstanceExists("google_spanner_instance.updater", &instance),
|
|
|
|
resource.TestCheckResourceAttr("google_spanner_instance.updater", "display_name", dName1),
|
|
|
|
resource.TestCheckResourceAttr("google_spanner_instance.updater", "num_nodes", "1"),
|
|
|
|
resource.TestCheckResourceAttr("google_spanner_instance.updater", "labels.%", "1"),
|
|
|
|
),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Config: testAccSpannerInstance_update(dName2, 2, true),
|
|
|
|
Check: resource.ComposeTestCheckFunc(
|
|
|
|
testAccCheckSpannerInstanceExists("google_spanner_instance.updater", &instance),
|
|
|
|
resource.TestCheckResourceAttr("google_spanner_instance.updater", "display_name", dName2),
|
|
|
|
resource.TestCheckResourceAttr("google_spanner_instance.updater", "num_nodes", "2"),
|
|
|
|
resource.TestCheckResourceAttr("google_spanner_instance.updater", "labels.%", "2"),
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAccCheckSpannerInstanceDestroy(s *terraform.State) error {
|
|
|
|
config := testAccProvider.Meta().(*Config)
|
|
|
|
|
|
|
|
for _, rs := range s.RootModule().Resources {
|
|
|
|
if rs.Type != "google_spanner_instance" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if rs.Primary.ID == "" {
|
|
|
|
return fmt.Errorf("Unable to verify delete of spanner instance, ID is empty")
|
|
|
|
}
|
|
|
|
|
|
|
|
instanceName := rs.Primary.Attributes["name"]
|
|
|
|
project, err := getTestProject(rs.Primary, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
id := spannerInstanceId{
|
|
|
|
Project: project,
|
|
|
|
Instance: instanceName,
|
|
|
|
}
|
|
|
|
_, err = config.clientSpanner.Projects.Instances.Get(
|
|
|
|
id.instanceUri()).Do()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == http.StatusNotFound {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return fmt.Errorf("Error make GCP platform call to verify spanner instance deleted: %s", err.Error())
|
|
|
|
}
|
|
|
|
return fmt.Errorf("Spanner instance not destroyed - still exists")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAccCheckSpannerInstanceExists(n string, instance *spanner.Instance) resource.TestCheckFunc {
|
|
|
|
return func(s *terraform.State) error {
|
|
|
|
config := testAccProvider.Meta().(*Config)
|
|
|
|
rs, ok := s.RootModule().Resources[n]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("Terraform resource Not found: %s", n)
|
|
|
|
}
|
|
|
|
|
|
|
|
if rs.Primary.ID == "" {
|
|
|
|
return fmt.Errorf("No ID is set for Spanner instance")
|
|
|
|
}
|
|
|
|
|
|
|
|
id, err := extractSpannerInstanceId(rs.Primary.ID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
found, err := config.clientSpanner.Projects.Instances.Get(
|
|
|
|
id.instanceUri()).Do()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-01-17 18:45:28 +00:00
|
|
|
fName := GetResourceNameFromSelfLink(found.Name)
|
|
|
|
if fName != GetResourceNameFromSelfLink(rs.Primary.ID) {
|
2017-08-14 16:30:41 +00:00
|
|
|
return fmt.Errorf("Spanner instance %s not found, found %s instead", rs.Primary.ID, fName)
|
|
|
|
}
|
|
|
|
|
|
|
|
*instance = *found
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAccSpannerInstance_basic(name string) string {
|
|
|
|
return fmt.Sprintf(`
|
|
|
|
resource "google_spanner_instance" "basic" {
|
|
|
|
name = "%s"
|
|
|
|
config = "regional-us-central1"
|
|
|
|
display_name = "%s-dname"
|
|
|
|
num_nodes = 1
|
|
|
|
}
|
|
|
|
`, name, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAccSpannerInstance_basicWithProject(project, name string) string {
|
|
|
|
return fmt.Sprintf(`
|
|
|
|
resource "google_spanner_instance" "basic" {
|
|
|
|
project = "%s"
|
|
|
|
name = "%s"
|
|
|
|
config = "regional-us-central1"
|
|
|
|
display_name = "%s-dname"
|
|
|
|
num_nodes = 1
|
|
|
|
}
|
|
|
|
`, project, name, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAccSpannerInstance_basicWithAutogenName(name string) string {
|
|
|
|
return fmt.Sprintf(`
|
|
|
|
resource "google_spanner_instance" "basic" {
|
|
|
|
config = "regional-us-central1"
|
|
|
|
display_name = "%s"
|
|
|
|
num_nodes = 1
|
|
|
|
}
|
|
|
|
`, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAccSpannerInstance_duplicateNameError_part1(name string) string {
|
|
|
|
return fmt.Sprintf(`
|
|
|
|
resource "google_spanner_instance" "basic1" {
|
|
|
|
name = "%s"
|
|
|
|
config = "regional-us-central1"
|
|
|
|
display_name = "%s-dname"
|
|
|
|
num_nodes = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
`, name, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAccSpannerInstance_duplicateNameError_part2(name string) string {
|
|
|
|
return fmt.Sprintf(`
|
|
|
|
%s
|
|
|
|
|
|
|
|
resource "google_spanner_instance" "basic2" {
|
|
|
|
name = "%s"
|
|
|
|
config = "regional-us-central1"
|
|
|
|
display_name = "%s-dname"
|
|
|
|
num_nodes = 1
|
|
|
|
}
|
|
|
|
`, testAccSpannerInstance_duplicateNameError_part1(name), name, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAccSpannerInstance_update(name string, nodes int, addLabel bool) string {
|
|
|
|
|
|
|
|
extraLabel := ""
|
|
|
|
if addLabel {
|
|
|
|
extraLabel = "\"key2\" = \"value2\""
|
|
|
|
}
|
|
|
|
return fmt.Sprintf(`
|
|
|
|
resource "google_spanner_instance" "updater" {
|
|
|
|
config = "regional-us-central1"
|
|
|
|
display_name = "%s"
|
|
|
|
num_nodes = %d
|
|
|
|
|
|
|
|
labels {
|
|
|
|
"key1" = "value1"
|
|
|
|
%s
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`, name, nodes, extraLabel)
|
|
|
|
}
|