From a9708b5cfde914e1e1fd6676b0ef816294cc196e Mon Sep 17 00:00:00 2001 From: ecmadao Date: Tue, 25 Feb 2025 17:22:32 +0800 Subject: [PATCH] chore: optimize instance resource (#98) * chore: optimize instance resource * chore: update docs * chore: update docs * fix: lint --- VERSION | 2 +- api/client.go | 2 +- api/setting.go | 2 - client/database.go | 4 +- docs/data-sources/instance.md | 53 ++++++ docs/data-sources/instance_list.md | 50 ++++- docs/data-sources/setting.md | 21 --- docs/resources/instance.md | 55 +++++- docs/resources/setting.md | 19 -- examples/environments/main.tf | 2 +- examples/groups/main.tf | 2 +- examples/instances/main.tf | 8 +- examples/policies/main.tf | 2 +- examples/projects/main.tf | 2 +- examples/roles/main.tf | 2 +- examples/settings/main.tf | 10 +- examples/setup/approval_flow.tf | 19 -- examples/setup/instance.tf | 30 ++- examples/setup/main.tf | 2 +- examples/setup/project.tf | 4 +- examples/setup/users.tf | 3 +- examples/users/main.tf | 2 +- examples/vcs/main.tf | 2 +- provider/data_source_database_list.go | 2 +- provider/data_source_instance.go | 124 +++++++++++- provider/data_source_instance_list.go | 24 +-- provider/data_source_project.go | 2 +- provider/data_source_project_list.go | 2 +- provider/data_source_setting.go | 74 +------- provider/internal/mock_client.go | 2 +- provider/resource_instance.go | 262 +++++++++++++++++++++++++- provider/resource_project.go | 2 +- provider/resource_setting.go | 45 +---- 33 files changed, 596 insertions(+), 241 deletions(-) diff --git a/VERSION b/VERSION index c678b02..e7ad390 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.18 \ No newline at end of file +1.0.19 \ No newline at end of file diff --git a/api/client.go b/api/client.go index a561dfb..69d0b74 100644 --- a/api/client.go +++ b/api/client.go @@ -56,7 +56,7 @@ type Client interface { // GetDatabase gets the database by instance resource id and the database name. GetDatabase(ctx context.Context, databaseName string) (*v1pb.Database, error) // ListDatabase list the databases. - ListDatabase(ctx context.Context, instanceID, filter string) ([]*v1pb.Database, error) + ListDatabase(ctx context.Context, instanceID, filter string, listAll bool) ([]*v1pb.Database, error) // UpdateDatabase patches the database. UpdateDatabase(ctx context.Context, patch *v1pb.Database, updateMasks []string) (*v1pb.Database, error) // BatchUpdateDatabases batch updates databases. diff --git a/api/setting.go b/api/setting.go index b1e9b08..b71ca0e 100644 --- a/api/setting.go +++ b/api/setting.go @@ -8,8 +8,6 @@ const ( SettingWorkspaceApproval SettingName = "bb.workspace.approval" // SettingWorkspaceProfile is the setting name for workspace profile settings. SettingWorkspaceProfile SettingName = "bb.workspace.profile" - // SettingWorkspaceExternalApproval is the setting name for workspace external approval config. - SettingWorkspaceExternalApproval SettingName = "bb.workspace.approval.external" // SettingDataClassification is the setting name for data classification. SettingDataClassification SettingName = "bb.workspace.data-classification" // SettingSemanticTypes is the setting name for semantic types. diff --git a/client/database.go b/client/database.go index 65bb474..cc8c74c 100644 --- a/client/database.go +++ b/client/database.go @@ -29,7 +29,7 @@ func (c *client) GetDatabase(ctx context.Context, databaseName string) (*v1pb.Da } // ListDatabase list all databases. -func (c *client) ListDatabase(ctx context.Context, parent, filter string) ([]*v1pb.Database, error) { +func (c *client) ListDatabase(ctx context.Context, parent, filter string, listAll bool) ([]*v1pb.Database, error) { res := []*v1pb.Database{} pageToken := "" startTime := time.Now() @@ -47,7 +47,7 @@ func (c *client) ListDatabase(ctx context.Context, parent, filter string) ([]*v1 }) pageToken = resp.NextPageToken - if pageToken == "" { + if pageToken == "" || !listAll { break } } diff --git a/docs/data-sources/instance.md b/docs/data-sources/instance.md index 5c71752..90c7b43 100644 --- a/docs/data-sources/instance.md +++ b/docs/data-sources/instance.md @@ -19,8 +19,13 @@ The instance data source. - `resource_id` (String) The instance unique resource id. +### Optional + +- `list_all_databases` (Boolean) List all databases in this instance. If false, will only list 500 databases. + ### Read-Only +- `activation` (Boolean) Whether assign license for this instance or not. - `data_sources` (Set of Object) (see [below for nested schema](#nestedatt--data_sources)) - `databases` (Set of String) The databases full name in the resource. - `engine` (String) The instance engine. Support MYSQL, POSTGRES, TIDB, SNOWFLAKE, CLICKHOUSE, MONGODB, SQLITE, REDIS, ORACLE, SPANNER, MSSQL, REDSHIFT, MARIADB, OCEANBASE. @@ -39,6 +44,7 @@ The instance data source. Read-Only: - `database` (String) +- `external_secret` (List of Object) (see [below for nested schema](#nestedobjatt--data_sources--external_secret)) - `host` (String) - `id` (String) - `password` (String) @@ -49,4 +55,51 @@ Read-Only: - `type` (String) - `username` (String) + +### Nested Schema for `data_sources.external_secret` + +Read-Only: + +- `aws_secrets_manager` (List of Object) (see [below for nested schema](#nestedobjatt--data_sources--external_secret--aws_secrets_manager)) +- `gcp_secret_manager` (List of Object) (see [below for nested schema](#nestedobjatt--data_sources--external_secret--gcp_secret_manager)) +- `vault` (List of Object) (see [below for nested schema](#nestedobjatt--data_sources--external_secret--vault)) + + +### Nested Schema for `data_sources.external_secret.aws_secrets_manager` + +Read-Only: + +- `password_key_name` (String) +- `secret_name` (String) + + + +### Nested Schema for `data_sources.external_secret.gcp_secret_manager` + +Read-Only: + +- `secret_name` (String) + + + +### Nested Schema for `data_sources.external_secret.vault` + +Read-Only: + +- `app_role` (List of Object) (see [below for nested schema](#nestedobjatt--data_sources--external_secret--vault--app_role)) +- `engine_name` (String) +- `password_key_name` (String) +- `secret_name` (String) +- `token` (String) +- `url` (String) + + +### Nested Schema for `data_sources.external_secret.vault.url` + +Read-Only: + +- `role_id` (String) +- `secret` (String) +- `secret_type` (String) + diff --git a/docs/data-sources/instance_list.md b/docs/data-sources/instance_list.md index 22b4d2b..b5e5975 100644 --- a/docs/data-sources/instance_list.md +++ b/docs/data-sources/instance_list.md @@ -29,8 +29,8 @@ The instance data source list. Read-Only: +- `activation` (Boolean) - `data_sources` (Set of Object) (see [below for nested schema](#nestedobjatt--instances--data_sources)) -- `databases` (Set of String) - `engine` (String) - `engine_version` (String) - `environment` (String) @@ -47,6 +47,7 @@ Read-Only: Read-Only: - `database` (String) +- `external_secret` (List of Object) (see [below for nested schema](#nestedobjatt--instances--data_sources--external_secret)) - `host` (String) - `id` (String) - `password` (String) @@ -57,4 +58,51 @@ Read-Only: - `type` (String) - `username` (String) + +### Nested Schema for `instances.data_sources.external_secret` + +Read-Only: + +- `aws_secrets_manager` (List of Object) (see [below for nested schema](#nestedobjatt--instances--data_sources--external_secret--aws_secrets_manager)) +- `gcp_secret_manager` (List of Object) (see [below for nested schema](#nestedobjatt--instances--data_sources--external_secret--gcp_secret_manager)) +- `vault` (List of Object) (see [below for nested schema](#nestedobjatt--instances--data_sources--external_secret--vault)) + + +### Nested Schema for `instances.data_sources.external_secret.vault` + +Read-Only: + +- `password_key_name` (String) +- `secret_name` (String) + + + +### Nested Schema for `instances.data_sources.external_secret.vault` + +Read-Only: + +- `secret_name` (String) + + + +### Nested Schema for `instances.data_sources.external_secret.vault` + +Read-Only: + +- `app_role` (List of Object) (see [below for nested schema](#nestedobjatt--instances--data_sources--external_secret--vault--app_role)) +- `engine_name` (String) +- `password_key_name` (String) +- `secret_name` (String) +- `token` (String) +- `url` (String) + + +### Nested Schema for `instances.data_sources.external_secret.vault.app_role` + +Read-Only: + +- `role_id` (String) +- `secret` (String) +- `secret_type` (String) + diff --git a/docs/data-sources/setting.md b/docs/data-sources/setting.md index 920680d..a7b27cb 100644 --- a/docs/data-sources/setting.md +++ b/docs/data-sources/setting.md @@ -28,7 +28,6 @@ The setting data source. ### Read-Only - `approval_flow` (Block List) Configure risk level and approval flow for different tasks. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--approval_flow)) -- `external_approval_nodes` (Block List) Configure external nodes in the approval flow. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--external_approval_nodes)) - `id` (String) The ID of this resource. @@ -186,23 +185,3 @@ Read-Only: - `type` (String) - - - - -### Nested Schema for `external_approval_nodes` - -Read-Only: - -- `nodes` (Set of Object) (see [below for nested schema](#nestedatt--external_approval_nodes--nodes)) - - -### Nested Schema for `external_approval_nodes.nodes` - -Read-Only: - -- `endpoint` (String) -- `id` (String) -- `title` (String) - - diff --git a/docs/resources/instance.md b/docs/resources/instance.md index 435e187..030a61a 100644 --- a/docs/resources/instance.md +++ b/docs/resources/instance.md @@ -25,7 +25,9 @@ The instance resource. ### Optional +- `activation` (Boolean) Whether assign license for this instance or not. - `external_link` (String) The external console URL managing this instance (e.g. AWS RDS console, your in-house DB instance console) +- `list_all_databases` (Boolean) List all databases in this instance. If false, will only list 500 databases. - `maximum_connections` (Number) The maximum number of connections. - `sync_interval` (Number) How often the instance is synced in seconds. Default 0, means never sync. @@ -44,15 +46,66 @@ Required: - `host` (String) Host or socket for your instance, or the account name if the instance type is Snowflake. - `id` (String) The unique data source id in this instance. - `port` (String) The port for your instance. -- `type` (String) The data source type. Should be ADMIN or RO. +- `type` (String) The data source type. Should be ADMIN or READ_ONLY. Optional: - `database` (String) The database for the instance, you can set this if the engine type is POSTGRES. +- `external_secret` (Block List, Max: 1) The external secret to get the database password. Learn more: https://www.bytebase.com/docs/get-started/instance/#use-external-secret-manager (see [below for nested schema](#nestedblock--data_sources--external_secret)) - `password` (String, Sensitive) The connection user password used by Bytebase to perform DDL and DML operations. - `ssl_ca` (String, Sensitive) The CA certificate. Optional, you can set this if the engine type is MYSQL, POSTGRES, TIDB or CLICKHOUSE. - `ssl_cert` (String, Sensitive) The client certificate. Optional, you can set this if the engine type is MYSQL, POSTGRES, TIDB or CLICKHOUSE. - `ssl_key` (String, Sensitive) The client key. Optional, you can set this if the engine type is MYSQL, POSTGRES, TIDB or CLICKHOUSE. - `username` (String) The connection user name used by Bytebase to perform DDL and DML operations. + +### Nested Schema for `data_sources.external_secret` + +Optional: + +- `aws_secrets_manager` (Block List, Max: 1) The AWS Secrets Manager to get the database password. Reference doc https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html (see [below for nested schema](#nestedblock--data_sources--external_secret--aws_secrets_manager)) +- `gcp_secret_manager` (Block List, Max: 1) The GCP Secret Manager to get the database password. Reference doc https://cloud.google.com/secret-manager/docs (see [below for nested schema](#nestedblock--data_sources--external_secret--gcp_secret_manager)) +- `vault` (Block List, Max: 1) The Valut to get the database password. Reference doc https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2 (see [below for nested schema](#nestedblock--data_sources--external_secret--vault)) + + +### Nested Schema for `data_sources.external_secret.aws_secrets_manager` + +Required: + +- `password_key_name` (String) The key name for the password. +- `secret_name` (String) The secret name to store the password. + + + +### Nested Schema for `data_sources.external_secret.gcp_secret_manager` + +Required: + +- `secret_name` (String) The secret name should be like "projects/{project-id}/secrets/{secret-id}". + + + +### Nested Schema for `data_sources.external_secret.vault` + +Required: + +- `engine_name` (String) The name for secret engine. +- `password_key_name` (String) The key name for the password. +- `secret_name` (String) The secret name in the engine to store the password. +- `url` (String) The Vault URL. + +Optional: + +- `app_role` (Block List, Max: 1) The Vault app role to get the password. (see [below for nested schema](#nestedblock--data_sources--external_secret--vault--app_role)) +- `token` (String, Sensitive) The root token without TTL. Learn more: https://developer.hashicorp.com/vault/docs/commands/operator/generate-root + + +### Nested Schema for `data_sources.external_secret.vault.app_role` + +Required: + +- `role_id` (String, Sensitive) The app role id. +- `secret` (String, Sensitive) The secret id for the role without ttl. +- `secret_type` (String) The secret id type, can be PLAIN (plain text for the secret) or ENVIRONMENT (envirionment name for the secret). + diff --git a/docs/resources/setting.md b/docs/resources/setting.md index 932c65a..da630a5 100644 --- a/docs/resources/setting.md +++ b/docs/resources/setting.md @@ -23,7 +23,6 @@ The setting resource. - `approval_flow` (Block List) Configure risk level and approval flow for different tasks. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--approval_flow)) - `classification` (Block List, Max: 1) Classification for data masking. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--classification)) -- `external_approval_nodes` (Block List) Configure external nodes in the approval flow. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--external_approval_nodes)) - `semantic_types` (Block Set) Semantic types for data masking. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--semantic_types)) - `workspace_profile` (Block List, Max: 1) (see [below for nested schema](#nestedblock--workspace_profile)) @@ -119,24 +118,6 @@ Optional: - -### Nested Schema for `external_approval_nodes` - -Required: - -- `nodes` (Block Set, Min: 1) (see [below for nested schema](#nestedblock--external_approval_nodes--nodes)) - - -### Nested Schema for `external_approval_nodes.nodes` - -Required: - -- `endpoint` (String) The endpoint URL to receive the approval message. Learn more: https://www.bytebase.com/docs/api/external-approval -- `id` (String) The unique external node id. -- `title` (String) The external node title. - - - ### Nested Schema for `semantic_types` diff --git a/examples/environments/main.tf b/examples/environments/main.tf index b254e47..288369d 100644 --- a/examples/environments/main.tf +++ b/examples/environments/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { bytebase = { - version = "1.0.18" + version = "1.0.19" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/groups/main.tf b/examples/groups/main.tf index 2f29574..4c8764c 100644 --- a/examples/groups/main.tf +++ b/examples/groups/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "1.0.18" + version = "1.0.19" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/instances/main.tf b/examples/instances/main.tf index 18eea87..b3626b6 100644 --- a/examples/instances/main.tf +++ b/examples/instances/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { bytebase = { - version = "1.0.18" + version = "1.0.19" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } @@ -32,7 +32,8 @@ output "all_instances" { # Find a specific instance by name data "bytebase_instance" "test" { - resource_id = local.instance_id_test + resource_id = local.instance_id_test + list_all_databases = true } output "test_instance" { @@ -40,7 +41,8 @@ output "test_instance" { } data "bytebase_instance" "prod" { - resource_id = local.instance_id_prod + resource_id = local.instance_id_prod + list_all_databases = false } output "prod_instance" { diff --git a/examples/policies/main.tf b/examples/policies/main.tf index ea6fb9b..6f45f4c 100644 --- a/examples/policies/main.tf +++ b/examples/policies/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "1.0.18" + version = "1.0.19" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/projects/main.tf b/examples/projects/main.tf index 4ddb66c..d3d3f61 100644 --- a/examples/projects/main.tf +++ b/examples/projects/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { bytebase = { - version = "1.0.18" + version = "1.0.19" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/roles/main.tf b/examples/roles/main.tf index a9b05bf..92b116c 100644 --- a/examples/roles/main.tf +++ b/examples/roles/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "1.0.18" + version = "1.0.19" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/settings/main.tf b/examples/settings/main.tf index b3f6272..8f0d146 100644 --- a/examples/settings/main.tf +++ b/examples/settings/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "1.0.18" + version = "1.0.19" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } @@ -21,10 +21,6 @@ data "bytebase_setting" "approval_flow" { name = "bb.workspace.approval" } -data "bytebase_setting" "external_approval" { - name = "bb.workspace.approval.external" -} - data "bytebase_setting" "workspace_profile" { name = "bb.workspace.profile" } @@ -41,10 +37,6 @@ output "approval_flow" { value = data.bytebase_setting.approval_flow } -output "external_approval" { - value = data.bytebase_setting.external_approval -} - output "workspace_profile" { value = data.bytebase_setting.workspace_profile } diff --git a/examples/setup/approval_flow.tf b/examples/setup/approval_flow.tf index de0bce1..79201e9 100644 --- a/examples/setup/approval_flow.tf +++ b/examples/setup/approval_flow.tf @@ -1,22 +1,3 @@ - -resource "bytebase_setting" "external_approval" { - name = "bb.workspace.approval.external" - - external_approval_nodes { - nodes { - id = "9e150339-f014-4835-83d7-123aeb1895ba" - title = "Example node" - endpoint = "https://example.com" - } - - nodes { - id = "49a976be-50de-4541-b2d3-f2e32f8e41ef" - title = "Example node 2" - endpoint = "https://example.com" - } - } -} - resource "bytebase_setting" "approval_flow" { name = "bb.workspace.approval" approval_flow { diff --git a/examples/setup/instance.tf b/examples/setup/instance.tf index 1a0b91b..de0ba67 100644 --- a/examples/setup/instance.tf +++ b/examples/setup/instance.tf @@ -9,23 +9,33 @@ resource "bytebase_instance" "test" { environment = bytebase_environment.test.name title = "test instance" engine = "MYSQL" + activation = true # You need to specific the data source data_sources { - id = "admin data source" - type = "ADMIN" - username = "" - password = "" - host = "127.0.0.1" - port = "3366" + id = "admin data source" + type = "ADMIN" + host = "127.0.0.1" + port = "3366" + + username = "bytebase" + external_secret { + vault { + url = "http://127.0.0.1:8200" + token = "" + engine_name = "secret" + secret_name = "bytebase" + key_name = "database_pwd" + } + } } # And you can add another data_sources with RO type data_sources { id = "read-only data source" type = "READ_ONLY" - username = "" - password = "" + username = "bytebase" + password = "YOUR_DB_PWD" host = "127.0.0.1" port = "3366" } @@ -46,8 +56,8 @@ resource "bytebase_instance" "prod" { data_sources { id = "admin data source" type = "ADMIN" - username = "" - password = "" + username = "bytebase" + password = "YOUR_DB_PWD" host = "127.0.0.1" port = "54321" } diff --git a/examples/setup/main.tf b/examples/setup/main.tf index babe852..116af61 100644 --- a/examples/setup/main.tf +++ b/examples/setup/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "1.0.18" + version = "1.0.19" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/setup/project.tf b/examples/setup/project.tf index d247e71..2cd13ff 100644 --- a/examples/setup/project.tf +++ b/examples/setup/project.tf @@ -4,7 +4,7 @@ resource "bytebase_project" "sample_project" { bytebase_user.workspace_dba, bytebase_user.project_developer, bytebase_group.developers, - bytebase_instance.prod + bytebase_instance.test ] resource_id = local.project_id @@ -31,5 +31,5 @@ resource "bytebase_project" "sample_project" { } } - databases = bytebase_instance.prod.databases + databases = bytebase_instance.test.databases } diff --git a/examples/setup/users.tf b/examples/setup/users.tf index 88c23e8..d8c178a 100644 --- a/examples/setup/users.tf +++ b/examples/setup/users.tf @@ -10,6 +10,7 @@ resource "bytebase_user" "workspace_dba" { # Create or update the user. resource "bytebase_user" "workspace_auditor" { depends_on = [ + bytebase_user.workspace_dba, bytebase_role.auditor ] title = "Auditor" @@ -22,7 +23,7 @@ resource "bytebase_user" "workspace_auditor" { # Create or update the user. resource "bytebase_user" "project_developer" { depends_on = [ - bytebase_user.workspace_dba + bytebase_user.workspace_auditor ] title = "Developer" diff --git a/examples/users/main.tf b/examples/users/main.tf index dcc0340..4c2d7ed 100644 --- a/examples/users/main.tf +++ b/examples/users/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "1.0.18" + version = "1.0.19" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/vcs/main.tf b/examples/vcs/main.tf index 907cd92..d72c644 100644 --- a/examples/vcs/main.tf +++ b/examples/vcs/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "1.0.18" + version = "1.0.19" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/provider/data_source_database_list.go b/provider/data_source_database_list.go index 60e5154..d130d02 100644 --- a/provider/data_source_database_list.go +++ b/provider/data_source_database_list.go @@ -81,7 +81,7 @@ func dataSourceDatabaseListRead(ctx context.Context, d *schema.ResourceData, m i client := m.(api.Client) parent := d.Get("parent").(string) - databases, err := client.ListDatabase(ctx, parent, "") + databases, err := client.ListDatabase(ctx, parent, "", true) if err != nil { return diag.FromErr(err) } diff --git a/provider/data_source_instance.go b/provider/data_source_instance.go index 7993f9d..de69c71 100644 --- a/provider/data_source_instance.go +++ b/provider/data_source_instance.go @@ -37,6 +37,11 @@ func dataSourceInstance() *schema.Resource { Computed: true, Description: "The instance title.", }, + "activation": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether assign license for this instance or not.", + }, "engine": { Type: schema.TypeString, Computed: true, @@ -75,7 +80,7 @@ func dataSourceInstance() *schema.Resource { "type": { Type: schema.TypeString, Computed: true, - Description: "The data source type. Should be ADMIN or RO.", + Description: "The data source type. Should be ADMIN or READ_ONLY.", }, "username": { Type: schema.TypeString, @@ -85,12 +90,12 @@ func dataSourceInstance() *schema.Resource { "host": { Type: schema.TypeString, Computed: true, - Description: "The Read-replica Host. Only works for RO type data source", + Description: "Host or socket for your instance, or the account name if the instance type is Snowflake.", }, "port": { Type: schema.TypeString, Computed: true, - Description: "The Read-replica Port. Only works for RO type data source", + Description: "The port for your instance.", }, "database": { Type: schema.TypeString, @@ -103,6 +108,7 @@ func dataSourceInstance() *schema.Resource { Sensitive: true, Description: "The connection user password used by Bytebase to perform DDL and DML operations.", }, + "external_secret": getExternalSecretSchema(), "ssl_ca": { Type: schema.TypeString, Computed: true, @@ -125,11 +131,123 @@ func dataSourceInstance() *schema.Resource { }, Set: dataSourceHash, }, + "list_all_databases": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "List all databases in this instance. If false, will only list 500 databases.", + }, "databases": getDatabasesSchema(true), }, } } +func getExternalSecretSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Description: "The external secret to get the database password. Learn more: https://www.bytebase.com/docs/get-started/instance/#use-external-secret-manager", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "vault": { + Type: schema.TypeList, + Computed: true, + Description: "The Valut to get the database password. Reference doc https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + Type: schema.TypeString, + Computed: true, + Description: "The Vault URL.", + }, + "token": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + Description: "The root token without TTL. Learn more: https://developer.hashicorp.com/vault/docs/commands/operator/generate-root", + }, + "app_role": { + Type: schema.TypeList, + Computed: true, + Description: "The Vault app role to get the password.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "role_id": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + Description: "The app role id.", + }, + "secret": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + Description: "The secret id for the role without ttl.", + }, + "secret_type": { + Type: schema.TypeString, + Computed: true, + Description: "The secret id type, can be PLAIN (plain text for the secret) or ENVIRONMENT (envirionment name for the secret).", + }, + }, + }, + }, + "engine_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name for secret engine.", + }, + "secret_name": { + Type: schema.TypeString, + Computed: true, + Description: "The secret name in the engine to store the password.", + }, + "password_key_name": { + Type: schema.TypeString, + Computed: true, + Description: "The key name for the password.", + }, + }, + }, + }, + "aws_secrets_manager": { + Type: schema.TypeList, + Computed: true, + Description: "The AWS Secrets Manager to get the database password. Reference doc https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "secret_name": { + Type: schema.TypeString, + Computed: true, + Description: "The secret name to store the password.", + }, + "password_key_name": { + Type: schema.TypeString, + Computed: true, + Description: "The key name for the password.", + }, + }, + }, + }, + "gcp_secret_manager": { + Type: schema.TypeList, + Computed: true, + Description: "The GCP Secret Manager to get the database password. Reference doc https://cloud.google.com/secret-manager/docs", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "secret_name": { + Type: schema.TypeString, + Computed: true, + Description: "The secret name should be like \"projects/{project-id}/secrets/{secret-id}\".", + }, + }, + }, + }, + }, + }, + } +} + func dataSourceInstanceRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { c := m.(api.Client) instanceName := fmt.Sprintf("%s%s", internal.InstanceNamePrefix, d.Get("resource_id").(string)) diff --git a/provider/data_source_instance_list.go b/provider/data_source_instance_list.go index 342916d..cb5c2c8 100644 --- a/provider/data_source_instance_list.go +++ b/provider/data_source_instance_list.go @@ -48,6 +48,11 @@ func dataSourceInstanceList() *schema.Resource { Computed: true, Description: "The instance title.", }, + "activation": { + Type: schema.TypeBool, + Computed: true, + Description: "Whether assign license for this instance or not.", + }, "engine": { Type: schema.TypeString, Computed: true, @@ -86,7 +91,7 @@ func dataSourceInstanceList() *schema.Resource { "type": { Type: schema.TypeString, Computed: true, - Description: "The data source type. Should be ADMIN or RO.", + Description: "The data source type. Should be ADMIN or READ_ONLY.", }, "username": { Type: schema.TypeString, @@ -96,12 +101,12 @@ func dataSourceInstanceList() *schema.Resource { "host": { Type: schema.TypeString, Computed: true, - Description: "The Read-replica Host. Only works for RO type data source", + Description: "Host or socket for your instance, or the account name if the instance type is Snowflake.", }, "port": { Type: schema.TypeString, Computed: true, - Description: "The Read-replica Port. Only works for RO type data source", + Description: "The port for your instance.", }, "database": { Type: schema.TypeString, @@ -114,6 +119,7 @@ func dataSourceInstanceList() *schema.Resource { Sensitive: true, Description: "The connection user password used by Bytebase to perform DDL and DML operations.", }, + "external_secret": getExternalSecretSchema(), "ssl_ca": { Type: schema.TypeString, Computed: true, @@ -136,7 +142,6 @@ func dataSourceInstanceList() *schema.Resource { }, Set: dataSourceHash, }, - "databases": getDatabasesSchema(true), }, }, }, @@ -166,14 +171,15 @@ func dataSourceInstanceListRead(ctx context.Context, d *schema.ResourceData, m i ins["resource_id"] = instanceID ins["title"] = instance.Title ins["name"] = instance.Name + ins["activation"] = instance.Activation ins["engine"] = instance.Engine.String() ins["engine_version"] = instance.EngineVersion ins["external_link"] = instance.ExternalLink ins["environment"] = instance.Environment if op := instance.Options; op != nil { - ins["sync_interval"] = op.SyncInterval.GetSeconds() - ins["maximum_connections"] = op.MaximumConnections + ins["sync_interval"] = op.GetSyncInterval().GetSeconds() + ins["maximum_connections"] = op.GetMaximumConnections() } dataSources, err := flattenDataSourceList(d, instance.DataSources) @@ -182,12 +188,6 @@ func dataSourceInstanceListRead(ctx context.Context, d *schema.ResourceData, m i } ins["data_sources"] = schema.NewSet(dataSourceHash, dataSources) - databases, err := c.ListDatabase(ctx, instance.Name, "") - if err != nil { - return diag.FromErr(err) - } - ins["databases"] = flattenDatabaseList(databases) - instances = append(instances, ins) } diff --git a/provider/data_source_project.go b/provider/data_source_project.go index 8114714..cf92471 100644 --- a/provider/data_source_project.go +++ b/provider/data_source_project.go @@ -257,7 +257,7 @@ func setProject( "project": project.Name, }) - databases, err := client.ListDatabase(ctx, project.Name, "") + databases, err := client.ListDatabase(ctx, project.Name, "", true) if err != nil { return diag.FromErr(err) } diff --git a/provider/data_source_project_list.go b/provider/data_source_project_list.go index 7c666b0..64415d4 100644 --- a/provider/data_source_project_list.go +++ b/provider/data_source_project_list.go @@ -117,7 +117,7 @@ func dataSourceProjectListRead(ctx context.Context, d *schema.ResourceData, m in proj["skip_backup_errors"] = project.AllowModifyStatement proj["postgres_database_tenant_mode"] = project.PostgresDatabaseTenantMode - databases, err := c.ListDatabase(ctx, project.Name, "") + databases, err := c.ListDatabase(ctx, project.Name, "", false) if err != nil { return diag.FromErr(err) } diff --git a/provider/data_source_setting.go b/provider/data_source_setting.go index 58245d5..8b5f90d 100644 --- a/provider/data_source_setting.go +++ b/provider/data_source_setting.go @@ -27,17 +27,15 @@ func dataSourceSetting() *schema.Resource { Required: true, ValidateFunc: validation.StringInSlice([]string{ string(api.SettingWorkspaceApproval), - string(api.SettingWorkspaceExternalApproval), string(api.SettingWorkspaceProfile), string(api.SettingDataClassification), string(api.SettingSemanticTypes), }, false), }, - "approval_flow": getWorkspaceApprovalSetting(true), - "external_approval_nodes": getExternalApprovalSetting(true), - "workspace_profile": getWorkspaceProfileSetting(true), - "classification": getClassificationSetting(true), - "semantic_types": getSemanticTypesSetting(true), + "approval_flow": getWorkspaceApprovalSetting(true), + "workspace_profile": getWorkspaceProfileSetting(true), + "classification": getClassificationSetting(true), + "semantic_types": getSemanticTypesSetting(true), }, } } @@ -341,48 +339,6 @@ func getWorkspaceProfileSetting(computed bool) *schema.Schema { } } -func getExternalApprovalSetting(computed bool) *schema.Schema { - return &schema.Schema{ - Computed: computed, - Optional: true, - Default: nil, - Type: schema.TypeList, - Description: "Configure external nodes in the approval flow. Require ENTERPRISE subscription.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "nodes": { - Type: schema.TypeSet, - Computed: computed, - Required: !computed, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: computed, - Required: !computed, - Description: "The unique external node id.", - }, - "title": { - Type: schema.TypeString, - Computed: computed, - Required: !computed, - Description: "The external node title.", - }, - "endpoint": { - Type: schema.TypeString, - Computed: computed, - Required: !computed, - Description: "The endpoint URL to receive the approval message. Learn more: https://www.bytebase.com/docs/api/external-approval", - }, - }, - }, - Set: itemIDHash, - }, - }, - }, - } -} - func getWorkspaceApprovalSetting(computed bool) *schema.Schema { return &schema.Schema{ Computed: computed, @@ -515,12 +471,6 @@ func setSettingMessage(ctx context.Context, d *schema.ResourceData, client api.C return diag.Errorf("cannot set workspace_approval_setting: %s", err.Error()) } } - if value := setting.Value.GetExternalApprovalSettingValue(); value != nil { - settingVal := flattenExternalApprovalSetting(value) - if err := d.Set("external_approval_nodes", settingVal); err != nil { - return diag.Errorf("cannot set external_approval_nodes: %s", err.Error()) - } - } if value := setting.Value.GetWorkspaceProfileSettingValue(); value != nil { settingVal := flattenWorkspaceProfileSetting(value) if err := d.Set("workspace_profile", settingVal); err != nil { @@ -668,22 +618,6 @@ func flattenWorkspaceApprovalSetting(ctx context.Context, client api.Client, set return []interface{}{approvalSetting}, nil } -func flattenExternalApprovalSetting(setting *v1pb.ExternalApprovalSetting) []interface{} { - nodeList := []interface{}{} - for _, node := range setting.Nodes { - rawNode := map[string]interface{}{} - rawNode["id"] = node.Id - rawNode["title"] = node.Title - rawNode["endpoint"] = node.Endpoint - nodeList = append(nodeList, rawNode) - } - - approvalSetting := map[string]interface{}{ - "nodes": nodeList, - } - return []interface{}{approvalSetting} -} - func flattenWorkspaceProfileSetting(setting *v1pb.WorkspaceProfileSetting) []interface{} { raw := map[string]interface{}{} diff --git a/provider/internal/mock_client.go b/provider/internal/mock_client.go index a9e9a27..c2f88a4 100644 --- a/provider/internal/mock_client.go +++ b/provider/internal/mock_client.go @@ -390,7 +390,7 @@ func (c *mockClient) GetDatabase(_ context.Context, databaseName string) (*v1pb. } // ListDatabase list the databases. -func (c *mockClient) ListDatabase(_ context.Context, instaceID, filter string) ([]*v1pb.Database, error) { +func (c *mockClient) ListDatabase(_ context.Context, instaceID, filter string, _ bool) ([]*v1pb.Database, error) { projectID := "-" if strings.HasPrefix(filter, "project == ") { projectID = strings.Split(filter, "project == ")[1] diff --git a/provider/resource_instance.go b/provider/resource_instance.go index d31a4e4..5902917 100644 --- a/provider/resource_instance.go +++ b/provider/resource_instance.go @@ -47,6 +47,12 @@ func resourceInstance() *schema.Resource { ValidateFunc: validation.StringIsNotEmpty, Description: "The instance title.", }, + "activation": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "Whether assign license for this instance or not.", + }, "name": { Type: schema.TypeString, Computed: true, @@ -123,7 +129,7 @@ func resourceInstance() *schema.Resource { v1pb.DataSourceType_ADMIN.String(), v1pb.DataSourceType_READ_ONLY.String(), }, false), - Description: "The data source type. Should be ADMIN or RO.", + Description: "The data source type. Should be ADMIN or READ_ONLY.", }, "username": { Type: schema.TypeString, @@ -138,6 +144,118 @@ func resourceInstance() *schema.Resource { Default: "", Description: "The connection user password used by Bytebase to perform DDL and DML operations.", }, + "external_secret": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "The external secret to get the database password. Learn more: https://www.bytebase.com/docs/get-started/instance/#use-external-secret-manager", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "vault": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "The Valut to get the database password. Reference doc https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + Type: schema.TypeString, + Required: true, + Description: "The Vault URL.", + }, + "token": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + Description: "The root token without TTL. Learn more: https://developer.hashicorp.com/vault/docs/commands/operator/generate-root", + }, + "app_role": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "The Vault app role to get the password.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "role_id": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + Description: "The app role id.", + }, + "secret": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + Description: "The secret id for the role without ttl.", + }, + "secret_type": { + Type: schema.TypeString, + Required: true, + Description: "The secret id type, can be PLAIN (plain text for the secret) or ENVIRONMENT (envirionment name for the secret).", + ValidateFunc: validation.StringInSlice([]string{ + v1pb.DataSourceExternalSecret_AppRoleAuthOption_PLAIN.String(), + v1pb.DataSourceExternalSecret_AppRoleAuthOption_ENVIRONMENT.String(), + }, false), + }, + }, + }, + }, + "engine_name": { + Type: schema.TypeString, + Required: true, + Description: "The name for secret engine.", + }, + "secret_name": { + Type: schema.TypeString, + Required: true, + Description: "The secret name in the engine to store the password.", + }, + "password_key_name": { + Type: schema.TypeString, + Required: true, + Description: "The key name for the password.", + }, + }, + }, + }, + "aws_secrets_manager": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "The AWS Secrets Manager to get the database password. Reference doc https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "secret_name": { + Type: schema.TypeString, + Required: true, + Description: "The secret name to store the password.", + }, + "password_key_name": { + Type: schema.TypeString, + Required: true, + Description: "The key name for the password.", + }, + }, + }, + }, + "gcp_secret_manager": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "The GCP Secret Manager to get the database password. Reference doc https://cloud.google.com/secret-manager/docs", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "secret_name": { + Type: schema.TypeString, + Required: true, + Description: "The secret name should be like \"projects/{project-id}/secrets/{secret-id}\".", + }, + }, + }, + }, + }, + }, + }, "ssl_ca": { Type: schema.TypeString, Optional: true, @@ -181,6 +299,12 @@ func resourceInstance() *schema.Resource { }, Set: dataSourceHash, }, + "list_all_databases": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "List all databases in this instance. If false, will only list 500 databases.", + }, "databases": getDatabasesSchema(true), }, } @@ -198,6 +322,8 @@ func resourceInstanceCreate(ctx context.Context, d *schema.ResourceData, m inter instanceName := fmt.Sprintf("%s%s", internal.InstanceNamePrefix, instanceID) title := d.Get("title").(string) externalLink := d.Get("external_link").(string) + environment := d.Get("environment").(string) + activation := d.Get("activation").(bool) instanceOptions := &v1pb.InstanceOptions{ SyncInterval: &durationpb.Duration{ Seconds: int64(d.Get("sync_interval").(int)), @@ -257,6 +383,12 @@ func resourceInstanceCreate(ctx context.Context, d *schema.ResourceData, m inter if externalLink != "" && externalLink != existedInstance.ExternalLink { updateMasks = append(updateMasks, "external_link") } + if environment != existedInstance.Environment { + updateMasks = append(updateMasks, "environment") + } + if activation != existedInstance.Activation { + updateMasks = append(updateMasks, "activation") + } if op := existedInstance.Options; op != nil { if instanceOptions.SyncInterval.GetSeconds() != op.SyncInterval.GetSeconds() { updateMasks = append(updateMasks, "options.sync_interval") @@ -275,6 +407,8 @@ func resourceInstanceCreate(ctx context.Context, d *schema.ResourceData, m inter Title: title, ExternalLink: externalLink, DataSources: dataSourceList, + Environment: environment, + Activation: activation, State: v1pb.State_ACTIVE, Options: instanceOptions, }, updateMasks); err != nil { @@ -289,12 +423,13 @@ func resourceInstanceCreate(ctx context.Context, d *schema.ResourceData, m inter } else { if _, err := c.CreateInstance(ctx, instanceID, &v1pb.Instance{ Name: instanceName, - Title: d.Get("title").(string), + Title: title, Engine: engine, - ExternalLink: d.Get("external_link").(string), + ExternalLink: externalLink, State: v1pb.State_ACTIVE, DataSources: dataSourceList, - Environment: d.Get("environment").(string), + Environment: environment, + Activation: activation, Options: instanceOptions, }); err != nil { return diag.FromErr(err) @@ -350,9 +485,6 @@ func resourceInstanceUpdate(ctx context.Context, d *schema.ResourceData, m inter if d.HasChange("resource_id") { return diag.Errorf("cannot change the resource id") } - if d.HasChange("environment") { - return diag.Errorf("cannot change the environment in instance") - } if d.HasChange("engine") { return diag.Errorf("cannot change the engine in instance") } @@ -394,6 +526,12 @@ func resourceInstanceUpdate(ctx context.Context, d *schema.ResourceData, m inter if d.HasChange("external_link") { paths = append(paths, "external_link") } + if d.HasChange("environment") { + paths = append(paths, "environment") + } + if d.HasChange("activation") { + paths = append(paths, "activation") + } if d.HasChange("data_sources") { paths = append(paths, "data_sources") } @@ -409,6 +547,8 @@ func resourceInstanceUpdate(ctx context.Context, d *schema.ResourceData, m inter Name: instanceName, Title: d.Get("title").(string), ExternalLink: d.Get("external_link").(string), + Environment: d.Get("environment").(string), + Activation: d.Get("activation").(bool), DataSources: dataSourceList, State: v1pb.State_ACTIVE, Options: &v1pb.InstanceOptions{ @@ -479,6 +619,9 @@ func setInstanceMessage( if err := d.Set("environment", instance.Environment); err != nil { return diag.Errorf("cannot set environment for instance: %s", err.Error()) } + if err := d.Set("activation", instance.Activation); err != nil { + return diag.Errorf("cannot set activation for instance: %s", err.Error()) + } if err := d.Set("engine", instance.Engine.String()); err != nil { return diag.Errorf("cannot set engine for instance: %s", err.Error()) } @@ -489,10 +632,10 @@ func setInstanceMessage( return diag.Errorf("cannot set external_link for instance: %s", err.Error()) } if op := instance.Options; op != nil { - if err := d.Set("sync_interval", op.SyncInterval.GetSeconds()); err != nil { + if err := d.Set("sync_interval", op.GetSyncInterval().GetSeconds()); err != nil { return diag.Errorf("cannot set sync_interval for instance: %s", err.Error()) } - if err := d.Set("maximum_connections", op.MaximumConnections); err != nil { + if err := d.Set("maximum_connections", op.GetMaximumConnections()); err != nil { return diag.Errorf("cannot set maximum_connections for instance: %s", err.Error()) } } @@ -509,7 +652,8 @@ func setInstanceMessage( "instance": instance.Name, }) - databases, err := client.ListDatabase(ctx, instance.Name, "") + listAllDatabases := d.Get("list_all_databases").(bool) + databases, err := client.ListDatabase(ctx, instance.Name, "", listAllDatabases) if err != nil { return diag.FromErr(err) } @@ -548,6 +692,57 @@ func flattenDataSourceList(d *schema.ResourceData, dataSourceList []*v1pb.DataSo raw["ssl_cert"] = ds.SslCert raw["ssl_key"] = ds.SslKey } + + if v := dataSource.ExternalSecret; v != nil { + switch v.SecretType { + case v1pb.DataSourceExternalSecret_GCP_SECRET_MANAGER: + gcp := map[string]interface{}{ + "secret_name": v.SecretName, + } + raw["external_secret"] = []any{ + map[string]interface{}{ + "gcp_secret_manager": []any{gcp}, + }, + } + case v1pb.DataSourceExternalSecret_AWS_SECRETS_MANAGER: + aws := map[string]interface{}{ + "secret_name": v.SecretName, + "password_key_name": v.PasswordKeyName, + } + raw["external_secret"] = []any{ + map[string]interface{}{ + "aws_secrets_manager": []any{aws}, + }, + } + case v1pb.DataSourceExternalSecret_VAULT_KV_V2: + vault := map[string]interface{}{ + "url": v.Url, + "engine_name": v.EngineName, + "secret_name": v.SecretName, + "password_key_name": v.PasswordKeyName, + } + switch v.AuthType { + case v1pb.DataSourceExternalSecret_TOKEN: + if ds, ok := oldDataSourceMap[dataSource.Id]; ok && ds.GetExternalSecret() != nil { + vault["token"] = ds.GetExternalSecret().GetToken() + } + case v1pb.DataSourceExternalSecret_VAULT_APP_ROLE: + appRole := map[string]interface{}{ + "secret_type": v.GetAppRole().Type.String(), + } + if ds, ok := oldDataSourceMap[dataSource.Id]; ok && ds.GetExternalSecret() != nil { + appRole["role_id"] = ds.GetExternalSecret().GetAppRole().GetRoleId() + appRole["secret"] = ds.GetExternalSecret().GetAppRole().GetSecretId() + } + vault["app_role"] = []any{appRole} + } + raw["external_secret"] = []any{ + map[string]interface{}{ + "vault": []any{vault}, + }, + } + } + } res = append(res, raw) } return res, nil @@ -583,6 +778,53 @@ func convertDataSourceCreateList(d *schema.ResourceData, validate bool) ([]*v1pb if v, ok := obj["password"].(string); ok && v != "" { dataSource.Password = v } + if v, ok := obj["external_secret"].([]interface{}); ok && len(v) == 1 { + externalSecret := &v1pb.DataSourceExternalSecret{} + rawExternalSecret := v[0].(map[string]interface{}) + if v, ok := rawExternalSecret["vault"].([]interface{}); ok && len(v) == 1 { + rawVault := v[0].(map[string]interface{}) + externalSecret.SecretType = v1pb.DataSourceExternalSecret_VAULT_KV_V2 + externalSecret.Url = rawVault["url"].(string) + externalSecret.EngineName = rawVault["engine_name"].(string) + externalSecret.SecretName = rawVault["secret_name"].(string) + externalSecret.PasswordKeyName = rawVault["password_key_name"].(string) + + if token, ok := rawVault["token"].(string); ok && token != "" { + externalSecret.AuthType = v1pb.DataSourceExternalSecret_TOKEN + externalSecret.AuthOption = &v1pb.DataSourceExternalSecret_Token{ + Token: token, + } + } else if v, ok := rawVault["app_role"].([]interface{}); ok && len(v) == 1 { + rawAppRole := v[0].(map[string]interface{}) + externalSecret.AuthType = v1pb.DataSourceExternalSecret_VAULT_APP_ROLE + externalSecret.AuthOption = &v1pb.DataSourceExternalSecret_AppRole{ + AppRole: &v1pb.DataSourceExternalSecret_AppRoleAuthOption{ + RoleId: rawAppRole["role_id"].(string), + SecretId: rawAppRole["secret"].(string), + Type: v1pb.DataSourceExternalSecret_AppRoleAuthOption_SecretType(v1pb.DataSourceExternalSecret_AppRoleAuthOption_SecretType_value[rawAppRole["secret_type"].(string)]), + }, + } + } else { + return nil, errors.Errorf("require token or app_role for Vault") + } + } else if v, ok := rawExternalSecret["aws_secrets_manager"].([]interface{}); ok && len(v) == 1 { + rawAWS := v[0].(map[string]interface{}) + externalSecret.SecretType = v1pb.DataSourceExternalSecret_AWS_SECRETS_MANAGER + externalSecret.SecretName = rawAWS["secret_name"].(string) + externalSecret.PasswordKeyName = rawAWS["password_key_name"].(string) + } else if v, ok := rawExternalSecret["gcp_secret_manager"].([]interface{}); ok && len(v) == 1 { + rawGCP := v[0].(map[string]interface{}) + externalSecret.SecretType = v1pb.DataSourceExternalSecret_GCP_SECRET_MANAGER + externalSecret.SecretName = rawGCP["secret_name"].(string) + } else { + return nil, errors.Errorf("must set one of vault, aws_secrets_manager or gcp_secret_manager") + } + dataSource.ExternalSecret = externalSecret + } + if dataSource.Password != "" && dataSource.ExternalSecret != nil { + return nil, errors.Errorf("cannot set both password and external_secret") + } + if v, ok := obj["ssl_ca"].(string); ok { dataSource.SslCa = v } diff --git a/provider/resource_project.go b/provider/resource_project.go index 398ce1c..3241d4b 100644 --- a/provider/resource_project.go +++ b/provider/resource_project.go @@ -448,7 +448,7 @@ func updateMembersInProject(ctx context.Context, d *schema.ResourceData, client const batchSize = 100 func updateDatabasesInProject(ctx context.Context, d *schema.ResourceData, client api.Client, projectName string) diag.Diagnostics { - databases, err := client.ListDatabase(ctx, projectName, "") + databases, err := client.ListDatabase(ctx, projectName, "", true) if err != nil { return diag.Errorf("failed to list database with error: %v", err.Error()) } diff --git a/provider/resource_setting.go b/provider/resource_setting.go index b26ba20..d008baf 100644 --- a/provider/resource_setting.go +++ b/provider/resource_setting.go @@ -33,17 +33,15 @@ func resourceSetting() *schema.Resource { Required: true, ValidateFunc: validation.StringInSlice([]string{ string(api.SettingWorkspaceApproval), - string(api.SettingWorkspaceExternalApproval), string(api.SettingWorkspaceProfile), string(api.SettingDataClassification), string(api.SettingSemanticTypes), }, false), }, - "approval_flow": getWorkspaceApprovalSetting(false), - "external_approval_nodes": getExternalApprovalSetting(false), - "workspace_profile": getWorkspaceProfileSetting(false), - "classification": getClassificationSetting(false), - "semantic_types": getSemanticTypesSetting(false), + "approval_flow": getWorkspaceApprovalSetting(false), + "workspace_profile": getWorkspaceProfileSetting(false), + "classification": getClassificationSetting(false), + "semantic_types": getSemanticTypesSetting(false), }, } } @@ -71,16 +69,6 @@ func resourceSettingUpsert(ctx context.Context, d *schema.ResourceData, m interf WorkspaceApprovalSettingValue: workspaceApproval, }, } - case api.SettingWorkspaceExternalApproval: - externalApproval, err := convertToV1ExternalNodesSetting(d) - if err != nil { - return diag.FromErr(err) - } - setting.Value = &v1pb.Value{ - Value: &v1pb.Value_ExternalApprovalSettingValue{ - ExternalApprovalSettingValue: externalApproval, - }, - } case api.SettingWorkspaceProfile: workspaceProfile, updatePathes, err := convertToV1WorkspaceProfileSetting(d) if err != nil { @@ -240,31 +228,6 @@ func convertToV1ClassificationSetting(d *schema.ResourceData) (*v1pb.DataClassif }, nil } -func convertToV1ExternalNodesSetting(d *schema.ResourceData) (*v1pb.ExternalApprovalSetting, error) { - rawList, ok := d.Get("external_approval_nodes").([]interface{}) - if !ok || len(rawList) != 1 { - return nil, errors.Errorf("invalid external_approval_nodes") - } - - raw := rawList[0].(map[string]interface{}) - nodes, ok := raw["nodes"].(*schema.Set) - if !ok { - return nil, errors.Errorf("missing nodes") - } - - externalApprovalSetting := &v1pb.ExternalApprovalSetting{} - - for _, node := range nodes.List() { - rawNode := node.(map[string]interface{}) - externalApprovalSetting.Nodes = append(externalApprovalSetting.Nodes, &v1pb.ExternalApprovalSetting_Node{ - Id: rawNode["id"].(string), - Title: rawNode["title"].(string), - Endpoint: rawNode["endpoint"].(string), - }) - } - return externalApprovalSetting, nil -} - func convertToV1ApprovalSetting(d *schema.ResourceData) (*v1pb.WorkspaceApprovalSetting, error) { rawList, ok := d.Get("approval_flow").([]interface{}) if !ok || len(rawList) != 1 {