From c1ddeac868ff1c7d81b21bf44470feb822f54570 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Mon, 22 May 2017 13:43:11 -0700 Subject: [PATCH] provider/google: Add import support to google_sql_user (#14457) * Support importing google_sql_user * Updated documentation to reflect that passwords are not retrieved. * Added additional documentation detailing use. * Removed unneeded d.setId() line from GoogleSqlUser Read method. * Changed an errors.New() call to fmt.Errorf(). * Migrate schemas of existing GoogleSqlUser resources. * Remove explicitly setting 'id' property * Added google_sql_user to importability page. * Changed separator to '/' from '.' and updated tests + debug messages. --- import_sql_user_test.go | 32 ++++++++++++ resource_sql_user.go | 42 +++++++++++----- resource_sql_user_migrate.go | 39 +++++++++++++++ resource_sql_user_migrate_test.go | 81 +++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 import_sql_user_test.go create mode 100644 resource_sql_user_migrate.go create mode 100644 resource_sql_user_migrate_test.go diff --git a/import_sql_user_test.go b/import_sql_user_test.go new file mode 100644 index 00000000..ea58f1aa --- /dev/null +++ b/import_sql_user_test.go @@ -0,0 +1,32 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccGoogleSqlUser_importBasic(t *testing.T) { + resourceName := "google_sql_user.user" + user := acctest.RandString(10) + instance := acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccGoogleSqlUserDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleSqlUser_basic(instance, user), + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password"}, + }, + }, + }) +} diff --git a/resource_sql_user.go b/resource_sql_user.go index 23daf461..afcc88e1 100644 --- a/resource_sql_user.go +++ b/resource_sql_user.go @@ -3,9 +3,9 @@ package google import ( "fmt" "log" + "strings" "github.com/hashicorp/terraform/helper/schema" - "google.golang.org/api/sqladmin/v1beta4" ) @@ -15,6 +15,12 @@ func resourceSqlUser() *schema.Resource { Read: resourceSqlUserRead, Update: resourceSqlUserUpdate, Delete: resourceSqlUserDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + SchemaVersion: 1, + MigrateState: resourceSqlUserMigrateState, Schema: map[string]*schema.Schema{ "host": &schema.Schema{ @@ -36,8 +42,9 @@ func resourceSqlUser() *schema.Resource { }, "password": &schema.Schema{ - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + Sensitive: true, }, "project": &schema.Schema{ @@ -77,6 +84,8 @@ func resourceSqlUserCreate(d *schema.ResourceData, meta interface{}) error { "user %s into instance %s: %s", name, instance, err) } + d.SetId(fmt.Sprintf("%s/%s", instance, name)) + err = sqladminOperationWait(config, op, "Insert User") if err != nil { @@ -95,8 +104,16 @@ func resourceSqlUserRead(d *schema.ResourceData, meta interface{}) error { return err } - name := d.Get("name").(string) - instance := d.Get("instance").(string) + instanceAndName := strings.SplitN(d.Id(), "/", 2) + if len(instanceAndName) != 2 { + return fmt.Errorf( + "Wrong number of arguments when specifying imported id. Expected: 2. Saw: %d. Expected Input: $INSTANCENAME/$SQLUSERNAME Input: %s", + len(instanceAndName), + d.Id()) + } + + instance := instanceAndName[0] + name := instanceAndName[1] users, err := config.clientSqlAdmin.Users.List(project, instance).Do() @@ -104,23 +121,24 @@ func resourceSqlUserRead(d *schema.ResourceData, meta interface{}) error { return handleNotFoundError(err, d, fmt.Sprintf("SQL User %q in instance %q", name, instance)) } - found := false - for _, user := range users.Items { - if user.Name == name { - found = true + var user *sqladmin.User + for _, currentUser := range users.Items { + if currentUser.Name == name { + user = currentUser break } } - if !found { + if user == nil { log.Printf("[WARN] Removing SQL User %q because it's gone", d.Get("name").(string)) d.SetId("") return nil } - d.SetId(name) - + d.Set("host", user.Host) + d.Set("instance", user.Instance) + d.Set("name", user.Name) return nil } diff --git a/resource_sql_user_migrate.go b/resource_sql_user_migrate.go new file mode 100644 index 00000000..7f52771a --- /dev/null +++ b/resource_sql_user_migrate.go @@ -0,0 +1,39 @@ +package google + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/terraform" +) + +func resourceSqlUserMigrateState( + v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { + if is.Empty() { + log.Println("[DEBUG] Empty InstanceState; nothing to migrate.") + return is, nil + } + + switch v { + case 0: + log.Println("[INFO] Found Google Sql User State v0; migrating to v1") + is, err := migrateSqlUserStateV0toV1(is) + if err != nil { + return is, err + } + return is, nil + default: + return is, fmt.Errorf("Unexpected schema version: %d", v) + } +} + +func migrateSqlUserStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) { + log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes) + + name := is.Attributes["name"] + instance := is.Attributes["instance"] + is.ID = fmt.Sprintf("%s/%s", instance, name) + + log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes) + return is, nil +} diff --git a/resource_sql_user_migrate_test.go b/resource_sql_user_migrate_test.go new file mode 100644 index 00000000..5e03d8d7 --- /dev/null +++ b/resource_sql_user_migrate_test.go @@ -0,0 +1,81 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform/terraform" +) + +func TestSqlUserMigrateState(t *testing.T) { + cases := map[string]struct { + StateVersion int + Attributes map[string]string + Expected map[string]string + Meta interface{} + ID string + ExpectedID string + }{ + "change id from $NAME to $INSTANCENAME.$NAME": { + StateVersion: 0, + Attributes: map[string]string{ + "name": "tf-user", + "instance": "tf-instance", + }, + Expected: map[string]string{ + "name": "tf-user", + "instance": "tf-instance", + }, + Meta: &Config{}, + ID: "tf-user", + ExpectedID: "tf-instance/tf-user", + }, + } + + for tn, tc := range cases { + is := &terraform.InstanceState{ + ID: tc.ID, + Attributes: tc.Attributes, + } + is, err := resourceSqlUserMigrateState( + tc.StateVersion, is, tc.Meta) + + if err != nil { + t.Fatalf("bad: %s, err: %#v", tn, err) + } + + if is.ID != tc.ExpectedID { + t.Fatalf("bad ID.\n\n expected: %s\n got: %s", tc.ExpectedID, is.ID) + } + + for k, v := range tc.Expected { + if is.Attributes[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + tn, k, v, k, is.Attributes[k], is.Attributes) + } + } + } +} + +func TestSqlUserMigrateState_empty(t *testing.T) { + var is *terraform.InstanceState + var meta *Config + + // should handle nil + is, err := resourceSqlUserMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } + if is != nil { + t.Fatalf("expected nil instancestate, got: %#v", is) + } + + // should handle non-nil but empty + is = &terraform.InstanceState{} + is, err = resourceSqlUserMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } +}