diff --git a/ovh/data_cloud_project_database_postgresql_user.go b/ovh/data_cloud_project_database_postgresql_user.go new file mode 100644 index 000000000..e523b25b1 --- /dev/null +++ b/ovh/data_cloud_project_database_postgresql_user.go @@ -0,0 +1,97 @@ +package ovh + +import ( + "fmt" + "log" + "net/url" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceCloudProjectDatabasePostgresqlUser() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudProjectDatabasePostgresqlUserRead, + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + }, + "cluster_id": { + Type: schema.TypeString, + Description: "Cluster ID", + Required: true, + }, + "name": { + Type: schema.TypeString, + Description: "Name of the user", + Required: true, + }, + + //Computed + "created_at": { + Type: schema.TypeString, + Description: "Date of the creation of the user", + Computed: true, + }, + "roles": { + Type: schema.TypeList, + Description: "Roles the user belongs to", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "status": { + Type: schema.TypeString, + Description: "Current status of the user", + Computed: true, + }, + }, + } +} + +func dataSourceCloudProjectDatabasePostgresqlUserRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + clusterId := d.Get("cluster_id").(string) + name := d.Get("name").(string) + + listEndpoint := fmt.Sprintf("/cloud/project/%s/database/postgresql/%s/user", + url.PathEscape(serviceName), + url.PathEscape(clusterId), + ) + + listRes := make([]string, 0) + + log.Printf("[DEBUG] Will read users from cluster %s from project %s", clusterId, serviceName) + if err := config.OVHClient.Get(listEndpoint, &listRes); err != nil { + return fmt.Errorf("Error calling GET %s:\n\t %q", listEndpoint, err) + } + + for _, id := range listRes { + endpoint := fmt.Sprintf("/cloud/project/%s/database/postgresql/%s/user/%s", + url.PathEscape(serviceName), + url.PathEscape(clusterId), + url.PathEscape(id), + ) + res := &CloudProjectDatabasePostgresqlUserResponse{} + + log.Printf("[DEBUG] Will read user %s from cluster %s from project %s", id, clusterId, serviceName) + if err := config.OVHClient.Get(endpoint, res); err != nil { + return fmt.Errorf("Error calling GET %s:\n\t %q", endpoint, err) + } + + if res.Username == name { + for k, v := range res.ToMap() { + if k != "id" { + d.Set(k, v) + } else { + d.SetId(fmt.Sprint(v)) + } + } + log.Printf("[DEBUG] Read user %+v", res) + return nil + } + } + + return fmt.Errorf("User name %s not found for cluster %s from project %s", name, clusterId, serviceName) +} diff --git a/ovh/data_cloud_project_database_postgresql_user_test.go b/ovh/data_cloud_project_database_postgresql_user_test.go new file mode 100644 index 000000000..4cc75c183 --- /dev/null +++ b/ovh/data_cloud_project_database_postgresql_user_test.go @@ -0,0 +1,76 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const testAccCloudProjectDatabasePostgresqlUserDatasourceConfig_Basic = ` +resource "ovh_cloud_project_database" "db" { + service_name = "%s" + engine = "postgresql" + version = "%s" + plan = "essential" + nodes { + region = "%s" + } + flavor = "%s" +} + +resource "ovh_cloud_project_database_postgresql_user" "user" { + service_name = ovh_cloud_project_database.db.service_name + cluster_id = ovh_cloud_project_database.db.id + name = "%s" + roles = ["replication"] +} + +data "ovh_cloud_project_database_postgresql_user" "user" { + service_name = ovh_cloud_project_database_postgresql_user.user.service_name + cluster_id = ovh_cloud_project_database_postgresql_user.user.cluster_id + name = ovh_cloud_project_database_postgresql_user.user.name +} +` + +func TestAccCloudProjectDatabasePostgresqlUserDataSource_basic(t *testing.T) { + serviceName := os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST") + version := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_POSTGRESQL_VERSION_TEST") + if version == "" { + version = os.Getenv("OVH_CLOUD_PROJECT_DATABASE_VERSION_TEST") + } + region := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_REGION_TEST") + flavor := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_FLAVOR_TEST") + name := "johndoe" + + config := fmt.Sprintf( + testAccCloudProjectDatabasePostgresqlUserDatasourceConfig_Basic, + serviceName, + version, + region, + flavor, + name, + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckCloudDatabaseNoEngine(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_database_postgresql_user.user", "created_at"), + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_database_postgresql_user.user", "roles.#"), + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_database_postgresql_user.user", "status"), + resource.TestCheckResourceAttr( + "data.ovh_cloud_project_database_postgresql_user.user", "name", name, + ), + ), + }, + }, + }) +} diff --git a/ovh/data_cloud_project_database_user.go b/ovh/data_cloud_project_database_user.go new file mode 100644 index 000000000..c39837af4 --- /dev/null +++ b/ovh/data_cloud_project_database_user.go @@ -0,0 +1,105 @@ +package ovh + +import ( + "fmt" + "log" + "net/url" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceCloudProjectDatabaseUser() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudProjectDatabaseUserRead, + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + }, + "engine": { + Type: schema.TypeString, + Description: "Name of the engine of the service", + Required: true, + }, + "cluster_id": { + Type: schema.TypeString, + Description: "Cluster ID", + Required: true, + }, + "name": { + Type: schema.TypeString, + Description: "Name of the user", + Required: true, + }, + + //Computed + "created_at": { + Type: schema.TypeString, + Description: "Date of the creation of the user", + Computed: true, + }, + "status": { + Type: schema.TypeString, + Description: "Current status of the user", + Computed: true, + }, + }, + } +} + +func dataSourceCloudProjectDatabaseUserRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + engine := d.Get("engine").(string) + clusterId := d.Get("cluster_id").(string) + name := d.Get("name").(string) + + listEndpoint := fmt.Sprintf("/cloud/project/%s/database/%s/%s/user", + url.PathEscape(serviceName), + url.PathEscape(engine), + url.PathEscape(clusterId), + ) + + listRes := make([]string, 0) + + log.Printf("[DEBUG] Will read users from cluster %s from project %s", clusterId, serviceName) + if err := config.OVHClient.Get(listEndpoint, &listRes); err != nil { + return fmt.Errorf("Error calling GET %s:\n\t %q", listEndpoint, err) + } + + for _, id := range listRes { + endpoint := fmt.Sprintf("/cloud/project/%s/database/%s/%s/user/%s", + url.PathEscape(serviceName), + url.PathEscape(engine), + url.PathEscape(clusterId), + url.PathEscape(id), + ) + + err := mustGenericEngineUserEndpoint(engine) + if err != nil { + return fmt.Errorf("Calling Get %s :\n\t %q", endpoint, err) + } + + res := &CloudProjectDatabaseUserResponse{} + + log.Printf("[DEBUG] Will read user %s from cluster %s from project %s", id, clusterId, serviceName) + if err := config.OVHClient.Get(endpoint, res); err != nil { + return fmt.Errorf("Error calling GET %s:\n\t %q", endpoint, err) + } + + if res.Username == name { + for k, v := range res.ToMap() { + if k != "id" { + d.Set(k, v) + } else { + d.SetId(fmt.Sprint(v)) + } + } + log.Printf("[DEBUG] Read user %+v", res) + return nil + } + } + + return fmt.Errorf("User name %s not found for cluster %s from project %s", name, clusterId, serviceName) +} diff --git a/ovh/data_cloud_project_database_user_test.go b/ovh/data_cloud_project_database_user_test.go new file mode 100644 index 000000000..76036b761 --- /dev/null +++ b/ovh/data_cloud_project_database_user_test.go @@ -0,0 +1,74 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const testAccCloudProjectDatabaseUserDatasourceConfig_Basic = ` +resource "ovh_cloud_project_database" "db" { + service_name = "%s" + engine = "%s" + version = "%s" + plan = "essential" + nodes { + region = "%s" + } + flavor = "%s" +} + +resource "ovh_cloud_project_database_user" "user" { + service_name = ovh_cloud_project_database.db.service_name + engine = ovh_cloud_project_database.db.engine + cluster_id = ovh_cloud_project_database.db.id + name = "%s" +} + +data "ovh_cloud_project_database_user" "user" { + service_name = ovh_cloud_project_database_user.user.service_name + engine = ovh_cloud_project_database_user.user.engine + cluster_id = ovh_cloud_project_database_user.user.cluster_id + name = ovh_cloud_project_database_user.user.name +} +` + +func TestAccCloudProjectDatabaseUserDataSource_basic(t *testing.T) { + serviceName := os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST") + engine := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_ENGINE_TEST") + version := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_VERSION_TEST") + region := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_REGION_TEST") + flavor := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_FLAVOR_TEST") + name := "johndoe" + + config := fmt.Sprintf( + testAccCloudProjectDatabaseUserDatasourceConfig_Basic, + serviceName, + engine, + version, + region, + flavor, + name, + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckCloudDatabase(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_database_user.user", "created_at"), + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_database_user.user", "status"), + resource.TestCheckResourceAttr( + "data.ovh_cloud_project_database_user.user", "name", name, + ), + ), + }, + }, + }) +} diff --git a/ovh/data_cloud_project_database_users.go b/ovh/data_cloud_project_database_users.go new file mode 100644 index 000000000..0772a3797 --- /dev/null +++ b/ovh/data_cloud_project_database_users.go @@ -0,0 +1,70 @@ +package ovh + +import ( + "fmt" + "log" + "net/url" + "sort" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/ovh/terraform-provider-ovh/ovh/helpers/hashcode" +) + +func dataSourceCloudProjectDatabaseUsers() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudProjectDatabaseUsersRead, + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + }, + "engine": { + Type: schema.TypeString, + Description: "Name of the engine of the service", + Required: true, + }, + "cluster_id": { + Type: schema.TypeString, + Description: "Cluster ID", + Required: true, + }, + + //Computed + "user_ids": { + Type: schema.TypeList, + Description: "List of users ids", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func dataSourceCloudProjectDatabaseUsersRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + engine := d.Get("engine").(string) + clusterId := d.Get("cluster_id").(string) + + endpoint := fmt.Sprintf("/cloud/project/%s/database/%s/%s/user", + url.PathEscape(serviceName), + url.PathEscape(engine), + url.PathEscape(clusterId), + ) + + res := make([]string, 0) + + log.Printf("[DEBUG] Will read users from cluster %s from project %s", clusterId, serviceName) + if err := config.OVHClient.Get(endpoint, &res); err != nil { + return fmt.Errorf("Error calling GET %s:\n\t %q", endpoint, err) + } + + // sort.Strings sorts in place, returns nothing + sort.Strings(res) + + d.SetId(hashcode.Strings(res)) + d.Set("user_ids", res) + + return nil +} diff --git a/ovh/data_cloud_project_database_users_test.go b/ovh/data_cloud_project_database_users_test.go new file mode 100644 index 000000000..1ddbb3616 --- /dev/null +++ b/ovh/data_cloud_project_database_users_test.go @@ -0,0 +1,69 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const testAccCloudProjectDatabaseUsersDatasourceConfig_Basic = ` +resource "ovh_cloud_project_database" "db" { + service_name = "%s" + engine = "%s" + version = "%s" + plan = "essential" + nodes { + region = "%s" + } + flavor = "%s" +} + +resource "ovh_cloud_project_database_user" "user" { + service_name = ovh_cloud_project_database.db.service_name + engine = ovh_cloud_project_database.db.engine + cluster_id = ovh_cloud_project_database.db.id + name = "%s" +} + +data "ovh_cloud_project_database_users" "users" { + service_name = ovh_cloud_project_database_user.user.service_name + engine = ovh_cloud_project_database_user.user.engine + cluster_id = ovh_cloud_project_database_user.user.cluster_id +} +` + +func TestAccCloudProjectDatabaseUsersDataSource_basic(t *testing.T) { + serviceName := os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST") + engine := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_ENGINE_TEST") + version := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_VERSION_TEST") + region := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_REGION_TEST") + flavor := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_FLAVOR_TEST") + + config := fmt.Sprintf( + testAccCloudProjectDatabaseUsersDatasourceConfig_Basic, + serviceName, + engine, + version, + region, + flavor, + "johndoe", + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckCloudDatabase(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_database_users.users", + "user_ids.#", + ), + ), + }, + }, + }) +} diff --git a/ovh/provider.go b/ovh/provider.go index 8e356f1c4..298fbbd08 100644 --- a/ovh/provider.go +++ b/ovh/provider.go @@ -46,9 +46,12 @@ func Provider() *schema.Provider { "ovh_cloud_project_containerregistries": dataSourceCloudProjectContainerRegistries(), "ovh_cloud_project_containerregistry": dataSourceCloudProjectContainerRegistry(), "ovh_cloud_project_containerregistry_users": dataSourceCloudProjectContainerRegistryUsers(), - "ovh_cloud_project_databases": dataSourceCloudProjectDatabases(), "ovh_cloud_project_database": dataSourceCloudProjectDatabase(), + "ovh_cloud_project_databases": dataSourceCloudProjectDatabases(), "ovh_cloud_project_database_ip_restrictions": dataSourceCloudProjectDatabaseIpRestrictions(), + "ovh_cloud_project_database_user": dataSourceCloudProjectDatabaseUser(), + "ovh_cloud_project_database_postgresql_user": dataSourceCloudProjectDatabasePostgresqlUser(), + "ovh_cloud_project_database_users": dataSourceCloudProjectDatabaseUsers(), "ovh_cloud_project_failover_ip_attach": dataSourceCloudProjectFailoverIpAttach(), "ovh_cloud_project_kube": dataSourceCloudProjectKube(), "ovh_cloud_project_kube_iprestrictions": dataSourceCloudProjectKubeIPRestrictions(), @@ -93,6 +96,8 @@ func Provider() *schema.Provider { "ovh_cloud_project_containerregistry_user": resourceCloudProjectContainerRegistryUser(), "ovh_cloud_project_database": resourceCloudProjectDatabase(), "ovh_cloud_project_database_ip_restriction": resourceCloudProjectDatabaseIpRestriction(), + "ovh_cloud_project_database_user": resourceCloudProjectDatabaseUser(), + "ovh_cloud_project_database_postgresql_user": resourceCloudProjectDatabasepPostgresqlUser(), "ovh_cloud_project_failover_ip_attach": resourceCloudProjectFailoverIpAttach(), "ovh_cloud_project_kube": resourceCloudProjectKube(), "ovh_cloud_project_kube_nodepool": resourceCloudProjectKubeNodePool(), diff --git a/ovh/provider_test.go b/ovh/provider_test.go index 0472044a3..3530e0329 100644 --- a/ovh/provider_test.go +++ b/ovh/provider_test.go @@ -122,9 +122,14 @@ func testAccPreCheckCloud(t *testing.T) { // Checks that the environment variables needed for the /cloud/project/{projectId}/database/ acceptance tests are set. func testAccPreCheckCloudDatabase(t *testing.T) { + testAccPreCheckCloudDatabaseNoEngine(t) + checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_DATABASE_ENGINE_TEST") +} + +// Checks that the environment variables needed for the /cloud/project/{projectId}/database// acceptance tests are set. +func testAccPreCheckCloudDatabaseNoEngine(t *testing.T) { testAccPreCheckCloud(t) testAccCheckCloudProjectExists(t) - checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_DATABASE_ENGINE_TEST") checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_DATABASE_VERSION_TEST") checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_DATABASE_REGION_TEST") checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_DATABASE_FLAVOR_TEST") diff --git a/ovh/resource_cloud_project_database_postgresql_user.go b/ovh/resource_cloud_project_database_postgresql_user.go new file mode 100644 index 000000000..dc0d58336 --- /dev/null +++ b/ovh/resource_cloud_project_database_postgresql_user.go @@ -0,0 +1,208 @@ +package ovh + +import ( + "fmt" + "log" + "net/url" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/ovh/terraform-provider-ovh/ovh/helpers" +) + +func resourceCloudProjectDatabasepPostgresqlUser() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudProjectDatabasePostgresqlUserCreate, + Read: resourceCloudProjectDatabasePostgresqlUserRead, + Delete: resourceCloudProjectDatabasePostgresqlUserDelete, + Update: resourceCloudProjectDatabasePostgresqlUserUpdate, + + Importer: &schema.ResourceImporter{ + State: resourceCloudProjectDatabasePostgresqlUserImportState, + }, + + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + }, + "cluster_id": { + Type: schema.TypeString, + Description: "Id of the database cluster", + ForceNew: true, + Required: true, + }, + "name": { + Type: schema.TypeString, + Description: "Name of the user", + ForceNew: true, + Required: true, + }, + "roles": { + Type: schema.TypeList, + Description: "Roles the user belongs to", + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + //Computed + "created_at": { + Type: schema.TypeString, + Description: "Date of the creation of the user", + Computed: true, + }, + "password": { + Type: schema.TypeString, + Description: "Password of the user", + Sensitive: true, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Description: "Current status of the user", + Computed: true, + }, + }, + } +} + +func resourceCloudProjectDatabasePostgresqlUserImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + givenId := d.Id() + splitId := strings.SplitN(givenId, "/", 3) + if len(splitId) != 3 { + return nil, fmt.Errorf("Import Id is not service_name/cluster_id/id formatted") + } + serviceName := splitId[0] + clusterId := splitId[1] + id := splitId[2] + d.SetId(id) + d.Set("cluster_id", clusterId) + d.Set("service_name", serviceName) + + results := make([]*schema.ResourceData, 1) + results[0] = d + return results, nil +} + +func resourceCloudProjectDatabasePostgresqlUserCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + clusterId := d.Get("cluster_id").(string) + + endpoint := fmt.Sprintf("/cloud/project/%s/database/postgresql/%s/user", + url.PathEscape(serviceName), + url.PathEscape(clusterId), + ) + params := (&CloudProjectDatabasePostgresqlUserCreateOpts{}).FromResource(d) + res := &CloudProjectDatabasePostgresqlUserResponse{} + + log.Printf("[DEBUG] Will create user: %+v for cluster %s from project %s", params, clusterId, serviceName) + err := config.OVHClient.Post(endpoint, params, res) + if err != nil { + return fmt.Errorf("calling Post %s with params %s:\n\t %q", endpoint, params, err) + } + + log.Printf("[DEBUG] Waiting for database %s to be READY", clusterId) + err = waitForCloudProjectDatabaseReady(config.OVHClient, serviceName, "postgresql", clusterId, 30*time.Second, 5*time.Second) + if err != nil { + return fmt.Errorf("timeout while waiting database %s to be READY: %v", clusterId, err) + } + log.Printf("[DEBUG] database %s is READY", clusterId) + + d.SetId(res.Id) + d.Set("password", res.Password) + + return resourceCloudProjectDatabasePostgresqlUserRead(d, meta) +} + +func resourceCloudProjectDatabasePostgresqlUserRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + clusterId := d.Get("cluster_id").(string) + id := d.Id() + + endpoint := fmt.Sprintf("/cloud/project/%s/database/postgresql/%s/user/%s", + url.PathEscape(serviceName), + url.PathEscape(clusterId), + url.PathEscape(id), + ) + res := &CloudProjectDatabasePostgresqlUserResponse{} + + log.Printf("[DEBUG] Will read user %s from cluster %s from project %s", id, clusterId, serviceName) + if err := config.OVHClient.Get(endpoint, res); err != nil { + return helpers.CheckDeleted(d, err, endpoint) + } + + for k, v := range res.ToMap() { + if k != "id" { + d.Set(k, v) + } else { + d.SetId(fmt.Sprint(v)) + } + } + + log.Printf("[DEBUG] Read user %+v", res) + return nil +} + +func resourceCloudProjectDatabasePostgresqlUserUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + clusterId := d.Get("cluster_id").(string) + id := d.Id() + + endpoint := fmt.Sprintf("/cloud/project/%s/database/postgresql/%s/user/%s", + url.PathEscape(serviceName), + url.PathEscape(clusterId), + url.PathEscape(id), + ) + params := (&CloudProjectDatabasePostgresqlUserUpdateOpts{}).FromResource(d) + + log.Printf("[DEBUG] Will update user: %+v from cluster %s from project %s", params, clusterId, serviceName) + err := config.OVHClient.Put(endpoint, params, nil) + if err != nil { + return fmt.Errorf("calling Put %s with params %v:\n\t %q", endpoint, params, err) + } + + log.Printf("[DEBUG] Waiting for database %s to be READY", clusterId) + err = waitForCloudProjectDatabaseReady(config.OVHClient, serviceName, "postgresql", clusterId, 20*time.Second, 1*time.Second) + if err != nil { + return fmt.Errorf("timeout while waiting database %s to be READY: %v", clusterId, err) + } + log.Printf("[DEBUG] database %s is READY", clusterId) + + return resourceCloudProjectDatabasePostgresqlUserRead(d, meta) +} + +func resourceCloudProjectDatabasePostgresqlUserDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + clusterId := d.Get("cluster_id").(string) + id := d.Id() + + endpoint := fmt.Sprintf("/cloud/project/%s/database/postgresql/%s/user/%s", + url.PathEscape(serviceName), + url.PathEscape(clusterId), + url.PathEscape(id), + ) + + log.Printf("[DEBUG] Will delete useruser %s from cluster %s from project %s", id, clusterId, serviceName) + err := config.OVHClient.Delete(endpoint, nil) + if err != nil { + return helpers.CheckDeleted(d, err, endpoint) + } + + log.Printf("[DEBUG] Waiting for database %s to be READY", clusterId) + err = waitForCloudProjectDatabaseReady(config.OVHClient, serviceName, "postgresql", clusterId, 20*time.Second, 1*time.Second) + if err != nil { + return fmt.Errorf("timeout while waiting database %s to be READY: %v", clusterId, err) + } + log.Printf("[DEBUG] database %s is READY", clusterId) + + d.SetId("") + + return nil +} diff --git a/ovh/resource_cloud_project_database_postgresql_user_test.go b/ovh/resource_cloud_project_database_postgresql_user_test.go new file mode 100644 index 000000000..0885b290d --- /dev/null +++ b/ovh/resource_cloud_project_database_postgresql_user_test.go @@ -0,0 +1,72 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const testAccCloudProjectDatabasePostgresqlUserConfig = ` +resource "ovh_cloud_project_database" "db" { + service_name = "%s" + engine = "postgresql" + version = "%s" + plan = "essential" + nodes { + region = "%s" + } + flavor = "%s" +} + +resource "ovh_cloud_project_database_postgresql_user" "user" { + service_name = ovh_cloud_project_database.db.service_name + cluster_id = ovh_cloud_project_database.db.id + name = "%s" + roles = ["replication"] +} +` + +func TestAccCloudProjectDatabasePostgresqlUser_basic(t *testing.T) { + serviceName := os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST") + version := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_POSTGRESQL_VERSION_TEST") + if version == "" { + version = os.Getenv("OVH_CLOUD_PROJECT_DATABASE_VERSION_TEST") + } + region := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_REGION_TEST") + flavor := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_FLAVOR_TEST") + name := "johndoe" + + config := fmt.Sprintf( + testAccCloudProjectDatabasePostgresqlUserConfig, + serviceName, + version, + region, + flavor, + name, + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckCloudDatabaseNoEngine(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet( + "ovh_cloud_project_database_postgresql_user.user", "created_at"), + resource.TestCheckResourceAttrSet( + "ovh_cloud_project_database_postgresql_user.user", "password"), + resource.TestCheckResourceAttrSet( + "ovh_cloud_project_database_postgresql_user.user", "roles.#"), + resource.TestCheckResourceAttrSet( + "ovh_cloud_project_database_postgresql_user.user", "status"), + resource.TestCheckResourceAttr( + "ovh_cloud_project_database_postgresql_user.user", "name", name, + ), + ), + }, + }, + }) +} diff --git a/ovh/resource_cloud_project_database_user.go b/ovh/resource_cloud_project_database_user.go new file mode 100644 index 000000000..f773bfbd6 --- /dev/null +++ b/ovh/resource_cloud_project_database_user.go @@ -0,0 +1,202 @@ +package ovh + +import ( + "fmt" + "log" + "net/url" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/ovh/terraform-provider-ovh/ovh/helpers" +) + +func resourceCloudProjectDatabaseUser() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudProjectDatabaseUserCreate, + Read: resourceCloudProjectDatabaseUserRead, + Delete: resourceCloudProjectDatabaseUserDelete, + + Importer: &schema.ResourceImporter{ + State: resourceCloudProjectDatabaseUserImportState, + }, + + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + }, + "engine": { + Type: schema.TypeString, + Description: "Name of the engine of the service", + ForceNew: true, + Required: true, + }, + "cluster_id": { + Type: schema.TypeString, + Description: "Id of the database cluster", + ForceNew: true, + Required: true, + }, + "name": { + Type: schema.TypeString, + Description: "Name of the user", + ForceNew: true, + Required: true, + }, + + //Computed + "created_at": { + Type: schema.TypeString, + Description: "Date of the creation of the user", + Computed: true, + }, + "password": { + Type: schema.TypeString, + Description: "Password of the user", + Sensitive: true, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Description: "Current status of the user", + Computed: true, + }, + }, + } +} + +func resourceCloudProjectDatabaseUserImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + givenId := d.Id() + splitId := strings.SplitN(givenId, "/", 4) + if len(splitId) != 3 { + return nil, fmt.Errorf("Import Id is not service_name/engine/cluster_id/id formatted") + } + serviceName := splitId[0] + engine := splitId[1] + clusterId := splitId[2] + id := splitId[3] + d.SetId(id) + d.Set("cluster_id", clusterId) + d.Set("engine", engine) + d.Set("service_name", serviceName) + + results := make([]*schema.ResourceData, 1) + results[0] = d + return results, nil +} + +func resourceCloudProjectDatabaseUserCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + engine := d.Get("engine").(string) + clusterId := d.Get("cluster_id").(string) + + endpoint := fmt.Sprintf("/cloud/project/%s/database/%s/%s/user", + url.PathEscape(serviceName), + url.PathEscape(engine), + url.PathEscape(clusterId), + ) + + err := mustGenericEngineUserEndpoint(engine) + if err != nil { + return fmt.Errorf("Calling Post %s :\n\t %q", endpoint, err) + } + + params := (&CloudProjectDatabaseUserCreateOpts{}).FromResource(d) + res := &CloudProjectDatabaseUserResponse{} + + log.Printf("[DEBUG] Will create user: %+v for cluster %s from project %s", params, clusterId, serviceName) + err = config.OVHClient.Post(endpoint, params, res) + if err != nil { + return fmt.Errorf("calling Post %s with params %s:\n\t %q", endpoint, params, err) + } + + log.Printf("[DEBUG] Waiting for database %s to be READY", clusterId) + err = waitForCloudProjectDatabaseReady(config.OVHClient, serviceName, engine, clusterId, 30*time.Second, 5*time.Second) + if err != nil { + return fmt.Errorf("timeout while waiting database %s to be READY: %v", clusterId, err) + } + log.Printf("[DEBUG] database %s is READY", clusterId) + + d.SetId(res.Id) + d.Set("password", res.Password) + + return resourceCloudProjectDatabaseUserRead(d, meta) +} + +func resourceCloudProjectDatabaseUserRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + engine := d.Get("engine").(string) + clusterId := d.Get("cluster_id").(string) + id := d.Id() + + endpoint := fmt.Sprintf("/cloud/project/%s/database/%s/%s/user/%s", + url.PathEscape(serviceName), + url.PathEscape(engine), + url.PathEscape(clusterId), + url.PathEscape(id), + ) + + err := mustGenericEngineUserEndpoint(engine) + if err != nil { + return fmt.Errorf("Calling Get %s :\n\t %q", endpoint, err) + } + + res := &CloudProjectDatabaseUserResponse{} + + log.Printf("[DEBUG] Will read user %s from cluster %s from project %s", id, clusterId, serviceName) + if err := config.OVHClient.Get(endpoint, res); err != nil { + return helpers.CheckDeleted(d, err, endpoint) + } + + for k, v := range res.ToMap() { + if k != "id" { + d.Set(k, v) + } else { + d.SetId(fmt.Sprint(v)) + } + } + + return nil +} + +func resourceCloudProjectDatabaseUserDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + engine := d.Get("engine").(string) + clusterId := d.Get("cluster_id").(string) + id := d.Id() + + endpoint := fmt.Sprintf("/cloud/project/%s/database/%s/%s/user/%s", + url.PathEscape(serviceName), + url.PathEscape(engine), + url.PathEscape(clusterId), + url.PathEscape(id), + ) + + err := mustGenericEngineUserEndpoint(engine) + if err != nil { + return fmt.Errorf("Calling Delete %s :\n\t %q", endpoint, err) + } + + log.Printf("[DEBUG] Will delete user %s from cluster %s from project %s", id, clusterId, serviceName) + err = config.OVHClient.Delete(endpoint, nil) + if err != nil { + return helpers.CheckDeleted(d, err, endpoint) + } + + log.Printf("[DEBUG] Waiting for database %s to be READY", clusterId) + err = waitForCloudProjectDatabaseReady(config.OVHClient, serviceName, engine, clusterId, 20*time.Second, 1*time.Second) + if err != nil { + return fmt.Errorf("timeout while waiting database %s to be READY: %v", clusterId, err) + } + log.Printf("[DEBUG] database %s is READY", clusterId) + + d.SetId("") + + return nil +} diff --git a/ovh/resource_cloud_project_database_user_test.go b/ovh/resource_cloud_project_database_user_test.go new file mode 100644 index 000000000..cc2e5fd1d --- /dev/null +++ b/ovh/resource_cloud_project_database_user_test.go @@ -0,0 +1,69 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const testAccCloudProjectDatabaseUserConfig = ` +resource "ovh_cloud_project_database" "db" { + service_name = "%s" + engine = "%s" + version = "%s" + plan = "essential" + nodes { + region = "%s" + } + flavor = "%s" +} + +resource "ovh_cloud_project_database_user" "user" { + service_name = ovh_cloud_project_database.db.service_name + engine = ovh_cloud_project_database.db.engine + cluster_id = ovh_cloud_project_database.db.id + name = "%s" +} +` + +func TestAccCloudProjectDatabaseUser_basic(t *testing.T) { + serviceName := os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST") + engine := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_ENGINE_TEST") + version := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_VERSION_TEST") + region := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_REGION_TEST") + flavor := os.Getenv("OVH_CLOUD_PROJECT_DATABASE_FLAVOR_TEST") + name := "johndoe" + + config := fmt.Sprintf( + testAccCloudProjectDatabaseUserConfig, + serviceName, + engine, + version, + region, + flavor, + name, + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckCloudDatabase(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet( + "ovh_cloud_project_database_user.user", "created_at"), + resource.TestCheckResourceAttrSet( + "ovh_cloud_project_database_user.user", "password"), + resource.TestCheckResourceAttrSet( + "ovh_cloud_project_database_user.user", "status"), + resource.TestCheckResourceAttr( + "ovh_cloud_project_database_user.user", "name", name, + ), + ), + }, + }, + }) +} diff --git a/ovh/types_cloud_project_database.go b/ovh/types_cloud_project_database.go index 64b333483..c9f9267d0 100644 --- a/ovh/types_cloud_project_database.go +++ b/ovh/types_cloud_project_database.go @@ -12,35 +12,6 @@ import ( "github.com/ovh/terraform-provider-ovh/ovh/helpers" ) -type CloudProjectDatabaseCreateOpts struct { - Description *string `json:"description,omitempty"` - NetworkId *string `json:"networkId,omitempty"` - NodesPattern CloudProjectDatabaseNodesPattern `json:"nodesPattern,omitempty"` - Plan string `json:"plan"` - SubnetId *string `json:"subnetId,omitempty"` - Version string `json:"version"` -} - -type CloudProjectDatabaseUpdateOpts struct { - Description string `json:"description,omitempty"` - Flavor string `json:"flavor,omitempty"` - NodeNumber int `json:"nodeNumber,omitempty"` - Plan string `json:"plan,omitempty"` - Version string `json:"version,omitempty"` -} - -type CloudProjectDatabaseNodesPattern struct { - Flavor string `json:"flavor"` - Number int `json:"number"` - Region string `json:"region"` -} - -type CloudProjectDatabaseNodes struct { - NetworkId *string `json:"networkId,omitempty"` - Region string `json:"region"` - SubnetId *string `json:"subnetId,omitempty"` -} - type CloudProjectDatabaseResponse struct { BackupTime string `json:"backupTime"` CreatedAt string `json:"createdAt"` @@ -59,70 +30,8 @@ type CloudProjectDatabaseResponse struct { Version string `json:"version"` } -type CloudProjectDatabaseEndpoint struct { - Component string `json:"component"` - Domain string `json:"domain"` - Path *string `json:"path,omitempty"` - Port *int `json:"port,omitempty"` - Scheme *string `json:"scheme,omitempty"` - Ssl *bool `json:"ssl,omitempty"` - SslMode *string `json:"sslMode,omitempty"` - Uri *string `json:"uri,omitempty"` -} - -func (opts *CloudProjectDatabaseCreateOpts) FromResource(d *schema.ResourceData) (error, *CloudProjectDatabaseCreateOpts) { - opts.Description = helpers.GetNilStringPointerFromData(d, "description") - opts.Plan = d.Get("plan").(string) - - nodes := []CloudProjectDatabaseNodes{} - nbOfNodes := d.Get("nodes.#").(int) - for i := 0; i < nbOfNodes; i++ { - nodes = append(nodes, *(&CloudProjectDatabaseNodes{}).FromResourceWithPath(d, fmt.Sprintf("nodes.%d", i))) - } - - if err := checkNodesEquality(nodes); err != nil { - return err, nil - } - - opts.NodesPattern = CloudProjectDatabaseNodesPattern{ - Flavor: d.Get("flavor").(string), - Region: nodes[0].Region, - Number: nbOfNodes, - } - - opts.NetworkId = nodes[0].NetworkId - opts.SubnetId = nodes[0].SubnetId - opts.Version = d.Get("version").(string) - - return nil, opts -} - -func (opts *CloudProjectDatabaseNodes) FromResourceWithPath(d *schema.ResourceData, path string) *CloudProjectDatabaseNodes { - opts.Region = d.Get(fmt.Sprintf("%s.region", path)).(string) - opts.NetworkId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.network_id", path)) - opts.SubnetId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.subnet_id", path)) - - return opts -} - -func (opts *CloudProjectDatabaseUpdateOpts) FromResource(d *schema.ResourceData) (error, *CloudProjectDatabaseUpdateOpts) { - opts.Description = d.Get("description").(string) - opts.Plan = d.Get("plan").(string) - - nodes := []CloudProjectDatabaseNodes{} - nbOfNodes := d.Get("nodes.#").(int) - for i := 0; i < nbOfNodes; i++ { - nodes = append(nodes, *(&CloudProjectDatabaseNodes{}).FromResourceWithPath(d, fmt.Sprintf("nodes.%d", i))) - } - - if err := checkNodesEquality(nodes); err != nil { - return err, nil - } - opts.Flavor = d.Get("flavor").(string) - opts.NodeNumber = nbOfNodes - opts.Version = d.Get("version").(string) - - return nil, opts +func (s *CloudProjectDatabaseResponse) String() string { + return fmt.Sprintf("%s(%s): %s", s.Description, s.Id, s.Status) } func (v CloudProjectDatabaseResponse) ToMap() map[string]interface{} { @@ -160,20 +69,15 @@ func (v CloudProjectDatabaseResponse) ToMap() map[string]interface{} { return obj } -func (v CloudProjectDatabaseNodes) ToMap() map[string]interface{} { - obj := make(map[string]interface{}) - - if v.NetworkId != nil { - obj["network_id"] = v.NetworkId - } - - obj["region"] = v.Region - - if v.SubnetId != nil { - obj["subnet_id"] = v.SubnetId - } - - return obj +type CloudProjectDatabaseEndpoint struct { + Component string `json:"component"` + Domain string `json:"domain"` + Path *string `json:"path,omitempty"` + Port *int `json:"port,omitempty"` + Scheme *string `json:"scheme,omitempty"` + Ssl *bool `json:"ssl,omitempty"` + SslMode *string `json:"sslMode,omitempty"` + Uri *string `json:"uri,omitempty"` } func (v CloudProjectDatabaseEndpoint) ToMap() map[string]interface{} { @@ -209,14 +113,110 @@ func (v CloudProjectDatabaseEndpoint) ToMap() map[string]interface{} { return obj } -func (s *CloudProjectDatabaseResponse) String() string { - return fmt.Sprintf("%s(%s): %s", s.Description, s.Id, s.Status) +type CloudProjectDatabaseNodes struct { + NetworkId *string `json:"networkId,omitempty"` + Region string `json:"region"` + SubnetId *string `json:"subnetId,omitempty"` +} + +func (opts *CloudProjectDatabaseNodes) FromResourceWithPath(d *schema.ResourceData, path string) *CloudProjectDatabaseNodes { + opts.Region = d.Get(fmt.Sprintf("%s.region", path)).(string) + opts.NetworkId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.network_id", path)) + opts.SubnetId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.subnet_id", path)) + + return opts +} + +func (v CloudProjectDatabaseNodes) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + + if v.NetworkId != nil { + obj["network_id"] = v.NetworkId + } + + obj["region"] = v.Region + + if v.SubnetId != nil { + obj["subnet_id"] = v.SubnetId + } + + return obj +} + +type CloudProjectDatabaseCreateOpts struct { + Description *string `json:"description,omitempty"` + NetworkId *string `json:"networkId,omitempty"` + NodesPattern CloudProjectDatabaseNodesPattern `json:"nodesPattern,omitempty"` + Plan string `json:"plan"` + SubnetId *string `json:"subnetId,omitempty"` + Version string `json:"version"` +} + +type CloudProjectDatabaseNodesPattern struct { + Flavor string `json:"flavor"` + Number int `json:"number"` + Region string `json:"region"` +} + +func (opts *CloudProjectDatabaseCreateOpts) FromResource(d *schema.ResourceData) (error, *CloudProjectDatabaseCreateOpts) { + opts.Description = helpers.GetNilStringPointerFromData(d, "description") + opts.Plan = d.Get("plan").(string) + + nodes := []CloudProjectDatabaseNodes{} + nbOfNodes := d.Get("nodes.#").(int) + for i := 0; i < nbOfNodes; i++ { + nodes = append(nodes, *(&CloudProjectDatabaseNodes{}).FromResourceWithPath(d, fmt.Sprintf("nodes.%d", i))) + } + + if err := checkNodesEquality(nodes); err != nil { + return err, nil + } + + opts.NodesPattern = CloudProjectDatabaseNodesPattern{ + Flavor: d.Get("flavor").(string), + Region: nodes[0].Region, + Number: nbOfNodes, + } + + opts.NetworkId = nodes[0].NetworkId + opts.SubnetId = nodes[0].SubnetId + opts.Version = d.Get("version").(string) + + return nil, opts } func (s *CloudProjectDatabaseCreateOpts) String() string { return fmt.Sprintf("%s: %s", *s.Description, s.Version) } +type CloudProjectDatabaseUpdateOpts struct { + Description string `json:"description,omitempty"` + Flavor string `json:"flavor,omitempty"` + NodeNumber int `json:"nodeNumber,omitempty"` + Plan string `json:"plan,omitempty"` + Version string `json:"version,omitempty"` +} + +func (opts *CloudProjectDatabaseUpdateOpts) FromResource(d *schema.ResourceData) (error, *CloudProjectDatabaseUpdateOpts) { + opts.Description = d.Get("description").(string) + opts.Plan = d.Get("plan").(string) + + nodes := []CloudProjectDatabaseNodes{} + nbOfNodes := d.Get("nodes.#").(int) + for i := 0; i < nbOfNodes; i++ { + nodes = append(nodes, *(&CloudProjectDatabaseNodes{}).FromResourceWithPath(d, fmt.Sprintf("nodes.%d", i))) + } + + if err := checkNodesEquality(nodes); err != nil { + return err, nil + } + opts.Flavor = d.Get("flavor").(string) + opts.NodeNumber = nbOfNodes + opts.Version = d.Get("version").(string) + + return nil, opts +} + // This make sure Nodes are homogenous. // When multi region cluster will be available the check will be done on API side func checkNodesEquality(nodes []CloudProjectDatabaseNodes) error { @@ -356,3 +356,116 @@ func (opts *CloudProjectDatabaseIpRestrictionUpdateOpts) FromResource(d *schema. opts.Description = d.Get("description").(string) return opts } + +var ( + genericEngineUserEndpoint = map[string]struct{}{ + "cassandra": {}, + "mysql": {}, + "kafka": {}, + "kafkaConnect": {}, + } +) + +type CloudProjectDatabaseUserResponse struct { + CreatedAt string `json:"createdAt"` + Id string `json:"id"` + Password string `json:"password"` + Status string `json:"status"` + Username string `json:"username"` +} + +func (p *CloudProjectDatabaseUserResponse) String() string { + return fmt.Sprintf( + "Id: %s, User: %s, Status: %s", + p.Id, + p.Username, + p.Status, + ) +} + +func (v CloudProjectDatabaseUserResponse) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + + obj["created_at"] = v.CreatedAt + obj["id"] = v.Id + obj["name"] = v.Username + obj["status"] = v.Status + + return obj +} + +type CloudProjectDatabaseUserCreateOpts struct { + Name string `json:"name"` +} + +func (opts *CloudProjectDatabaseUserCreateOpts) FromResource(d *schema.ResourceData) *CloudProjectDatabaseUserCreateOpts { + opts.Name = d.Get("name").(string) + return opts +} + +func mustGenericEngineUserEndpoint(engine string) error { + if _, ok := genericEngineUserEndpoint[engine]; !ok { + return fmt.Errorf("Engine %s do not use the generic user endpoint", engine) + } + return nil +} + +type CloudProjectDatabasePostgresqlUserResponse struct { + CreatedAt string `json:"createdAt"` + Id string `json:"id"` + Password string `json:"password"` + Roles []string `json:"roles"` + Status string `json:"status"` + Username string `json:"username"` +} + +func (p *CloudProjectDatabasePostgresqlUserResponse) String() string { + return fmt.Sprintf( + "Id: %s, User: %s, Status: %s", + p.Id, + p.Username, + p.Status, + ) +} + +func (v CloudProjectDatabasePostgresqlUserResponse) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + + obj["created_at"] = v.CreatedAt + obj["id"] = v.Id + obj["name"] = v.Username + obj["roles"] = v.Roles + obj["status"] = v.Status + + return obj +} + +type CloudProjectDatabasePostgresqlUserCreateOpts struct { + Name string `json:"name"` + Roles []string `json:"roles"` +} + +func (opts *CloudProjectDatabasePostgresqlUserCreateOpts) FromResource(d *schema.ResourceData) *CloudProjectDatabasePostgresqlUserCreateOpts { + opts.Name = d.Get("name").(string) + roles := d.Get("roles").([]interface{}) + opts.Roles = make([]string, len(roles)) + for i, e := range roles { + if e != nil { + opts.Roles[i] = e.(string) + } + } + return opts +} + +type CloudProjectDatabasePostgresqlUserUpdateOpts struct { + Roles []string `json:"roles"` +} + +func (opts *CloudProjectDatabasePostgresqlUserUpdateOpts) FromResource(d *schema.ResourceData) *CloudProjectDatabasePostgresqlUserUpdateOpts { + roles := d.Get("roles").([]interface{}) + opts.Roles = make([]string, len(roles)) + for i, e := range roles { + opts.Roles[i] = e.(string) + } + return opts +} diff --git a/website/docs/d/cloud_project_database_postgresql_user.html.markdown b/website/docs/d/cloud_project_database_postgresql_user.html.markdown new file mode 100644 index 000000000..8ebb35d6f --- /dev/null +++ b/website/docs/d/cloud_project_database_postgresql_user.html.markdown @@ -0,0 +1,44 @@ +--- +layout: "ovh" +page_title: "OVH: cloud_project_database_postgresql_user" +sidebar_current: "docs-ovh-datasource-cloud-project-database-postgresql-user" +description: |- + Get information about a user of a postgresql cluster associated with a public cloud project. +--- + +# cloud_project_database_postgresql_user (Data Source) + +Use this data source to get information about a user of a postgresql cluster associated with a public cloud project. + +## Example Usage + +```hcl +data "ovh_cloud_project_database_postgresql_user" "pguser" { + service_name = "XXX" + cluster_id = "YYY" + name = "ZZZ" +} + +output "pguser_roles" { + value = data.ovh_cloud_project_database_postgresql_user.pguser.roles +} +``` + +## Argument Reference + +* `service_name` - The id of the public cloud project. If omitted, + the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. + +* `cluster_id` - Cluster ID + +* `name` - Name of the user. + +## Attributes Reference + +The following attributes are exported: + +* `created_at` - Date of the creation of the user. +* `id` - ID of the user. +* `roles` - Roles the user belongs to. +* `status` - Current status of the user. +* `name` - Name of the user. diff --git a/website/docs/d/cloud_project_database_user.html.markdown b/website/docs/d/cloud_project_database_user.html.markdown new file mode 100644 index 000000000..6b4f55505 --- /dev/null +++ b/website/docs/d/cloud_project_database_user.html.markdown @@ -0,0 +1,52 @@ +--- +layout: "ovh" +page_title: "OVH: cloud_project_database_user" +sidebar_current: "docs-ovh-datasource-cloud-project-database-user" +description: |- + Get information about a user of a database cluster associated with a public cloud project. +--- + +# cloud_project_database_user (Data Source) + +Use this data source to get information about a user of a database cluster associated with a public cloud project. + +## Example Usage + +```hcl +data "ovh_cloud_project_database_user" "user" { + service_name = "XXX" + engine = "YYY" + cluster_id = "ZZZ" + name = "UUU" +} + +output "user_name" { + value = data.ovh_cloud_project_database_user.user.name +} +``` + +## Argument Reference + +* `service_name` - The id of the public cloud project. If omitted, + the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. + +* `engine` - The engine of the database cluster you want user information. To get a full list of available engine visit : +[public documentation](https://docs.ovh.com/gb/en/publiccloud/databases).\ +Available engines for this resource (other have specific resource): + * `cassandra` + * `kafka` + * `kafkaConnect` + * `mysql` + +* `cluster_id` - Cluster ID + +* `name` - Name of the user. + +## Attributes Reference + +The following attributes are exported: + +* `created_at` - Date of the creation of the user. +* `id` - ID of the user. +* `status` - Current status of the user. +* `name` - Name of the user. diff --git a/website/docs/d/cloud_project_database_users.html.markdown b/website/docs/d/cloud_project_database_users.html.markdown new file mode 100644 index 000000000..cd7072fa3 --- /dev/null +++ b/website/docs/d/cloud_project_database_users.html.markdown @@ -0,0 +1,41 @@ +--- +layout: "ovh" +page_title: "OVH: cloud_project_database_users" +sidebar_current: "docs-ovh-datasource-cloud-project-database-users" +description: |- + Get the list of users of a database cluster associated with a public cloud project. +--- + +# cloud_project_database_users (Data Source) + +Use this data source to get the list of users of a database cluster associated with a public cloud project. + +## Example Usage + +```hcl +data "ovh_cloud_project_database_users" "users" { + service_name = "XXXX" + engine = "YYYY" + cluster_id = "ZZZ" +} + +output "user_ids" { + value = data.ovh_cloud_project_database_users.users.user_ids +} +``` + +## Argument Reference + +* `service_name` - The id of the public cloud project. If omitted, + the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. + +* `engine` - The engine of the database cluster you want to list users. To get a full list of available engine visit. +[public documentation](https://docs.ovh.com/gb/en/publiccloud/databases). + +* `cluster_id` - Cluster ID + +## Attributes Reference + +The following attributes are exported: + +* `user_ids` - The list of users ids of the database cluster associated with the project. diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index f7ce5641c..0a3e7526c 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -125,6 +125,8 @@ variables must also be set: * `OVH_CLOUD_PROJECT_DATABASE_VERSION_TEST` - The version of the database engine to test. +* `OVH_CLOUD_PROJECT_DATABASE_POSTGRESQL_VERSION_TEST` - The version of the postgresql to test. if not set `OVH_CLOUD_PROJECT_DATABASE_VERSION_TEST` is use. + * `OVH_CLOUD_PROJECT_DATABASE_REGION_TEST` - The region of the database service to test. * `OVH_CLOUD_PROJECT_DATABASE_FLAVOR_TEST` - The node flavor of the database service to test. diff --git a/website/docs/r/cloud_project_database_postgresql_user.html.markdown b/website/docs/r/cloud_project_database_postgresql_user.html.markdown new file mode 100644 index 000000000..085893d94 --- /dev/null +++ b/website/docs/r/cloud_project_database_postgresql_user.html.markdown @@ -0,0 +1,62 @@ +--- +layout: "ovh" +page_title: "OVH: cloud_project_database_postgresql_user" +sidebar_current: "docs-ovh-resource-cloud-project-database-postgresql-user" +description: |- + Creates an user for a postgresql cluster associated with a public cloud project. +--- + +# cloud_project_database_postgresql_user + +Creates an user for a postgresql cluster associated with a public cloud project. + +## Example Usage + +```hcl +data "ovh_cloud_project_database" "postgresql" { + service_name = "XXXX" + engine = "postgresql" + cluster_id = "ZZZZ" +} + +resource "ovh_cloud_project_database_postgresql_user" "user" { + service_name = ovh_cloud_project_database.postgresql.service_name + cluster_id = ovh_cloud_project_database.postgresql.id + name = "johndoe" + roles = ["replication"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `service_name` - The id of the public cloud project. If omitted, + the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. + +* `cluster_id` - Cluster ID. + +* `name` - Name of the user. + +* `roles` - (Optional: if omit, default role) Roles the user belongs to. Possible values: + * `["replication"]` + * `[]` (default role) + + +## Attributes Reference + +The following attributes are exported: + +* `created_at` - Date of the creation of the user. +* `id` - Public Cloud Database Service ID. +* `password` - Password of the user. +* `roles` - Roles the user belongs to. +* `status` - Current status of the user. +* `name` - See Argument Reference above. + +## Import + +OVHcloud Managed postgresql clusters users can be imported using the `service_name`, `cluster_id` and `id` of the user, separated by "/" E.g., + +``` +$ terraform import ovh_cloud_project_database_postgresql_user.my_user // \ No newline at end of file diff --git a/website/docs/r/cloud_project_database_user.html.markdown b/website/docs/r/cloud_project_database_user.html.markdown new file mode 100644 index 000000000..6c5c4840b --- /dev/null +++ b/website/docs/r/cloud_project_database_user.html.markdown @@ -0,0 +1,71 @@ +--- +layout: "ovh" +page_title: "OVH: cloud_project_database_user" +sidebar_current: "docs-ovh-resource-cloud-project-database-user" +description: |- + Creates an user for a database cluster associated with a public cloud project. +--- + +# cloud_project_database_user + +Creates an user for a database cluster associated with a public cloud project. + +With this resource you can create a user for the following database engine: + + * `cassandra` + * `kafka` + * `kafkaConnect` + * `mysql` + +## Example Usage + +```hcl +data "ovh_cloud_project_database" "db" { + service_name = "XXXX" + engine = "YYYY" + cluster_id = "ZZZZ" +} + +resource "ovh_cloud_project_database_user" "user" { + service_name = ovh_cloud_project_database.db.service_name + engine = ovh_cloud_project_database.db.engine + cluster_id = ovh_cloud_project_database.db.id + name = "johndoe" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `service_name` - The id of the public cloud project. If omitted, + the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. + +* `engine` - The engine of the database cluster you want to add. To get a full list of available engine visit : +[public documentation](https://docs.ovh.com/gb/en/publiccloud/databases).\ +Available engines for this resource (other have specific resource): + * `cassandra` + * `kafka` + * `kafkaConnect` + * `mysql` + +* `cluster_id` - Cluster ID. + +* `name` - Name of the user. + +## Attributes Reference + +The following attributes are exported: + +* `created_at` - Date of the creation of the user. +* `id` - Public Cloud Database Service ID. +* `password` - Password of the user. +* `status` - Current status of the user. +* `name` - See Argument Reference above. + +## Import + +OVHcloud Managed database clusters users can be imported using the `service_name`, `engine`, `cluster_id` and `id` of the user, separated by "/" E.g., + +``` +$ terraform import ovh_cloud_project_database_user.my_user /// \ No newline at end of file