diff --git a/VERSION b/VERSION
index 1464c52..ece61c6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.5
\ No newline at end of file
+1.0.6
\ No newline at end of file
diff --git a/api/client.go b/api/client.go
index bb8a57c..56f0864 100644
--- a/api/client.go
+++ b/api/client.go
@@ -59,6 +59,10 @@ type Client interface {
ListDatabase(ctx context.Context, instanceID, filter string) (*v1pb.ListDatabasesResponse, error)
// UpdateDatabase patches the database.
UpdateDatabase(ctx context.Context, patch *v1pb.Database, updateMasks []string) (*v1pb.Database, error)
+ // GetDatabaseCatalog gets the database catalog by the database full name.
+ GetDatabaseCatalog(ctx context.Context, databaseName string) (*v1pb.DatabaseCatalog, error)
+ // UpdateDatabaseCatalog patches the database catalog.
+ UpdateDatabaseCatalog(ctx context.Context, patch *v1pb.DatabaseCatalog, updateMasks []string) (*v1pb.DatabaseCatalog, error)
// Project
// GetProject gets the project by project full name.
diff --git a/client/database.go b/client/database.go
index 5c2277a..00b29cf 100644
--- a/client/database.go
+++ b/client/database.go
@@ -63,3 +63,33 @@ func (c *client) UpdateDatabase(ctx context.Context, patch *v1pb.Database, updat
return &res, nil
}
+
+// GetDatabaseCatalog gets the database catalog by the database full name.
+func (c *client) GetDatabaseCatalog(ctx context.Context, databaseName string) (*v1pb.DatabaseCatalog, error) {
+ body, err := c.getResource(ctx, fmt.Sprintf("%s/catalog", databaseName))
+ if err != nil {
+ return nil, err
+ }
+
+ var res v1pb.DatabaseCatalog
+ if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil {
+ return nil, err
+ }
+
+ return &res, nil
+}
+
+// UpdateDatabaseCatalog patches the database catalog.
+func (c *client) UpdateDatabaseCatalog(ctx context.Context, patch *v1pb.DatabaseCatalog, updateMasks []string) (*v1pb.DatabaseCatalog, error) {
+ body, err := c.updateResource(ctx, patch.Name, patch, updateMasks, false /* allow missing = false*/)
+ if err != nil {
+ return nil, err
+ }
+
+ var res v1pb.DatabaseCatalog
+ if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil {
+ return nil, err
+ }
+
+ return &res, nil
+}
diff --git a/docs/data-sources/database_catalog.md b/docs/data-sources/database_catalog.md
new file mode 100644
index 0000000..fa4d185
--- /dev/null
+++ b/docs/data-sources/database_catalog.md
@@ -0,0 +1,54 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "bytebase_database_catalog Data Source - terraform-provider-bytebase"
+subcategory: ""
+description: |-
+ The database catalog data source.
+---
+
+# bytebase_database_catalog (Data Source)
+
+The database catalog data source.
+
+
+
+
+## Schema
+
+### Required
+
+- `database` (String) The database full name in instances/{instance}/databases/{database} format
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+- `schemas` (List of Object) (see [below for nested schema](#nestedatt--schemas))
+
+
+### Nested Schema for `schemas`
+
+Read-Only:
+
+- `name` (String)
+- `tables` (List of Object) (see [below for nested schema](#nestedobjatt--schemas--tables))
+
+
+### Nested Schema for `schemas.tables`
+
+Read-Only:
+
+- `classification` (String)
+- `columns` (List of Object) (see [below for nested schema](#nestedobjatt--schemas--tables--columns))
+- `name` (String)
+
+
+### Nested Schema for `schemas.tables.columns`
+
+Read-Only:
+
+- `classification` (String)
+- `labels` (Map of String)
+- `name` (String)
+- `semantic_type` (String)
+
+
diff --git a/docs/data-sources/policy.md b/docs/data-sources/policy.md
index 029fa32..f99bc66 100644
--- a/docs/data-sources/policy.md
+++ b/docs/data-sources/policy.md
@@ -22,7 +22,6 @@ The policy data source.
### Optional
- `masking_exception_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--masking_exception_policy))
-- `masking_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--masking_policy))
- `parent` (String) The policy parent name for the policy, support projects/{resource id}, environments/{resource id}, instances/{resource id}, or instances/{resource id}/databases/{database name}
### Read-Only
@@ -48,30 +47,8 @@ Optional:
- `column` (String)
- `database` (String) The database full name in instances/{instance resource id}/databases/{database name} format
- `expire_timestamp` (String) The expiration timestamp in YYYY-MM-DDThh:mm:ss.000Z format
-- `masking_level` (String)
- `member` (String) The member in user:{email} or group:{email} format.
- `schema` (String)
- `table` (String)
-
-
-### Nested Schema for `masking_policy`
-
-Optional:
-
-- `mask_data` (Block List) (see [below for nested schema](#nestedblock--masking_policy--mask_data))
-
-
-### Nested Schema for `masking_policy.mask_data`
-
-Optional:
-
-- `column` (String)
-- `full_masking_algorithm_id` (String)
-- `masking_level` (String)
-- `partial_masking_algorithm_id` (String)
-- `schema` (String)
-- `table` (String)
-
-
diff --git a/docs/data-sources/policy_list.md b/docs/data-sources/policy_list.md
index 90390fd..3b3db7c 100644
--- a/docs/data-sources/policy_list.md
+++ b/docs/data-sources/policy_list.md
@@ -32,7 +32,6 @@ Read-Only:
- `enforce` (Boolean)
- `inherit_from_parent` (Boolean)
- `masking_exception_policy` (List of Object) (see [below for nested schema](#nestedobjatt--policies--masking_exception_policy))
-- `masking_policy` (List of Object) (see [below for nested schema](#nestedobjatt--policies--masking_policy))
- `name` (String)
- `type` (String)
@@ -52,30 +51,8 @@ Read-Only:
- `column` (String)
- `database` (String)
- `expire_timestamp` (String)
-- `masking_level` (String)
- `member` (String)
- `schema` (String)
- `table` (String)
-
-
-### Nested Schema for `policies.masking_policy`
-
-Read-Only:
-
-- `mask_data` (List of Object) (see [below for nested schema](#nestedobjatt--policies--masking_policy--mask_data))
-
-
-### Nested Schema for `policies.masking_policy.mask_data`
-
-Read-Only:
-
-- `column` (String)
-- `full_masking_algorithm_id` (String)
-- `masking_level` (String)
-- `partial_masking_algorithm_id` (String)
-- `schema` (String)
-- `table` (String)
-
-
diff --git a/docs/data-sources/setting.md b/docs/data-sources/setting.md
index 0c9bf2e..1ce3d3d 100644
--- a/docs/data-sources/setting.md
+++ b/docs/data-sources/setting.md
@@ -21,8 +21,8 @@ The setting data source.
### Read-Only
-- `approval_flow` (Block List) (see [below for nested schema](#nestedblock--approval_flow))
-- `external_approval_nodes` (Block List) (see [below for nested schema](#nestedblock--external_approval_nodes))
+- `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.
diff --git a/docs/resources/database_catalog.md b/docs/resources/database_catalog.md
new file mode 100644
index 0000000..16f8ead
--- /dev/null
+++ b/docs/resources/database_catalog.md
@@ -0,0 +1,63 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "bytebase_database_catalog Resource - terraform-provider-bytebase"
+subcategory: ""
+description: |-
+ The database catalog resource.
+---
+
+# bytebase_database_catalog (Resource)
+
+The database catalog resource.
+
+
+
+
+## Schema
+
+### Required
+
+- `database` (String) The database full name in instances/{instance}/databases/{database} format
+- `schemas` (Block List, Min: 1) (see [below for nested schema](#nestedblock--schemas))
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `schemas`
+
+Required:
+
+- `tables` (Block List, Min: 1) (see [below for nested schema](#nestedblock--schemas--tables))
+
+Optional:
+
+- `name` (String)
+
+
+### Nested Schema for `schemas.tables`
+
+Required:
+
+- `columns` (Block List, Min: 1) (see [below for nested schema](#nestedblock--schemas--tables--columns))
+- `name` (String)
+
+Optional:
+
+- `classification` (String) The classification id
+
+
+### Nested Schema for `schemas.tables.columns`
+
+Required:
+
+- `name` (String)
+
+Optional:
+
+- `classification` (String) The classification id
+- `labels` (Map of String)
+- `semantic_type` (String) The semantic type id
+
+
diff --git a/docs/resources/environment.md b/docs/resources/environment.md
index d9f22d1..d78ac86 100644
--- a/docs/resources/environment.md
+++ b/docs/resources/environment.md
@@ -17,7 +17,7 @@ The environment resource.
### Required
-- `environment_tier_policy` (String) If marked as PROTECTED, developers cannot execute any query on this environment's databases using SQL Editor by default.
+- `environment_tier_policy` (String) If marked as PROTECTED, developers cannot execute any query on this environment's databases using SQL Editor by default. Require ENTERPRISE subscription.
- `order` (Number) The environment sorting order.
- `resource_id` (String) The environment unique resource id.
- `title` (String) The environment title.
diff --git a/docs/resources/group.md b/docs/resources/group.md
index 79f4eca..fa628a4 100644
--- a/docs/resources/group.md
+++ b/docs/resources/group.md
@@ -3,12 +3,12 @@
page_title: "bytebase_group Resource - terraform-provider-bytebase"
subcategory: ""
description: |-
- The group resource.
+ The group resource. Workspace domain is required for creating groups.
---
# bytebase_group (Resource)
-The group resource.
+The group resource. Workspace domain is required for creating groups.
diff --git a/docs/resources/policy.md b/docs/resources/policy.md
index b26f102..dc743a2 100644
--- a/docs/resources/policy.md
+++ b/docs/resources/policy.md
@@ -25,7 +25,6 @@ The policy resource.
- `enforce` (Boolean) Decide if the policy is enforced.
- `inherit_from_parent` (Boolean) Decide if the policy should inherit from the parent.
- `masking_exception_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--masking_exception_policy))
-- `masking_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--masking_policy))
### Read-Only
@@ -48,30 +47,8 @@ Optional:
- `column` (String)
- `database` (String) The database full name in instances/{instance resource id}/databases/{database name} format
- `expire_timestamp` (String) The expiration timestamp in YYYY-MM-DDThh:mm:ss.000Z format
-- `masking_level` (String)
- `member` (String) The member in user:{email} or group:{email} format.
- `schema` (String)
- `table` (String)
-
-
-### Nested Schema for `masking_policy`
-
-Optional:
-
-- `mask_data` (Block List) (see [below for nested schema](#nestedblock--masking_policy--mask_data))
-
-
-### Nested Schema for `masking_policy.mask_data`
-
-Optional:
-
-- `column` (String)
-- `full_masking_algorithm_id` (String)
-- `masking_level` (String)
-- `partial_masking_algorithm_id` (String)
-- `schema` (String)
-- `table` (String)
-
-
diff --git a/docs/resources/setting.md b/docs/resources/setting.md
index 93938a6..4912e6f 100644
--- a/docs/resources/setting.md
+++ b/docs/resources/setting.md
@@ -21,8 +21,8 @@ The setting resource.
### Optional
-- `approval_flow` (Block List) (see [below for nested schema](#nestedblock--approval_flow))
-- `external_approval_nodes` (Block List) (see [below for nested schema](#nestedblock--external_approval_nodes))
+- `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))
### Read-Only
diff --git a/examples/database/main.tf b/examples/database/main.tf
new file mode 100644
index 0000000..e139030
--- /dev/null
+++ b/examples/database/main.tf
@@ -0,0 +1,29 @@
+# Examples for query the environments
+terraform {
+ required_providers {
+ bytebase = {
+ version = "1.0.6"
+ # For local development, please use "terraform.local/bytebase/bytebase" instead
+ source = "registry.terraform.io/bytebase/bytebase"
+ }
+ }
+}
+
+provider "bytebase" {
+ # You need to replace the account and key with your Bytebase service account.
+ service_account = "terraform@service.bytebase.com"
+ service_key = "bbs_BxVIp7uQsARl8nR92ZZV"
+ # The Bytebase service URL. You can use the external URL in production.
+ # Check the docs about external URL: https://www.bytebase.com/docs/get-started/install/external-url
+ url = "https://bytebase.example.com"
+}
+
+
+
+data "bytebase_database_catalog" "employee" {
+ database = "instances/test-sample-instance/databases/employee"
+}
+
+output "employee_catalog" {
+ value = data.bytebase_database_catalog.employee
+}
diff --git a/examples/environments/main.tf b/examples/environments/main.tf
index 114ef21..8929126 100644
--- a/examples/environments/main.tf
+++ b/examples/environments/main.tf
@@ -2,7 +2,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.5"
+ version = "1.0.6"
# 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 b7fa47c..7d0bc8f 100644
--- a/examples/groups/main.tf
+++ b/examples/groups/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.5"
+ version = "1.0.6"
# 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 0a2b238..cac84e7 100644
--- a/examples/instances/main.tf
+++ b/examples/instances/main.tf
@@ -2,7 +2,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.5"
+ version = "1.0.6"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
diff --git a/examples/policies/main.tf b/examples/policies/main.tf
index 416cb5b..3b9b7a4 100644
--- a/examples/policies/main.tf
+++ b/examples/policies/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.5"
+ version = "1.0.6"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
@@ -17,20 +17,11 @@ provider "bytebase" {
url = "https://bytebase.example.com"
}
-data "bytebase_policy" "masking_policy" {
- parent = "instances/test-sample-instance/databases/employee"
- type = "MASKING"
-}
-
data "bytebase_policy" "masking_exception_policy" {
parent = "projects/project-sample"
type = "MASKING_EXCEPTION"
}
-output "masking_policy" {
- value = data.bytebase_policy.masking_policy
-}
-
output "masking_exception_policy" {
value = data.bytebase_policy.masking_exception_policy
}
diff --git a/examples/projects/main.tf b/examples/projects/main.tf
index 615156a..5421c88 100644
--- a/examples/projects/main.tf
+++ b/examples/projects/main.tf
@@ -2,7 +2,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.5"
+ version = "1.0.6"
# 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 095fedc..8dc2b66 100644
--- a/examples/settings/main.tf
+++ b/examples/settings/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.5"
+ version = "1.0.6"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
diff --git a/examples/setup/approval_flow.tf b/examples/setup/approval_flow.tf
index e69de29..de0bce1 100644
--- a/examples/setup/approval_flow.tf
+++ b/examples/setup/approval_flow.tf
@@ -0,0 +1,52 @@
+
+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 {
+ rules {
+ flow {
+ title = "DBA -> OWNER"
+ description = "Need DBA and workspace owner approval"
+ creator = "users/support@bytebase.com"
+
+ # Approval flow following the step order.
+ steps {
+ type = "GROUP"
+ node = "WORKSPACE_DBA"
+ }
+
+ steps {
+ type = "GROUP"
+ node = "WORKSPACE_OWNER"
+ }
+ }
+
+ # Match any condition will trigger this approval flow.
+ conditions {
+ source = "DML"
+ level = "MODERATE"
+ }
+ conditions {
+ source = "DDL"
+ level = "HIGH"
+ }
+ }
+ }
+}
diff --git a/examples/setup/data_masking.tf b/examples/setup/data_masking.tf
index 0a52d1a..74be919 100644
--- a/examples/setup/data_masking.tf
+++ b/examples/setup/data_masking.tf
@@ -1,30 +1,34 @@
-resource "bytebase_policy" "masking_policy" {
+resource "bytebase_database_catalog" "employee_catalog" {
depends_on = [
bytebase_instance.test
]
- parent = "instances/test-sample-instance/databases/employee"
- type = "MASKING"
- enforce = true
- inherit_from_parent = false
+ database = "instances/test-sample-instance/databases/employee"
- masking_policy {
- mask_data {
- table = "salary"
- column = "amount"
- masking_level = "FULL"
- }
- mask_data {
- table = "salary"
- column = "emp_no"
- masking_level = "NONE"
+ schemas {
+ tables {
+ name = "salary"
+ columns {
+ name = "amount"
+ semantic_type = "default"
+ classification = "1-1-1"
+ }
+ columns {
+ name = "emp_no"
+ semantic_type = "default-partial"
+ labels = {
+ tenant = "example"
+ region = "asia"
+ }
+ }
}
}
}
resource "bytebase_policy" "masking_exception_policy" {
depends_on = [
- bytebase_project.sample_project
+ bytebase_project.sample_project,
+ bytebase_instance.test
]
parent = bytebase_project.sample_project.name
@@ -34,20 +38,18 @@ resource "bytebase_policy" "masking_exception_policy" {
masking_exception_policy {
exceptions {
- database = "instances/test-sample-instance/databases/employee"
- table = "salary"
- column = "amount"
- masking_level = "NONE"
- member = "user:ed@bytebase.com"
- action = "EXPORT"
+ database = "instances/test-sample-instance/databases/employee"
+ table = "salary"
+ column = "amount"
+ member = "user:ed@bytebase.com"
+ action = "EXPORT"
}
exceptions {
- database = "instances/test-sample-instance/databases/employee"
- table = "salary"
- column = "amount"
- masking_level = "NONE"
- member = "user:ed@bytebase.com"
- action = "QUERY"
+ database = "instances/test-sample-instance/databases/employee"
+ table = "salary"
+ column = "amount"
+ member = "user:ed@bytebase.com"
+ action = "QUERY"
}
}
}
diff --git a/examples/setup/main.tf b/examples/setup/main.tf
index 36a58d6..580fef4 100644
--- a/examples/setup/main.tf
+++ b/examples/setup/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.5"
+ version = "1.0.6"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
diff --git a/examples/users/main.tf b/examples/users/main.tf
index 2baadf6..a571ee7 100644
--- a/examples/users/main.tf
+++ b/examples/users/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.5"
+ version = "1.0.6"
# 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 4e5b9fc..5d9c9f7 100644
--- a/examples/vcs/main.tf
+++ b/examples/vcs/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.5"
+ version = "1.0.6"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
diff --git a/go.mod b/go.mod
index 449b615..b740a5f 100644
--- a/go.mod
+++ b/go.mod
@@ -1,17 +1,17 @@
module github.com/bytebase/terraform-provider-bytebase
-go 1.23.2
+go 1.23.4
require (
- github.com/bytebase/bytebase v0.0.0-20241205093738-38cba35b1547
+ github.com/bytebase/bytebase v0.0.0-20250106034445-3306b7f8a2cb
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
github.com/hashicorp/terraform-plugin-docs v0.13.0
github.com/hashicorp/terraform-plugin-log v0.7.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0
github.com/pkg/errors v0.9.1
- google.golang.org/genproto v0.0.0-20241118233622-e639e219e697
- google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583
- google.golang.org/protobuf v1.35.2
+ google.golang.org/genproto v0.0.0-20241230172942-26aa7a208def
+ google.golang.org/genproto/googleapis/api v0.0.0-20241230172942-26aa7a208def
+ google.golang.org/protobuf v1.36.1
)
require (
@@ -27,7 +27,7 @@ require (
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@@ -64,11 +64,11 @@ require (
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
github.com/vmihailenco/tagparser v0.1.1 // indirect
github.com/zclconf/go-cty v1.11.0 // indirect
- golang.org/x/crypto v0.29.0 // indirect
- golang.org/x/net v0.31.0 // indirect
- golang.org/x/sys v0.27.0 // indirect
- golang.org/x/text v0.20.0 // indirect
+ golang.org/x/crypto v0.31.0 // indirect
+ golang.org/x/net v0.33.0 // indirect
+ golang.org/x/sys v0.28.0 // indirect
+ golang.org/x/text v0.21.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect
- google.golang.org/grpc v1.68.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20241230172942-26aa7a208def // indirect
+ google.golang.org/grpc v1.69.2 // indirect
)
diff --git a/go.sum b/go.sum
index 7e3736f..4077d7c 100644
--- a/go.sum
+++ b/go.sum
@@ -29,8 +29,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bytebase/bytebase v0.0.0-20241205093738-38cba35b1547 h1:4iW1KxDfqJkTbebeuiVgzxxrN6X/Ask1ic9MDFmIfno=
-github.com/bytebase/bytebase v0.0.0-20241205093738-38cba35b1547/go.mod h1:3eFZBpvlGqEmGwGK6x7sgdUth4I6uNCNTw57fRFuXr8=
+github.com/bytebase/bytebase v0.0.0-20250106034445-3306b7f8a2cb h1:wzMO4Lr1bh5YH0+y67zg1xF+AgXaQuj3A1aotSXNS1M=
+github.com/bytebase/bytebase v0.0.0-20250106034445-3306b7f8a2cb/go.mod h1:VW4jbd0t2jCPazRYDVBFrLCNa+Q8PHqPQV8hP2g0OiU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -53,6 +53,10 @@ github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -70,8 +74,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -213,8 +217,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
@@ -230,6 +234,16 @@ github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uU
github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0=
github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
+go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
+go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
+go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
+go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
+go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
+go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
+go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
+go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
+go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
+go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -237,8 +251,8 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
-golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
+golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
+golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -249,8 +263,8 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
-golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
-golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
+golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
+golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -272,31 +286,31 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
-golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
-golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
-google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
-google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583 h1:v+j+5gpj0FopU0KKLDGfDo9ZRRpKdi5UBrCP0f76kuY=
-google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
-google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
-google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
-google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
-google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/genproto v0.0.0-20241230172942-26aa7a208def h1:uz2w9bZTljGBXc3ugqrL/KOsVhQuODYyLNYXUTKrh6M=
+google.golang.org/genproto v0.0.0-20241230172942-26aa7a208def/go.mod h1:zNtPaqLK0Wbf5PaSZDYDR+1t5rQoBAIMh06tpzkjvY8=
+google.golang.org/genproto/googleapis/api v0.0.0-20241230172942-26aa7a208def h1:0Km0hi+g2KXbXL0+riZzSCKz23f4MmwicuEb00JeonI=
+google.golang.org/genproto/googleapis/api v0.0.0-20241230172942-26aa7a208def/go.mod h1:u2DoMSpCXjrzqLdobRccQMc9wrnMAJ1DLng0a2yqM2Q=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241230172942-26aa7a208def h1:4P81qv5JXI/sDNae2ClVx88cgDDA6DPilADkG9tYKz8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241230172942-26aa7a208def/go.mod h1:bdAgzvd4kFrpykc5/AC2eLUiegK9T/qxZHD4hXYf/ho=
+google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
+google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
+google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
+google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/provider/data_source_database_catalog.go b/provider/data_source_database_catalog.go
new file mode 100644
index 0000000..e634e71
--- /dev/null
+++ b/provider/data_source_database_catalog.go
@@ -0,0 +1,140 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+
+ v1pb "github.com/bytebase/bytebase/proto/generated-go/v1"
+
+ "github.com/bytebase/terraform-provider-bytebase/api"
+ "github.com/bytebase/terraform-provider-bytebase/provider/internal"
+)
+
+func dataSourceDatabaseCatalog() *schema.Resource {
+ return &schema.Resource{
+ Description: "The database catalog data source.",
+ ReadContext: dataSourceDatabaseCatalogRead,
+ Schema: map[string]*schema.Schema{
+ "database": {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "The database full name in instances/{instance}/databases/{database} format",
+ ValidateDiagFunc: internal.ResourceNameValidation(
+ regexp.MustCompile(fmt.Sprintf("^%s%s/%s%s$", internal.InstanceNamePrefix, internal.ResourceIDPattern, internal.DatabaseIDPrefix, internal.ResourceIDPattern)),
+ ),
+ },
+ "schemas": {
+ Computed: true,
+ Type: schema.TypeList,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tables": {
+ Computed: true,
+ Type: schema.TypeList,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "classification": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "The classification id",
+ },
+ "columns": {
+ Computed: true,
+ Type: schema.TypeList,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "semantic_type": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "The semantic type id",
+ },
+ "classification": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "The classification id",
+ },
+ "labels": {
+ Type: schema.TypeMap,
+ Computed: true,
+ Elem: &schema.Schema{Type: schema.TypeString},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourceDatabaseCatalogRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
+ c := m.(api.Client)
+ database := d.Get("database").(string)
+
+ catalog, err := c.GetDatabaseCatalog(ctx, database)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ d.SetId(catalog.Name)
+
+ return setDatabaseCatalog(d, catalog)
+}
+
+func setDatabaseCatalog(d *schema.ResourceData, catalog *v1pb.DatabaseCatalog) diag.Diagnostics {
+ database := getDatabaseFullNameFromCatalog(catalog.Name)
+ if err := d.Set("database", database); err != nil {
+ return diag.Errorf("cannot set database: %s", err.Error())
+ }
+
+ schemaList := []interface{}{}
+ for _, schema := range catalog.Schemas {
+ rawSchema := map[string]interface{}{}
+
+ tableList := []interface{}{}
+ for _, table := range schema.Tables {
+ rawTable := map[string]interface{}{}
+ rawTable["name"] = table.Name
+ rawTable["classification"] = table.Classification
+
+ columnList := []interface{}{}
+ for _, column := range table.GetColumns().Columns {
+ rawColumn := map[string]interface{}{}
+ rawColumn["name"] = column.Name
+ rawColumn["semantic_type"] = column.SemanticType
+ rawColumn["classification"] = column.Classification
+ rawColumn["labels"] = column.Labels
+ columnList = append(columnList, rawColumn)
+ }
+ rawTable["columns"] = columnList
+ tableList = append(tableList, rawTable)
+ }
+ rawSchema["tables"] = tableList
+ schemaList = append(schemaList, rawSchema)
+ }
+
+ if err := d.Set("schemas", schemaList); err != nil {
+ return diag.Errorf("cannot set schemas: %s", err.Error())
+ }
+ return nil
+}
diff --git a/provider/data_source_policy.go b/provider/data_source_policy.go
index 2158dde..51e475f 100644
--- a/provider/data_source_policy.go
+++ b/provider/data_source_policy.go
@@ -44,7 +44,6 @@ func dataSourcePolicy() *schema.Resource {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
- v1pb.PolicyType_MASKING.String(),
v1pb.PolicyType_MASKING_EXCEPTION.String(),
}, false),
Description: "The policy type.",
@@ -64,7 +63,6 @@ func dataSourcePolicy() *schema.Resource {
Computed: true,
Description: "Decide if the policy is enforced.",
},
- "masking_policy": getMaskingPolicySchema(true),
"masking_exception_policy": getMaskingExceptionPolicySchema(true),
},
}
@@ -119,15 +117,6 @@ func getMaskingExceptionPolicySchema(computed bool) *schema.Schema {
ValidateFunc: validation.StringIsNotEmpty,
Description: "The member in user:{email} or group:{email} format.",
},
- "masking_level": {
- Type: schema.TypeString,
- Computed: computed,
- Optional: true,
- ValidateFunc: validation.StringInSlice([]string{
- v1pb.MaskingLevel_NONE.String(),
- v1pb.MaskingLevel_PARTIAL.String(),
- }, false),
- },
"action": {
Type: schema.TypeString,
Computed: computed,
@@ -151,69 +140,6 @@ func getMaskingExceptionPolicySchema(computed bool) *schema.Schema {
}
}
-func getMaskingPolicySchema(computed bool) *schema.Schema {
- return &schema.Schema{
- Computed: computed,
- Optional: true,
- Default: nil,
- Type: schema.TypeList,
- MinItems: 0,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "mask_data": {
- MinItems: 0,
- Computed: computed,
- Optional: true,
- Default: nil,
- Type: schema.TypeList,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "schema": {
- Type: schema.TypeString,
- Computed: computed,
- Optional: true,
- },
- "table": {
- Type: schema.TypeString,
- Computed: computed,
- Optional: true,
- ValidateFunc: validation.StringIsNotEmpty,
- },
- "column": {
- Type: schema.TypeString,
- Computed: computed,
- Optional: true,
- ValidateFunc: validation.StringIsNotEmpty,
- },
- "full_masking_algorithm_id": {
- Type: schema.TypeString,
- Computed: computed,
- Optional: true,
- },
- "partial_masking_algorithm_id": {
- Type: schema.TypeString,
- Computed: computed,
- Optional: true,
- },
- "masking_level": {
- Type: schema.TypeString,
- Computed: computed,
- Optional: true,
- ValidateFunc: validation.StringInSlice([]string{
- v1pb.MaskingLevel_NONE.String(),
- v1pb.MaskingLevel_PARTIAL.String(),
- v1pb.MaskingLevel_FULL.String(),
- }, false),
- },
- },
- },
- },
- },
- },
- }
-}
-
func dataSourcePolicyRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(api.Client)
@@ -245,49 +171,25 @@ func setPolicyMessage(d *schema.ResourceData, policy *v1pb.Policy) diag.Diagnost
return diag.Errorf("cannot set enforce for policy: %s", err.Error())
}
- if p := policy.GetMaskingPolicy(); p != nil {
- if err := d.Set("masking_policy", flattenMaskingPolicy(p)); err != nil {
- return diag.Errorf("cannot set masking_policy: %s", err.Error())
- }
- }
if p := policy.GetMaskingExceptionPolicy(); p != nil {
exceptionPolicy, err := flattenMaskingExceptionPolicy(p)
if err != nil {
return diag.FromErr(err)
}
if err := d.Set("masking_exception_policy", exceptionPolicy); err != nil {
- return diag.Errorf("cannot set masking_policy: %s", err.Error())
+ return diag.Errorf("cannot set masking_exception_policy: %s", err.Error())
}
}
return nil
}
-func flattenMaskingPolicy(p *v1pb.MaskingPolicy) []interface{} {
- maskDataList := []interface{}{}
- for _, maskData := range p.MaskData {
- raw := map[string]interface{}{}
- raw["schema"] = maskData.Schema
- raw["table"] = maskData.Table
- raw["column"] = maskData.Column
- raw["full_masking_algorithm_id"] = maskData.FullMaskingAlgorithmId
- raw["partial_masking_algorithm_id"] = maskData.PartialMaskingAlgorithmId
- raw["masking_level"] = maskData.MaskingLevel.String()
- maskDataList = append(maskDataList, raw)
- }
- policy := map[string]interface{}{
- "mask_data": maskDataList,
- }
- return []interface{}{policy}
-}
-
func flattenMaskingExceptionPolicy(p *v1pb.MaskingExceptionPolicy) ([]interface{}, error) {
exceptionList := []interface{}{}
for _, exception := range p.MaskingExceptions {
raw := map[string]interface{}{}
raw["member"] = exception.Member
raw["action"] = exception.Action.String()
- raw["masking_level"] = exception.MaskingLevel.String()
if exception.Condition == nil || exception.Condition.Expression == "" {
return nil, errors.Errorf("invalid exception policy condition")
diff --git a/provider/data_source_policy_list.go b/provider/data_source_policy_list.go
index 10bcf64..6f8548a 100644
--- a/provider/data_source_policy_list.go
+++ b/provider/data_source_policy_list.go
@@ -33,7 +33,7 @@ func dataSourcePolicyList() *schema.Resource {
// project policy
regexp.MustCompile(fmt.Sprintf("^%s%s$", internal.ProjectNamePrefix, internal.ResourceIDPattern)),
// database policy
- regexp.MustCompile(fmt.Sprintf("^%s%s%s%s$", internal.InstanceNamePrefix, internal.ResourceIDPattern, internal.DatabaseIDPrefix, internal.ResourceIDPattern)),
+ regexp.MustCompile(fmt.Sprintf("^%s%s/%s%s$", internal.InstanceNamePrefix, internal.ResourceIDPattern, internal.DatabaseIDPrefix, internal.ResourceIDPattern)),
),
Description: "The policy parent name for the policy, support projects/{resource id}, environments/{resource id}, instances/{resource id}, or instances/{resource id}/databases/{database name}",
},
@@ -62,7 +62,6 @@ func dataSourcePolicyList() *schema.Resource {
Computed: true,
Description: "Decide if the policy is enforced.",
},
- "masking_policy": getMaskingPolicySchema(true),
"masking_exception_policy": getMaskingExceptionPolicySchema(true),
},
},
@@ -87,9 +86,6 @@ func dataSourcePolicyListRead(ctx context.Context, d *schema.ResourceData, m int
raw["inherit_from_parent"] = policy.InheritFromParent
raw["enforce"] = policy.Enforce
- if p := policy.GetMaskingPolicy(); p != nil {
- raw["masking_policy"] = flattenMaskingPolicy(p)
- }
if p := policy.GetMaskingExceptionPolicy(); p != nil {
exceptionPolicy, err := flattenMaskingExceptionPolicy(p)
if err != nil {
diff --git a/provider/data_source_policy_list_test.go b/provider/data_source_policy_list_test.go
index 7dfb26b..ae37bb4 100644
--- a/provider/data_source_policy_list_test.go
+++ b/provider/data_source_policy_list_test.go
@@ -28,12 +28,12 @@ func TestAccPolicyListDataSource(t *testing.T) {
),
internal.GetTestStepForDataSourceList(
testAccCheckPolicyResource(
- "masking_policy",
- "instances/test-sample-instance/databases/employee",
- getMaskingPolicy("salary", "amount", v1pb.MaskingLevel_FULL),
- v1pb.PolicyType_MASKING,
+ "masking_exception_policy",
+ "projects/project-sample",
+ getMaskingExceptionPolicy("instances/test-sample-instance/databases/employee", "salary", "amount"),
+ v1pb.PolicyType_MASKING_EXCEPTION,
),
- "bytebase_policy.masking_policy",
+ "bytebase_policy.masking_exception_policy",
"bytebase_policy_list",
"after",
"policies",
diff --git a/provider/data_source_policy_test.go b/provider/data_source_policy_test.go
index 06dedbb..bf1328b 100644
--- a/provider/data_source_policy_test.go
+++ b/provider/data_source_policy_test.go
@@ -23,24 +23,23 @@ func TestAccPolicyDataSource(t *testing.T) {
{
Config: testAccCheckPolicyDataSource(
testAccCheckPolicyResource(
- "masking_policy",
- "instances/test-sample-instance/databases/employee",
- getMaskingPolicy("salary", "amount", v1pb.MaskingLevel_FULL),
- v1pb.PolicyType_MASKING,
+ "masking_exception_policy",
+ "projects/project-sample",
+ getMaskingExceptionPolicy("instances/test-sample-instance/databases/employee", "salary", "amount"),
+ v1pb.PolicyType_MASKING_EXCEPTION,
),
- "masking_policy",
- "instances/test-sample-instance/databases/employee",
- "bytebase_policy.masking_policy",
- v1pb.PolicyType_MASKING,
+ "masking_exception_policy",
+ "projects/project-sample",
+ "bytebase_policy.masking_exception_policy",
+ v1pb.PolicyType_MASKING_EXCEPTION,
),
Check: resource.ComposeTestCheckFunc(
- internal.TestCheckResourceExists("data.bytebase_policy.masking_policy"),
- resource.TestCheckResourceAttr("data.bytebase_policy.masking_policy", "type", v1pb.PolicyType_MASKING.String()),
- resource.TestCheckResourceAttr("data.bytebase_policy.masking_policy", "masking_policy.#", "1"),
- resource.TestCheckResourceAttr("data.bytebase_policy.masking_policy", "masking_policy.0.mask_data.#", "1"),
- resource.TestCheckResourceAttr("data.bytebase_policy.masking_policy", "masking_policy.0.mask_data.0.table", "salary"),
- resource.TestCheckResourceAttr("data.bytebase_policy.masking_policy", "masking_policy.0.mask_data.0.column", "amount"),
- resource.TestCheckResourceAttr("data.bytebase_policy.masking_policy", "masking_policy.0.mask_data.0.masking_level", v1pb.MaskingLevel_FULL.String()),
+ internal.TestCheckResourceExists("data.bytebase_policy.masking_exception_policy"),
+ resource.TestCheckResourceAttr("data.bytebase_policy.masking_exception_policy", "type", v1pb.PolicyType_MASKING_EXCEPTION.String()),
+ resource.TestCheckResourceAttr("data.bytebase_policy.masking_exception_policy", "masking_exception_policy.#", "1"),
+ resource.TestCheckResourceAttr("data.bytebase_policy.masking_exception_policy", "masking_exception_policy.0.exceptions.#", "1"),
+ resource.TestCheckResourceAttr("data.bytebase_policy.masking_exception_policy", "masking_exception_policy.0.exceptions.0.table", "salary"),
+ resource.TestCheckResourceAttr("data.bytebase_policy.masking_exception_policy", "masking_exception_policy.0.exceptions.0.column", "amount"),
),
},
},
@@ -59,11 +58,11 @@ func TestAccPolicyDataSource_NotFound(t *testing.T) {
Config: testAccCheckPolicyDataSource(
"",
"policy",
- "instances/test-sample-instance/databases/employee",
+ "projects/project-sample",
"",
- v1pb.PolicyType_MASKING,
+ v1pb.PolicyType_MASKING_EXCEPTION,
),
- ExpectError: regexp.MustCompile("Cannot found policy instances/test-sample-instance/databases/employee/policies/MASKING"),
+ ExpectError: regexp.MustCompile("Cannot found policy projects/project-sample/policies/MASKING_EXCEPTION"),
},
},
})
diff --git a/provider/data_source_setting.go b/provider/data_source_setting.go
index 91c8c4d..5b5c6f7 100644
--- a/provider/data_source_setting.go
+++ b/provider/data_source_setting.go
@@ -38,10 +38,11 @@ func dataSourceSetting() *schema.Resource {
func getExternalApprovalSetting(computed bool) *schema.Schema {
return &schema.Schema{
- Computed: computed,
- Optional: true,
- Default: nil,
- Type: schema.TypeList,
+ 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": {
@@ -78,10 +79,11 @@ func getExternalApprovalSetting(computed bool) *schema.Schema {
func getWorkspaceApprovalSetting(computed bool) *schema.Schema {
return &schema.Schema{
- Computed: computed,
- Optional: true,
- Default: nil,
- Type: schema.TypeList,
+ Computed: computed,
+ Optional: true,
+ Default: nil,
+ Type: schema.TypeList,
+ Description: "Configure risk level and approval flow for different tasks. Require ENTERPRISE subscription.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"rules": {
diff --git a/provider/internal/mock_client.go b/provider/internal/mock_client.go
index 3c28658..7502f87 100644
--- a/provider/internal/mock_client.go
+++ b/provider/internal/mock_client.go
@@ -20,6 +20,7 @@ var policyMap map[string]*v1pb.Policy
var projectMap map[string]*v1pb.Project
var projectIAMMap map[string]*v1pb.IamPolicy
var databaseMap map[string]*v1pb.Database
+var databaseCatalogMap map[string]*v1pb.DatabaseCatalog
var settingMap map[string]*v1pb.Setting
var vcsProviderMap map[string]*v1pb.VCSProvider
var vcsConnectorMap map[string]*v1pb.VCSConnector
@@ -33,6 +34,7 @@ func init() {
projectMap = map[string]*v1pb.Project{}
projectIAMMap = map[string]*v1pb.IamPolicy{}
databaseMap = map[string]*v1pb.Database{}
+ databaseCatalogMap = map[string]*v1pb.DatabaseCatalog{}
settingMap = map[string]*v1pb.Setting{}
vcsProviderMap = map[string]*v1pb.VCSProvider{}
vcsConnectorMap = map[string]*v1pb.VCSConnector{}
@@ -47,6 +49,7 @@ type mockClient struct {
projectMap map[string]*v1pb.Project
projectIAMMap map[string]*v1pb.IamPolicy
databaseMap map[string]*v1pb.Database
+ databaseCatalogMap map[string]*v1pb.DatabaseCatalog
settingMap map[string]*v1pb.Setting
vcsProviderMap map[string]*v1pb.VCSProvider
vcsConnectorMap map[string]*v1pb.VCSConnector
@@ -64,6 +67,7 @@ func newMockClient(_, _, _ string) (api.Client, error) {
projectMap: projectMap,
projectIAMMap: projectIAMMap,
databaseMap: databaseMap,
+ databaseCatalogMap: databaseCatalogMap,
settingMap: settingMap,
vcsProviderMap: vcsProviderMap,
vcsConnectorMap: vcsConnectorMap,
@@ -327,17 +331,6 @@ func (c *mockClient) UpsertPolicy(_ context.Context, patch *v1pb.Policy, updateM
}
switch policyType {
- case v1pb.PolicyType_MASKING:
- if !existed {
- if patch.GetMaskingPolicy() == nil {
- return nil, errors.Errorf("payload is required to create the policy")
- }
- }
- if v := patch.GetMaskingPolicy(); v != nil {
- policy.Policy = &v1pb.Policy_MaskingPolicy{
- MaskingPolicy: v,
- }
- }
case v1pb.PolicyType_MASKING_EXCEPTION:
if !existed {
if patch.GetMaskingExceptionPolicy() == nil {
@@ -419,6 +412,22 @@ func (c *mockClient) UpdateDatabase(ctx context.Context, patch *v1pb.Database, u
return db, nil
}
+// GetDatabaseCatalog gets the database catalog by the database full name.
+func (c *mockClient) GetDatabaseCatalog(_ context.Context, databaseName string) (*v1pb.DatabaseCatalog, error) {
+ db, ok := c.databaseCatalogMap[databaseName]
+ if !ok {
+ return nil, errors.Errorf("Cannot found database catalog %s", databaseName)
+ }
+
+ return db, nil
+}
+
+// UpdateDatabaseCatalog patches the database catalog.
+func (c *mockClient) UpdateDatabaseCatalog(_ context.Context, patch *v1pb.DatabaseCatalog, _ []string) (*v1pb.DatabaseCatalog, error) {
+ c.databaseCatalogMap[patch.Name] = patch
+ return patch, nil
+}
+
// GetProject gets the project by resource id.
func (c *mockClient) GetProject(_ context.Context, projectName string) (*v1pb.Project, error) {
proj, ok := c.projectMap[projectName]
diff --git a/provider/internal/utils.go b/provider/internal/utils.go
index ba466db..5c9d75e 100644
--- a/provider/internal/utils.go
+++ b/provider/internal/utils.go
@@ -37,6 +37,8 @@ const (
GroupNamePrefix = "groups/"
// RoleNamePrefix is the prefix for role name.
RoleNamePrefix = "roles/"
+ // DatabaseCatalogNameSuffix is the suffix for the database catalog name.
+ DatabaseCatalogNameSuffix = "/catalog"
// ResourceIDPattern is the pattern for resource id.
ResourceIDPattern = "[a-z]([a-z0-9-]{0,61}[a-z0-9])?"
)
diff --git a/provider/provider.go b/provider/provider.go
index ed7a5fe..044781c 100644
--- a/provider/provider.go
+++ b/provider/provider.go
@@ -66,17 +66,19 @@ func NewProvider() *schema.Provider {
"bytebase_user_list": dataSourceUserList(),
"bytebase_group": dataSourceGroup(),
"bytebase_group_list": dataSourceGroupList(),
+ "bytebase_database_catalog": dataSourceDatabaseCatalog(),
},
ResourcesMap: map[string]*schema.Resource{
- "bytebase_environment": resourceEnvironment(),
- "bytebase_instance": resourceInstance(),
- "bytebase_policy": resourcePolicy(),
- "bytebase_project": resourceProjct(),
- "bytebase_setting": resourceSetting(),
- "bytebase_vcs_provider": resourceVCSProvider(),
- "bytebase_vcs_connector": resourceVCSConnector(),
- "bytebase_user": resourceUser(),
- "bytebase_group": resourceGroup(),
+ "bytebase_environment": resourceEnvironment(),
+ "bytebase_instance": resourceInstance(),
+ "bytebase_policy": resourcePolicy(),
+ "bytebase_project": resourceProjct(),
+ "bytebase_setting": resourceSetting(),
+ "bytebase_vcs_provider": resourceVCSProvider(),
+ "bytebase_vcs_connector": resourceVCSConnector(),
+ "bytebase_user": resourceUser(),
+ "bytebase_group": resourceGroup(),
+ "bytebase_database_catalog": resourceDatabaseCatalog(),
},
}
}
diff --git a/provider/resource_database_catalog.go b/provider/resource_database_catalog.go
new file mode 100644
index 0000000..3d83369
--- /dev/null
+++ b/provider/resource_database_catalog.go
@@ -0,0 +1,241 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+ "strings"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+ "github.com/pkg/errors"
+
+ v1pb "github.com/bytebase/bytebase/proto/generated-go/v1"
+
+ "github.com/bytebase/terraform-provider-bytebase/api"
+ "github.com/bytebase/terraform-provider-bytebase/provider/internal"
+)
+
+func resourceDatabaseCatalog() *schema.Resource {
+ return &schema.Resource{
+ Description: "The database catalog resource.",
+ CreateContext: resourceDatabaseCatalogCreate,
+ ReadContext: resourceDatabaseCatalogRead,
+ UpdateContext: resourceDatabaseCatalogUpdate,
+ DeleteContext: resourceDatabaseCatalogDelete,
+ Importer: &schema.ResourceImporter{
+ StateContext: schema.ImportStatePassthroughContext,
+ },
+ Schema: map[string]*schema.Schema{
+ "database": {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "The database full name in instances/{instance}/databases/{database} format",
+ ValidateDiagFunc: internal.ResourceNameValidation(
+ regexp.MustCompile(fmt.Sprintf("^%s%s/%s%s$", internal.InstanceNamePrefix, internal.ResourceIDPattern, internal.DatabaseIDPrefix, internal.ResourceIDPattern)),
+ ),
+ },
+ "schemas": {
+ Required: true,
+ Type: schema.TypeList,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Optional: true,
+ Default: "",
+ },
+ "tables": {
+ Required: true,
+ Type: schema.TypeList,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ ValidateFunc: validation.StringIsNotEmpty,
+ },
+ "classification": {
+ Type: schema.TypeString,
+ Optional: true,
+ Default: "",
+ Description: "The classification id",
+ },
+ "columns": {
+ Required: true,
+ Type: schema.TypeList,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "semantic_type": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "The semantic type id",
+ },
+ "classification": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "The classification id",
+ },
+ "labels": {
+ Type: schema.TypeMap,
+ Optional: true,
+ Elem: &schema.Schema{Type: schema.TypeString},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func resourceDatabaseCatalogRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
+ c := m.(api.Client)
+ catelogName := d.Id()
+ database := getDatabaseFullNameFromCatalog(catelogName)
+
+ catalog, err := c.GetDatabaseCatalog(ctx, database)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ return setDatabaseCatalog(d, catalog)
+}
+
+func getDatabaseFullNameFromCatalog(catalog string) string {
+ return strings.TrimSuffix(catalog, internal.DatabaseCatalogNameSuffix)
+}
+
+func resourceDatabaseCatalogCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
+ c := m.(api.Client)
+ database := d.Get("database").(string)
+
+ catalog, err := convertToDatabaseCatalog(d)
+ if err != nil {
+ return diag.Errorf("failed to convert catalog %v with error: %v", database, err.Error())
+ }
+
+ if _, err := c.UpdateDatabaseCatalog(ctx, catalog, []string{}); err != nil {
+ return diag.Errorf("failed to update catalog %v with error: %v", database, err.Error())
+ }
+
+ d.SetId(catalog.Name)
+
+ var diags diag.Diagnostics
+ diag := resourceDatabaseCatalogRead(ctx, d, m)
+ if diag != nil {
+ diags = append(diags, diag...)
+ }
+
+ return diags
+}
+
+func resourceDatabaseCatalogUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
+ c := m.(api.Client)
+ catelogName := d.Id()
+ database := getDatabaseFullNameFromCatalog(catelogName)
+
+ catalog, err := convertToDatabaseCatalog(d)
+ if err != nil {
+ return diag.Errorf("failed to convert catalog %v with error: %v", database, err.Error())
+ }
+
+ if _, err := c.UpdateDatabaseCatalog(ctx, catalog, []string{}); err != nil {
+ return diag.Errorf("failed to update catalog %v with error: %v", database, err.Error())
+ }
+
+ if _, err := c.UpdateDatabaseCatalog(ctx, catalog, []string{}); err != nil {
+ return diag.Errorf("failed to update catalog %v with error: %v", database, err.Error())
+ }
+
+ var diags diag.Diagnostics
+ diag := resourceDatabaseCatalogRead(ctx, d, m)
+ if diag != nil {
+ diags = append(diags, diag...)
+ }
+
+ return diags
+}
+
+func resourceDatabaseCatalogDelete(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics {
+ d.SetId("")
+ return nil
+}
+
+func convertToDatabaseCatalog(d *schema.ResourceData) (*v1pb.DatabaseCatalog, error) {
+ database, ok := d.Get("database").(string)
+ if !ok || database == "" {
+ return nil, errors.Errorf("invalid database")
+ }
+ rawSchemaList, ok := d.Get("schemas").([]interface{})
+ if !ok {
+ return nil, errors.Errorf("invalid schemas")
+ }
+
+ catalog := &v1pb.DatabaseCatalog{
+ Name: fmt.Sprintf("%s%s", database, internal.DatabaseCatalogNameSuffix),
+ Schemas: []*v1pb.SchemaCatalog{},
+ }
+
+ for _, schema := range rawSchemaList {
+ rawSchema := schema.(map[string]interface{})
+ schema := &v1pb.SchemaCatalog{
+ Name: rawSchema["name"].(string),
+ }
+
+ rawTableList, ok := rawSchema["tables"].([]interface{})
+ if !ok {
+ return nil, errors.Errorf("invalid tables")
+ }
+ for _, table := range rawTableList {
+ rawTable := table.(map[string]interface{})
+ table := &v1pb.TableCatalog{
+ Name: rawTable["name"].(string),
+ Classification: rawTable["classification"].(string),
+ }
+
+ columnList := []*v1pb.ColumnCatalog{}
+ rawColumnList, ok := rawTable["columns"].([]interface{})
+ if !ok {
+ return nil, errors.Errorf("invalid columns")
+ }
+ for _, column := range rawColumnList {
+ rawColumn := column.(map[string]interface{})
+ labels := map[string]string{}
+ for key, val := range rawColumn["labels"].(map[string]interface{}) {
+ labels[key] = val.(string)
+ }
+
+ column := &v1pb.ColumnCatalog{
+ Name: rawColumn["name"].(string),
+ SemanticType: rawColumn["semantic_type"].(string),
+ Classification: rawColumn["classification"].(string),
+ Labels: labels,
+ }
+ columnList = append(columnList, column)
+ }
+
+ table.Kind = &v1pb.TableCatalog_Columns_{
+ Columns: &v1pb.TableCatalog_Columns{
+ Columns: columnList,
+ },
+ }
+
+ schema.Tables = append(schema.Tables, table)
+ }
+
+ catalog.Schemas = append(catalog.Schemas, schema)
+ }
+
+ return catalog, nil
+}
diff --git a/provider/resource_environment.go b/provider/resource_environment.go
index e783870..35e08d6 100644
--- a/provider/resource_environment.go
+++ b/provider/resource_environment.go
@@ -59,7 +59,7 @@ func resourceEnvironment() *schema.Resource {
v1pb.EnvironmentTier_PROTECTED.String(),
v1pb.EnvironmentTier_UNPROTECTED.String(),
}, false),
- Description: "If marked as PROTECTED, developers cannot execute any query on this environment's databases using SQL Editor by default.",
+ Description: "If marked as PROTECTED, developers cannot execute any query on this environment's databases using SQL Editor by default. Require ENTERPRISE subscription.",
},
},
}
diff --git a/provider/resource_group.go b/provider/resource_group.go
index 1056442..8b95c15 100644
--- a/provider/resource_group.go
+++ b/provider/resource_group.go
@@ -18,7 +18,7 @@ import (
func resourceGroup() *schema.Resource {
return &schema.Resource{
- Description: "The group resource.",
+ Description: "The group resource. Workspace domain is required for creating groups.",
ReadContext: resourceGroupRead,
DeleteContext: resourceGroupDelete,
CreateContext: resourceGroupCreate,
diff --git a/provider/resource_policy.go b/provider/resource_policy.go
index 0c4c064..ccc211d 100644
--- a/provider/resource_policy.go
+++ b/provider/resource_policy.go
@@ -51,7 +51,6 @@ func resourcePolicy() *schema.Resource {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
- v1pb.PolicyType_MASKING.String(),
v1pb.PolicyType_MASKING_EXCEPTION.String(),
}, false),
Description: "The policy type.",
@@ -73,7 +72,6 @@ func resourcePolicy() *schema.Resource {
Default: false,
Description: "Decide if the policy should inherit from the parent.",
},
- "masking_policy": getMaskingPolicySchema(false),
"masking_exception_policy": getMaskingExceptionPolicySchema(false),
},
}
@@ -125,16 +123,7 @@ func resourcePolicyCreate(ctx context.Context, d *schema.ResourceData, m interfa
Type: policyType,
}
- switch policyType {
- case v1pb.PolicyType_MASKING:
- maskingPolicy, err := convertToMaskingPolicy(d)
- if err != nil {
- return diag.FromErr(err)
- }
- patch.Policy = &v1pb.Policy_MaskingPolicy{
- MaskingPolicy: maskingPolicy,
- }
- case v1pb.PolicyType_MASKING_EXCEPTION:
+ if policyType == v1pb.PolicyType_MASKING_EXCEPTION {
maskingExceptionPolicy, err := convertToMaskingExceptionPolicy(d)
if err != nil {
return diag.FromErr(err)
@@ -188,16 +177,6 @@ func resourcePolicyUpdate(ctx context.Context, d *schema.ResourceData, m interfa
updateMasks = append(updateMasks, "enforce")
}
- if d.HasChange("masking_policy") {
- updateMasks = append(updateMasks, "payload")
- maskingPolicy, err := convertToMaskingPolicy(d)
- if err != nil {
- return diag.FromErr(err)
- }
- patch.Policy = &v1pb.Policy_MaskingPolicy{
- MaskingPolicy: maskingPolicy,
- }
- }
if d.HasChange("masking_exception_policy") {
updateMasks = append(updateMasks, "payload")
maskingExceptionPolicy, err := convertToMaskingExceptionPolicy(d)
@@ -229,10 +208,6 @@ func resourcePolicyUpdate(ctx context.Context, d *schema.ResourceData, m interfa
return diags
}
-func convertToMaskingLevel(level string) v1pb.MaskingLevel {
- return v1pb.MaskingLevel(v1pb.MaskingLevel_value[level])
-}
-
func convertToMaskingExceptionPolicy(d *schema.ResourceData) (*v1pb.MaskingExceptionPolicy, error) {
rawList, ok := d.Get("masking_exception_policy").([]interface{})
if !ok || len(rawList) != 1 {
@@ -282,7 +257,6 @@ func convertToMaskingExceptionPolicy(d *schema.ResourceData) (*v1pb.MaskingExcep
Action: v1pb.MaskingExceptionPolicy_MaskingException_Action(
v1pb.MaskingExceptionPolicy_MaskingException_Action_value[rawException["action"].(string)],
),
- MaskingLevel: convertToMaskingLevel(rawException["masking_level"].(string)),
Condition: &expr.Expr{
Expression: strings.Join(expressions, " && "),
},
@@ -290,29 +264,3 @@ func convertToMaskingExceptionPolicy(d *schema.ResourceData) (*v1pb.MaskingExcep
}
return policy, nil
}
-
-func convertToMaskingPolicy(d *schema.ResourceData) (*v1pb.MaskingPolicy, error) {
- rawList, ok := d.Get("masking_policy").([]interface{})
- if !ok || len(rawList) != 1 {
- return nil, errors.Errorf("invalid masking_policy")
- }
-
- raw := rawList[0].(map[string]interface{})
- rawMaskList := raw["mask_data"].([]interface{})
-
- policy := &v1pb.MaskingPolicy{}
-
- for _, maskData := range rawMaskList {
- rawMask := maskData.(map[string]interface{})
- policy.MaskData = append(policy.MaskData, &v1pb.MaskData{
- Schema: rawMask["schema"].(string),
- Table: rawMask["table"].(string),
- Column: rawMask["column"].(string),
- FullMaskingAlgorithmId: rawMask["full_masking_algorithm_id"].(string),
- PartialMaskingAlgorithmId: rawMask["partial_masking_algorithm_id"].(string),
- MaskingLevel: convertToMaskingLevel(rawMask["masking_level"].(string)),
- })
- }
-
- return policy, nil
-}
diff --git a/provider/resource_policy_test.go b/provider/resource_policy_test.go
index e78d951..cc87109 100644
--- a/provider/resource_policy_test.go
+++ b/provider/resource_policy_test.go
@@ -22,28 +22,11 @@ func TestAccPolicy(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccCheckPolicyDestroy,
Steps: []resource.TestStep{
- {
- Config: testAccCheckPolicyResource(
- "masking_policy",
- "instances/test-sample-instance/databases/employee",
- getMaskingPolicy("salary", "amount", v1pb.MaskingLevel_FULL),
- v1pb.PolicyType_MASKING,
- ),
- Check: resource.ComposeTestCheckFunc(
- internal.TestCheckResourceExists("bytebase_policy.masking_policy"),
- resource.TestCheckResourceAttr("bytebase_policy.masking_policy", "type", v1pb.PolicyType_MASKING.String()),
- resource.TestCheckResourceAttr("bytebase_policy.masking_policy", "masking_policy.#", "1"),
- resource.TestCheckResourceAttr("bytebase_policy.masking_policy", "masking_policy.0.mask_data.#", "1"),
- resource.TestCheckResourceAttr("bytebase_policy.masking_policy", "masking_policy.0.mask_data.0.table", "salary"),
- resource.TestCheckResourceAttr("bytebase_policy.masking_policy", "masking_policy.0.mask_data.0.column", "amount"),
- resource.TestCheckResourceAttr("bytebase_policy.masking_policy", "masking_policy.0.mask_data.0.masking_level", v1pb.MaskingLevel_FULL.String()),
- ),
- },
{
Config: testAccCheckPolicyResource(
"masking_exception_policy",
"projects/project-sample",
- getMaskingExceptionPolicy("instances/test-sample-instance/databases/employee", "salary", "amount", v1pb.MaskingLevel_PARTIAL),
+ getMaskingExceptionPolicy("instances/test-sample-instance/databases/employee", "salary", "amount"),
v1pb.PolicyType_MASKING_EXCEPTION,
),
Check: resource.ComposeTestCheckFunc(
@@ -53,7 +36,6 @@ func TestAccPolicy(t *testing.T) {
resource.TestCheckResourceAttr("bytebase_policy.masking_exception_policy", "masking_exception_policy.0.exceptions.#", "1"),
resource.TestCheckResourceAttr("bytebase_policy.masking_exception_policy", "masking_exception_policy.0.exceptions.0.table", "salary"),
resource.TestCheckResourceAttr("bytebase_policy.masking_exception_policy", "masking_exception_policy.0.exceptions.0.column", "amount"),
- resource.TestCheckResourceAttr("bytebase_policy.masking_exception_policy", "masking_exception_policy.0.exceptions.0.masking_level", v1pb.MaskingLevel_PARTIAL.String()),
),
},
},
@@ -71,31 +53,18 @@ func testAccCheckPolicyResource(identifier, parent, payload string, pType v1pb.P
`, identifier, parent, pType.String(), payload)
}
-func getMaskingPolicy(table, column string, level v1pb.MaskingLevel) string {
- return fmt.Sprintf(`
- masking_policy {
- mask_data {
- table = "%s"
- column = "%s"
- masking_level = "%s"
- }
- }
- `, table, column, level.String())
-}
-
-func getMaskingExceptionPolicy(database, table, column string, level v1pb.MaskingLevel) string {
+func getMaskingExceptionPolicy(database, table, column string) string {
return fmt.Sprintf(`
masking_exception_policy {
exceptions {
database = "%s"
table = "%s"
column = "%s"
- masking_level = "%s"
member = "user:ed@bytebase.com"
action = "QUERY"
}
}
- `, database, table, column, level.String())
+ `, database, table, column)
}
func testAccCheckPolicyDestroy(s *terraform.State) error {