From 9651ce09cd187e76155ba1c25d8ab4ec386de9bc Mon Sep 17 00:00:00 2001 From: ecmadao Date: Fri, 10 Oct 2025 11:37:20 +0800 Subject: [PATCH 1/6] chore: update for Bytebase 3.11 compatibility --- docs/data-sources/iam_policy.md | 1 - docs/data-sources/policy.md | 10 +------ docs/data-sources/policy_list.md | 10 +------ docs/resources/iam_policy.md | 1 - docs/resources/policy.md | 10 +------ examples/database_group/main.tf | 2 +- examples/setup/database_group.tf | 2 +- examples/setup/environment.tf | 8 +++--- examples/setup/iam.tf | 1 - go.mod | 8 +++--- go.sum | 8 ++++++ provider/data_source_iam_policy.go | 15 ---------- provider/data_source_policy.go | 44 +++++------------------------ provider/data_source_policy_list.go | 1 - provider/internal/mock_client.go | 3 -- provider/resource_iam_policy.go | 3 -- provider/resource_policy.go | 41 ++------------------------- tutorials/1-3-env-policy-data.tf | 10 +++---- 18 files changed, 36 insertions(+), 142 deletions(-) diff --git a/docs/data-sources/iam_policy.md b/docs/data-sources/iam_policy.md index c05a213..46db336 100644 --- a/docs/data-sources/iam_policy.md +++ b/docs/data-sources/iam_policy.md @@ -50,7 +50,6 @@ Optional: - `database` (String) The accessible database full name in instances/{instance resource id}/databases/{database name} format - `expire_timestamp` (String) The expiration timestamp in YYYY-MM-DDThh:mm:ssZ format -- `row_limit` (Number) The export row limit for exporter role - `schema` (String) The accessible schema in the database - `tables` (Set of String) The accessible table list diff --git a/docs/data-sources/policy.md b/docs/data-sources/policy.md index 531daa4..10bf91f 100644 --- a/docs/data-sources/policy.md +++ b/docs/data-sources/policy.md @@ -23,7 +23,6 @@ The policy data source. ### Optional - `data_source_query_policy` (Block List, Max: 1) Restrict querying admin data sources (see [below for nested schema](#nestedblock--data_source_query_policy)) -- `disable_copy_data_policy` (Block List, Max: 1) Restrict data copying in SQL Editor (Admins/DBAs allowed) (see [below for nested schema](#nestedblock--disable_copy_data_policy)) - `global_masking_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--global_masking_policy)) - `masking_exception_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--masking_exception_policy)) - `query_data_policy` (Block List, Max: 1) The policy for query data (see [below for nested schema](#nestedblock--query_data_policy)) @@ -46,14 +45,6 @@ Optional: - `restriction` (String) RESTRICTION_UNSPECIFIED means no restriction; FALLBACK will allows to query admin data sources when there is no read-only data source; DISALLOW will always disallow to query admin data sources. - -### Nested Schema for `disable_copy_data_policy` - -Required: - -- `enable` (Boolean) Restrict data copying - - ### Nested Schema for `global_masking_policy` @@ -108,6 +99,7 @@ Optional: Required: +- `disable_copy_data` (Boolean) Disable copying data in the SQL editor - `disable_export` (Boolean) Disable export data in the SQL editor Optional: diff --git a/docs/data-sources/policy_list.md b/docs/data-sources/policy_list.md index 0ed87ff..3135f21 100644 --- a/docs/data-sources/policy_list.md +++ b/docs/data-sources/policy_list.md @@ -30,7 +30,6 @@ The policy data source list. Read-Only: - `data_source_query_policy` (List of Object) (see [below for nested schema](#nestedobjatt--policies--data_source_query_policy)) -- `disable_copy_data_policy` (List of Object) (see [below for nested schema](#nestedobjatt--policies--disable_copy_data_policy)) - `enforce` (Boolean) - `global_masking_policy` (List of Object) (see [below for nested schema](#nestedobjatt--policies--global_masking_policy)) - `inherit_from_parent` (Boolean) @@ -50,14 +49,6 @@ Read-Only: - `restriction` (String) - -### Nested Schema for `policies.disable_copy_data_policy` - -Read-Only: - -- `enable` (Boolean) - - ### Nested Schema for `policies.global_masking_policy` @@ -106,6 +97,7 @@ Read-Only: Read-Only: +- `disable_copy_data` (Boolean) - `disable_export` (Boolean) - `maximum_result_rows` (Number) - `maximum_result_size` (Number) diff --git a/docs/resources/iam_policy.md b/docs/resources/iam_policy.md index 3457946..2b0610b 100644 --- a/docs/resources/iam_policy.md +++ b/docs/resources/iam_policy.md @@ -50,7 +50,6 @@ Optional: - `database` (String) The accessible database full name in instances/{instance resource id}/databases/{database name} format - `expire_timestamp` (String) The expiration timestamp in YYYY-MM-DDThh:mm:ssZ format -- `row_limit` (Number) The export row limit for exporter role - `schema` (String) The accessible schema in the database - `tables` (Set of String) The accessible table list diff --git a/docs/resources/policy.md b/docs/resources/policy.md index a8bbe45..855f2bb 100644 --- a/docs/resources/policy.md +++ b/docs/resources/policy.md @@ -23,7 +23,6 @@ The policy resource. ### Optional - `data_source_query_policy` (Block List, Max: 1) Restrict querying admin data sources (see [below for nested schema](#nestedblock--data_source_query_policy)) -- `disable_copy_data_policy` (Block List, Max: 1) Restrict data copying in SQL Editor (Admins/DBAs allowed) (see [below for nested schema](#nestedblock--disable_copy_data_policy)) - `enforce` (Boolean) Decide if the policy is enforced. - `global_masking_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--global_masking_policy)) - `inherit_from_parent` (Boolean) Decide if the policy should inherit from the parent. @@ -46,14 +45,6 @@ Optional: - `restriction` (String) RESTRICTION_UNSPECIFIED means no restriction; FALLBACK will allows to query admin data sources when there is no read-only data source; DISALLOW will always disallow to query admin data sources. - -### Nested Schema for `disable_copy_data_policy` - -Required: - -- `enable` (Boolean) Restrict data copying - - ### Nested Schema for `global_masking_policy` @@ -108,6 +99,7 @@ Optional: Required: +- `disable_copy_data` (Boolean) Disable copying data in the SQL editor - `disable_export` (Boolean) Disable export data in the SQL editor Optional: diff --git a/examples/database_group/main.tf b/examples/database_group/main.tf index e4083b6..b0a34e1 100644 --- a/examples/database_group/main.tf +++ b/examples/database_group/main.tf @@ -36,7 +36,7 @@ resource "bytebase_database_group" "databases_in_test" { resource_id = "databases-in-test" project = data.bytebase_project.sample_project.name title = "Databases in test env" - condition = "resource.environment_name == \"test\"" + condition = "resource.environment_id == \"test\"" } data "bytebase_database_group" "databases_in_test" { diff --git a/examples/setup/database_group.tf b/examples/setup/database_group.tf index 479d08d..5b3737d 100644 --- a/examples/setup/database_group.tf +++ b/examples/setup/database_group.tf @@ -4,5 +4,5 @@ resource "bytebase_database_group" "databases_in_test" { resource_id = "databases-in-test" project = bytebase_project.sample_project.name title = "Databases in test env" - condition = "resource.environment_name == \"test\"" + condition = "resource.environment_id == \"test\"" } diff --git a/examples/setup/environment.tf b/examples/setup/environment.tf index ddbec9b..3c85785 100644 --- a/examples/setup/environment.tf +++ b/examples/setup/environment.tf @@ -51,13 +51,13 @@ resource "bytebase_policy" "rollout_policy" { } } -resource "bytebase_policy" "disable_copy_data_policy" { +resource "bytebase_policy" "env_query_data_policy" { depends_on = [bytebase_setting.environments] parent = bytebase_setting.environments.environment_setting[0].environment[0].name - type = "DISABLE_COPY_DATA" + type = "DATA_QUERY" - disable_copy_data_policy { - enable = false + query_data_policy { + disable_copy_data = true } } diff --git a/examples/setup/iam.tf b/examples/setup/iam.tf index bd11ef9..6f3d382 100644 --- a/examples/setup/iam.tf +++ b/examples/setup/iam.tf @@ -80,7 +80,6 @@ resource "bytebase_iam_policy" "project_iam" { condition { database = "instances/test-sample-instance/databases/employee" tables = ["dept_emp", "dept_manager"] - row_limit = 10000 expire_timestamp = "2027-03-09T16:17:49Z" } } diff --git a/go.mod b/go.mod index 059001a..bbfeaa5 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.24.4 toolchain go1.24.5 require ( - buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.18.1-20250908030532-58bfc338601e.1 - buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.8-20250908030532-58bfc338601e.1 - connectrpc.com/connect v1.18.1 + buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010010834-1aa0c6de76e4.1 + buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010010834-1aa0c6de76e4.1 + connectrpc.com/connect v1.19.1 github.com/hashicorp/go-cty v1.5.0 github.com/hashicorp/terraform-plugin-docs v0.13.0 github.com/hashicorp/terraform-plugin-log v0.9.0 @@ -15,7 +15,7 @@ require ( github.com/pkg/errors v0.9.1 google.golang.org/genproto v0.0.0-20250528174236-200df99c418a google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 - google.golang.org/protobuf v1.36.8 + google.golang.org/protobuf v1.36.10 ) require ( diff --git a/go.sum b/go.sum index cdb6f53..4b71dae 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,15 @@ buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.18.1-20250908030532-58bfc338601e.1 h1:FQEei3tpJ+OjhAAfLOTzqm+XaFboZI+UpWbXEiQJ1m4= buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.18.1-20250908030532-58bfc338601e.1/go.mod h1:/jFGXncqA/NGlTsADOmP7o1PaKo05HUP64uWe4g7UZg= +buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010010834-1aa0c6de76e4.1 h1:WNQflXc5jqg/LZikcsJxwVFfJMB0i1O7olWXgBZ/DVM= +buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010010834-1aa0c6de76e4.1/go.mod h1:EhBo6m8asi2++hICIxqtAJdmw8SGYXdxZxPpytC1xUw= buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.8-20250908030532-58bfc338601e.1 h1:9VCImLW8Zda6Tay+4zoS85Y8RwX94/x5XlZeeyQHZ38= buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.8-20250908030532-58bfc338601e.1/go.mod h1:dwdKUX0jGgJ7OJe024SNHvANb1TKuBzIrZOzL/3Njtk= +buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010010834-1aa0c6de76e4.1 h1:LKV7Q1GToSWI0IFvWxfw5Ul8AyiR8ItAzq1cKbEGP8U= +buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010010834-1aa0c6de76e4.1/go.mod h1:hCjM3DsxzHQGb3QEnCNYrln9ZgB16AZLOVfRJZRKC0E= connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= +connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= @@ -296,6 +302,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/provider/data_source_iam_policy.go b/provider/data_source_iam_policy.go index d9d2a95..52bc8df 100644 --- a/provider/data_source_iam_policy.go +++ b/provider/data_source_iam_policy.go @@ -3,12 +3,10 @@ package provider import ( "context" "fmt" - "strconv" "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/pkg/errors" v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" @@ -113,12 +111,6 @@ func getIAMBindingSchema(computed bool) *schema.Schema { Set: schema.HashString, Description: "The accessible table list", }, - "row_limit": { - Type: schema.TypeInt, - Computed: computed, - Optional: true, - Description: "The export row limit for exporter role", - }, "expire_timestamp": { Type: schema.TypeString, Computed: computed, @@ -204,13 +196,6 @@ func flattenIAMPolicy(p *v1pb.IamPolicy) ([]interface{}, error) { } rawCondition["tables"] = schema.NewSet(schema.HashString, rawTableList) } - if strings.HasPrefix(expression, `request.row_limit <= `) { - i, err := strconv.Atoi(strings.TrimPrefix(expression, `request.row_limit <= `)) - if err != nil { - return nil, errors.Errorf("cannot convert %s to int with error: %s", expression, err.Error()) - } - rawCondition["row_limit"] = i - } if strings.HasPrefix(expression, "request.time < ") { rawCondition["expire_timestamp"] = strings.TrimSuffix( strings.TrimPrefix(expression, `request.time < timestamp("`), diff --git a/provider/data_source_policy.go b/provider/data_source_policy.go index 2553ff5..f088398 100644 --- a/provider/data_source_policy.go +++ b/provider/data_source_policy.go @@ -44,7 +44,6 @@ func dataSourcePolicy() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ v1pb.PolicyType_MASKING_EXCEPTION.String(), v1pb.PolicyType_MASKING_RULE.String(), - v1pb.PolicyType_DISABLE_COPY_DATA.String(), v1pb.PolicyType_DATA_SOURCE_QUERY.String(), v1pb.PolicyType_ROLLOUT_POLICY.String(), v1pb.PolicyType_DATA_QUERY.String(), @@ -68,7 +67,6 @@ func dataSourcePolicy() *schema.Resource { }, "masking_exception_policy": getMaskingExceptionPolicySchema(true), "global_masking_policy": getGlobalMaskingPolicySchema(true), - "disable_copy_data_policy": getDisableCopyDataPolicySchema(true), "data_source_query_policy": getDataSourceQueryPolicySchema(true), "rollout_policy": getRolloutPolicySchema(true), "query_data_policy": getDataQueryPolicySchema(true), @@ -250,9 +248,14 @@ func getDataQueryPolicySchema(computed bool) *schema.Schema { }, "disable_export": { Type: schema.TypeBool, - Required: true, + Optional: true, Description: "Disable export data in the SQL editor", }, + "disable_copy_data": { + Type: schema.TypeBool, + Optional: true, + Description: "Disable copying data in the SQL editor", + }, "timeout_in_seconds": { Type: schema.TypeInt, Optional: true, @@ -263,27 +266,6 @@ func getDataQueryPolicySchema(computed bool) *schema.Schema { } } -func getDisableCopyDataPolicySchema(computed bool) *schema.Schema { - return &schema.Schema{ - Computed: computed, - Optional: true, - Default: nil, - Type: schema.TypeList, - MinItems: 0, - MaxItems: 1, - Description: "Restrict data copying in SQL Editor (Admins/DBAs allowed)", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enable": { - Type: schema.TypeBool, - Required: true, - Description: "Restrict data copying", - }, - }, - }, - } -} - func getDataSourceQueryPolicySchema(computed bool) *schema.Schema { return &schema.Schema{ Computed: computed, @@ -415,11 +397,6 @@ func flattenPolicyPayload(policy *v1pb.Policy) (string, interface{}, diag.Diagno } return "global_masking_policy", maskingPolicy, nil } - case v1pb.PolicyType_DISABLE_COPY_DATA: - if p := policy.GetDisableCopyDataPolicy(); p != nil { - disableCopyDataPolicy := flattenDisableCopyDataPolicy(p) - return "disable_copy_data_policy", disableCopyDataPolicy, nil - } case v1pb.PolicyType_DATA_SOURCE_QUERY: if p := policy.GetDataSourceQueryPolicy(); p != nil { dataSourceQueryPolicy := flattenDataSourceQueryPolicy(p) @@ -443,7 +420,6 @@ func flattenPolicyPayload(policy *v1pb.Policy) (string, interface{}, diag.Diagno func flattenRolloutPolicy(p *v1pb.RolloutPolicy) []interface{} { roles := []string{} roles = append(roles, p.Roles...) - roles = append(roles, p.IssueRoles...) policy := map[string]interface{}{ "automatic": p.Automatic, "roles": roles, @@ -460,18 +436,12 @@ func flattenDataSourceQueryPolicy(p *v1pb.DataSourceQueryPolicy) []interface{} { return []interface{}{policy} } -func flattenDisableCopyDataPolicy(p *v1pb.DisableCopyDataPolicy) []interface{} { - policy := map[string]interface{}{ - "enable": p.Active, - } - return []interface{}{policy} -} - func flattenQueryDataPolicy(p *v1pb.QueryDataPolicy) []interface{} { policy := map[string]interface{}{ "maximum_result_size": int(p.MaximumResultSize), "maximum_result_rows": int(p.MaximumResultRows), "disable_export": p.DisableExport, + "disable_copy_data": p.DisableCopyData, "timeout_in_seconds": int(p.Timeout.Seconds), } return []interface{}{policy} diff --git a/provider/data_source_policy_list.go b/provider/data_source_policy_list.go index 05820ac..36f564c 100644 --- a/provider/data_source_policy_list.go +++ b/provider/data_source_policy_list.go @@ -65,7 +65,6 @@ func dataSourcePolicyList() *schema.Resource { }, "masking_exception_policy": getMaskingExceptionPolicySchema(true), "global_masking_policy": getGlobalMaskingPolicySchema(true), - "disable_copy_data_policy": getDisableCopyDataPolicySchema(true), "data_source_query_policy": getDataSourceQueryPolicySchema(true), "rollout_policy": getRolloutPolicySchema(true), "query_data_policy": getDataQueryPolicySchema(true), diff --git a/provider/internal/mock_client.go b/provider/internal/mock_client.go index cb59d93..8580658 100644 --- a/provider/internal/mock_client.go +++ b/provider/internal/mock_client.go @@ -1070,9 +1070,6 @@ func (c *mockClient) UpdateDatabaseGroup(_ context.Context, group *v1pb.Database if slices.Contains(updateMasks, "matched_databases") { existed.MatchedDatabases = group.MatchedDatabases } - if slices.Contains(updateMasks, "unmatched_databases") { - existed.UnmatchedDatabases = group.UnmatchedDatabases - } c.databaseGroupMap[group.Name] = existed return existed, nil diff --git a/provider/resource_iam_policy.go b/provider/resource_iam_policy.go index 8de33df..18e4111 100644 --- a/provider/resource_iam_policy.go +++ b/provider/resource_iam_policy.go @@ -143,9 +143,6 @@ func convertToV1Condition(rawSchema interface{}) (*expr.Expr, error) { } expressions = append(expressions, fmt.Sprintf(`resource.table_name in [%s]`, strings.Join(tableList, ","))) } - if rowLimit, ok := rawCondition["row_limit"].(int); ok && rowLimit > 0 { - expressions = append(expressions, fmt.Sprintf(`request.row_limit <= %d`, rowLimit)) - } if expire, ok := rawCondition["expire_timestamp"].(string); ok && expire != "" { formattedTime, err := time.Parse(time.RFC3339, expire) if err != nil { diff --git a/provider/resource_policy.go b/provider/resource_policy.go index c8eaf6a..4845da1 100644 --- a/provider/resource_policy.go +++ b/provider/resource_policy.go @@ -54,7 +54,6 @@ func resourcePolicy() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ v1pb.PolicyType_MASKING_EXCEPTION.String(), v1pb.PolicyType_MASKING_RULE.String(), - v1pb.PolicyType_DISABLE_COPY_DATA.String(), v1pb.PolicyType_DATA_SOURCE_QUERY.String(), v1pb.PolicyType_ROLLOUT_POLICY.String(), v1pb.PolicyType_DATA_QUERY.String(), @@ -80,7 +79,6 @@ func resourcePolicy() *schema.Resource { }, "masking_exception_policy": getMaskingExceptionPolicySchema(false), "global_masking_policy": getGlobalMaskingPolicySchema(false), - "disable_copy_data_policy": getDisableCopyDataPolicySchema(false), "data_source_query_policy": getDataSourceQueryPolicySchema(false), "rollout_policy": getRolloutPolicySchema(false), "query_data_policy": getDataQueryPolicySchema(false), @@ -153,18 +151,6 @@ func resourcePolicyCreate(ctx context.Context, d *schema.ResourceData, m interfa MaskingRulePolicy: maskingRulePolicy, } updateMasks = append(updateMasks, "masking_rule_policy") - case v1pb.PolicyType_DISABLE_COPY_DATA: - if !strings.HasPrefix(policyName, internal.EnvironmentNamePrefix) && !strings.HasPrefix(policyName, internal.ProjectNamePrefix) { - return diag.Errorf("policy %v only support environment or project resource", policyName) - } - disableCopyDataPolicy, err := convertToDisableCopyDataPolicy(d) - if err != nil { - return diag.FromErr(err) - } - patch.Policy = &v1pb.Policy_DisableCopyDataPolicy{ - DisableCopyDataPolicy: disableCopyDataPolicy, - } - updateMasks = append(updateMasks, "disable_copy_data_policy") case v1pb.PolicyType_DATA_SOURCE_QUERY: if !strings.HasPrefix(policyName, internal.EnvironmentNamePrefix) && !strings.HasPrefix(policyName, internal.ProjectNamePrefix) { return diag.Errorf("policy %v only support environment or project resource", policyName) @@ -190,8 +176,8 @@ func resourcePolicyCreate(ctx context.Context, d *schema.ResourceData, m interfa } updateMasks = append(updateMasks, "rollout_policy") case v1pb.PolicyType_DATA_QUERY: - if parent != internal.WorkspaceName { - return diag.Errorf("policy %v only support %v parent", policyName, internal.WorkspaceName) + if parent != internal.WorkspaceName && !strings.HasPrefix(policyName, internal.EnvironmentNamePrefix) { + return diag.Errorf("policy %v only support %v or environment resource", policyName, internal.WorkspaceName) } queryDataPolicy, err := convertToQueryDataPolicy(d) if err != nil { @@ -282,16 +268,6 @@ func resourcePolicyUpdate(ctx context.Context, d *schema.ResourceData, m interfa MaskingRulePolicy: maskingRulePolicy, } } - if d.HasChange("disable_copy_data_policy") { - updateMasks = append(updateMasks, "disable_copy_data_policy") - disableCopyDataPolicy, err := convertToDisableCopyDataPolicy(d) - if err != nil { - return diag.FromErr(err) - } - patch.Policy = &v1pb.Policy_DisableCopyDataPolicy{ - DisableCopyDataPolicy: disableCopyDataPolicy, - } - } if d.HasChange("data_source_query_policy") { updateMasks = append(updateMasks, "data_source_query_policy") dataSourceQueryPolicy, err := convertToDataSourceQueryPolicy(d) @@ -511,6 +487,7 @@ func convertToQueryDataPolicy(d *schema.ResourceData) (*v1pb.QueryDataPolicy, er raw := rawList[0].(map[string]interface{}) return &v1pb.QueryDataPolicy{ DisableExport: raw["disable_export"].(bool), + DisableCopyData: raw["disable_copy_data"].(bool), MaximumResultSize: int64(raw["maximum_result_size"].(int)), MaximumResultRows: int32(raw["maximum_result_rows"].(int)), Timeout: &durationpb.Duration{ @@ -519,18 +496,6 @@ func convertToQueryDataPolicy(d *schema.ResourceData) (*v1pb.QueryDataPolicy, er }, nil } -func convertToDisableCopyDataPolicy(d *schema.ResourceData) (*v1pb.DisableCopyDataPolicy, error) { - rawList, ok := d.Get("disable_copy_data_policy").([]interface{}) - if !ok || len(rawList) != 1 { - return nil, errors.Errorf("invalid disable_copy_data_policy") - } - - raw := rawList[0].(map[string]interface{}) - return &v1pb.DisableCopyDataPolicy{ - Active: raw["enable"].(bool), - }, nil -} - func convertToDataSourceQueryPolicy(d *schema.ResourceData) (*v1pb.DataSourceQueryPolicy, error) { rawList, ok := d.Get("data_source_query_policy").([]interface{}) if !ok || len(rawList) != 1 { diff --git a/tutorials/1-3-env-policy-data.tf b/tutorials/1-3-env-policy-data.tf index e965794..6faf3c3 100644 --- a/tutorials/1-3-env-policy-data.tf +++ b/tutorials/1-3-env-policy-data.tf @@ -1,10 +1,10 @@ -resource "bytebase_policy" "disable_copy_data_policy_prod" { +resource "bytebase_policy" "disable_copy_data_prod" { depends_on = [bytebase_setting.environments] parent = bytebase_setting.environments.environment_setting[0].environment[1].name - type = "DISABLE_COPY_DATA" + type = "DATA_QUERY" - disable_copy_data_policy { - enable = true + query_data_policy { + disable_copy_data = true } } @@ -18,4 +18,4 @@ resource "bytebase_policy" "data_source_query_policy_prod" { disallow_ddl = true disallow_dml = true } -} \ No newline at end of file +} From 10aaa5a1df8ee30a1baf8e1ca104d53f355cca6b Mon Sep 17 00:00:00 2001 From: ecmadao Date: Fri, 10 Oct 2025 11:43:20 +0800 Subject: [PATCH 2/6] chore: update --- docs/data-sources/setting.md | 10 +------ docs/resources/setting.md | 10 +------ examples/setup/approval_flow.tf | 16 ++++-------- provider/data_source_setting.go | 43 ++++++++++++------------------- provider/resource_setting.go | 25 +++++------------- provider/resource_setting_test.go | 26 +++++++------------ 6 files changed, 39 insertions(+), 91 deletions(-) diff --git a/docs/data-sources/setting.md b/docs/data-sources/setting.md index 5b22d82..6924a85 100644 --- a/docs/data-sources/setting.md +++ b/docs/data-sources/setting.md @@ -214,17 +214,9 @@ Read-Only: Read-Only: - `description` (String) -- `steps` (List of Object) (see [below for nested schema](#nestedobjatt--approval_flow--rules--flow--steps)) +- `roles` (List of String) - `title` (String) - -### Nested Schema for `approval_flow.rules.flow.title` - -Read-Only: - -- `role` (String) - - diff --git a/docs/resources/setting.md b/docs/resources/setting.md index dab9d97..05ae32c 100644 --- a/docs/resources/setting.md +++ b/docs/resources/setting.md @@ -55,21 +55,13 @@ Optional: Required: -- `steps` (Block List, Min: 1) Approval flow following the step order. (see [below for nested schema](#nestedblock--approval_flow--rules--flow--steps)) +- `roles` (List of String) The role require to review in this step - `title` (String) Optional: - `description` (String) - -### Nested Schema for `approval_flow.rules.flow.steps` - -Required: - -- `role` (String) The role require to review in this step - - ### Nested Schema for `approval_flow.rules.conditions` diff --git a/examples/setup/approval_flow.tf b/examples/setup/approval_flow.tf index dd59a78..2eba0c8 100644 --- a/examples/setup/approval_flow.tf +++ b/examples/setup/approval_flow.tf @@ -7,17 +7,11 @@ resource "bytebase_setting" "approval_flow" { description = "Need DBA and workspace admin approval" # Approval flow following the step order. - steps { - role = "roles/projectOwner" - } - - steps { - role = "roles/workspaceDBA" - } - - steps { - role = "roles/workspaceAdmin" - } + roles = [ + "roles/projectOwner", + "roles/workspaceDBA", + "roles/workspaceAdmin" + ] } # Match any condition will trigger this approval flow. diff --git a/provider/data_source_setting.go b/provider/data_source_setting.go index 618fc12..9962d95 100644 --- a/provider/data_source_setting.go +++ b/provider/data_source_setting.go @@ -503,31 +503,25 @@ func getWorkspaceApprovalSetting(computed bool) *schema.Schema { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "title": { - Type: schema.TypeString, - Computed: computed, - Required: !computed, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, }, "description": { Type: schema.TypeString, Computed: computed, Optional: true, }, - "steps": { + "roles": { Type: schema.TypeList, - Computed: computed, - Required: !computed, - Description: "Approval flow following the step order.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "role": { - Type: schema.TypeString, - Required: true, - ValidateDiagFunc: internal.ResourceNameValidation( - fmt.Sprintf("^%s", internal.RoleNamePrefix), - ), - Description: "The role require to review in this step", - }, - }, + Required: true, + Description: "The role require to review in this step", + Elem: &schema.Schema{ + Type: schema.TypeString, + Description: `Role full name in roles/{id} format.`, + ValidateDiagFunc: internal.ResourceNameValidation( + fmt.Sprintf("^%s", internal.RoleNamePrefix), + ), }, }, }, @@ -717,14 +711,9 @@ func parseApprovalExpression(callExpr *v1alpha1.Expr_Call) ([]map[string]interfa func flattenWorkspaceApprovalSetting(ctx context.Context, client api.Client, setting *v1pb.WorkspaceApprovalSetting) ([]interface{}, error) { ruleList := []interface{}{} for _, rule := range setting.Rules { - stepList := []interface{}{} - for _, step := range rule.Template.Flow.Steps { - rawStep := map[string]interface{}{} - for _, node := range step.Nodes { - rawStep["role"] = node.Role - break - } - stepList = append(stepList, rawStep) + roleList := []interface{}{} + for _, role := range rule.Template.Flow.Roles { + roleList = append(roleList, role) } conditionList := []map[string]interface{}{} @@ -746,7 +735,7 @@ func flattenWorkspaceApprovalSetting(ctx context.Context, client api.Client, set map[string]interface{}{ "title": rule.Template.Title, "description": rule.Template.Description, - "steps": stepList, + "roles": roleList, }, }, } diff --git a/provider/resource_setting.go b/provider/resource_setting.go index b21b932..82456fe 100644 --- a/provider/resource_setting.go +++ b/provider/resource_setting.go @@ -380,27 +380,16 @@ func convertToV1ApprovalSetting(d *schema.ResourceData) (*v1pb.WorkspaceApproval }, } - stepList, ok := rawFlow["steps"].([]interface{}) + roleList, ok := rawFlow["roles"].([]interface{}) if !ok { - return nil, errors.Errorf("invalid steps") + return nil, errors.Errorf("invalid roles") } - - for _, step := range stepList { - approvalStep := &v1pb.ApprovalStep{ - Type: v1pb.ApprovalStep_ANY, - } - - rawStep := step.(map[string]interface{}) - role := rawStep["role"].(string) - if !strings.HasPrefix(role, "roles/") { - return nil, errors.Errorf("invalid role name: %v, role name should in roles/{role} format", role) + for _, raw := range roleList { + role := raw.(string) + if !strings.HasPrefix(role, internal.RoleNamePrefix) { + return nil, errors.Errorf("invalid role format, role must in roles/{id} format") } - approvalStep.Nodes = append(approvalStep.Nodes, &v1pb.ApprovalNode{ - Type: v1pb.ApprovalNode_ANY_IN_GROUP, - Role: role, - }) - - approvalRule.Template.Flow.Steps = append(approvalRule.Template.Flow.Steps, approvalStep) + approvalRule.Template.Flow.Roles = append(approvalRule.Template.Flow.Roles, role) } workspaceApprovalSetting.Rules = append(workspaceApprovalSetting.Rules, approvalRule) diff --git a/provider/resource_setting_test.go b/provider/resource_setting_test.go index ec80506..042e254 100644 --- a/provider/resource_setting_test.go +++ b/provider/resource_setting_test.go @@ -31,7 +31,7 @@ func TestAccSetting_WorkspaceApproval(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "approval_flow.0.rules.#", "1"), resource.TestCheckResourceAttr(resourceName, "approval_flow.0.rules.0.conditions.#", "1"), resource.TestCheckResourceAttr(resourceName, "approval_flow.0.rules.0.flow.#", "1"), - resource.TestCheckResourceAttr(resourceName, "approval_flow.0.rules.0.flow.0.steps.#", "2"), + resource.TestCheckResourceAttr(resourceName, "approval_flow.0.rules.0.flow.0.roles.#", "2"), ), }, // Update workspace approval setting @@ -44,7 +44,7 @@ func TestAccSetting_WorkspaceApproval(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "approval_flow.0.rules.#", "1"), resource.TestCheckResourceAttr(resourceName, "approval_flow.0.rules.0.conditions.#", "2"), resource.TestCheckResourceAttr(resourceName, "approval_flow.0.rules.0.flow.#", "1"), - resource.TestCheckResourceAttr(resourceName, "approval_flow.0.rules.0.flow.0.steps.#", "1"), + resource.TestCheckResourceAttr(resourceName, "approval_flow.0.rules.0.flow.0.roles.#", "1"), ), }, }, @@ -228,9 +228,7 @@ resource "bytebase_setting" "%s" { flow { title = "Test" description = "Test" - steps { - role = "roles/test" - } + roles = ["roles/test"] } } } @@ -265,9 +263,7 @@ resource "bytebase_setting" "%s" { flow { title = "Test" description = "Test" - steps { - role = "invalid-role" - } + roles = ["invalid-role"] } } } @@ -339,12 +335,10 @@ resource "bytebase_setting" "%s" { flow { title = "DDL Approval Flow" description = "Approval flow for DDL operations" - steps { - role = bytebase_role.approval_role_%s.name - } - steps { - role = bytebase_role.approval_role_%s.name - } + roles = [ + bytebase_role.approval_role_%s.name, + bytebase_role.approval_role_%s.name + ] } } } @@ -377,9 +371,7 @@ resource "bytebase_setting" "%s" { flow { title = "Updated Approval Flow" description = "Updated approval flow" - steps { - role = bytebase_role.approval_role_%s.name - } + roles = [bytebase_role.approval_role_%s.name] } } } From 339ec4ffa08d46ada429eb5762ddd9043dabd5ce Mon Sep 17 00:00:00 2001 From: ecmadao Date: Fri, 10 Oct 2025 11:44:40 +0800 Subject: [PATCH 3/6] chore: update --- go.sum | 8 -------- tutorials/4-3-approval-flow.tf | 11 ++++++----- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/go.sum b/go.sum index 4b71dae..0193da4 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,7 @@ -buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.18.1-20250908030532-58bfc338601e.1 h1:FQEei3tpJ+OjhAAfLOTzqm+XaFboZI+UpWbXEiQJ1m4= -buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.18.1-20250908030532-58bfc338601e.1/go.mod h1:/jFGXncqA/NGlTsADOmP7o1PaKo05HUP64uWe4g7UZg= buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010010834-1aa0c6de76e4.1 h1:WNQflXc5jqg/LZikcsJxwVFfJMB0i1O7olWXgBZ/DVM= buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010010834-1aa0c6de76e4.1/go.mod h1:EhBo6m8asi2++hICIxqtAJdmw8SGYXdxZxPpytC1xUw= -buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.8-20250908030532-58bfc338601e.1 h1:9VCImLW8Zda6Tay+4zoS85Y8RwX94/x5XlZeeyQHZ38= -buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.8-20250908030532-58bfc338601e.1/go.mod h1:dwdKUX0jGgJ7OJe024SNHvANb1TKuBzIrZOzL/3Njtk= buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010010834-1aa0c6de76e4.1 h1:LKV7Q1GToSWI0IFvWxfw5Ul8AyiR8ItAzq1cKbEGP8U= buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010010834-1aa0c6de76e4.1/go.mod h1:hCjM3DsxzHQGb3QEnCNYrln9ZgB16AZLOVfRJZRKC0E= -connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= -connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= @@ -300,8 +294,6 @@ google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/tutorials/4-3-approval-flow.tf b/tutorials/4-3-approval-flow.tf index 8af617f..d6bed80 100644 --- a/tutorials/4-3-approval-flow.tf +++ b/tutorials/4-3-approval-flow.tf @@ -6,10 +6,11 @@ resource "bytebase_setting" "approval_flow" { flow { title = "Project Owner → DBA → Admin" description = "Need DBA and workspace admin approval" - - steps { role = "roles/projectOwner" } - steps { role = "roles/workspaceDBA" } - steps { role = "roles/workspaceAdmin" } + roles = [ + "roles/projectOwner", + "roles/workspaceDBA", + "roles/workspaceAdmin" + ] } conditions { source = "DML" @@ -21,4 +22,4 @@ resource "bytebase_setting" "approval_flow" { } } } -} \ No newline at end of file +} From 4e839fc30e7d5cbea2b9efc98d226291bd6a3a31 Mon Sep 17 00:00:00 2001 From: ecmadao Date: Fri, 10 Oct 2025 14:01:57 +0800 Subject: [PATCH 4/6] fix: test --- provider/internal/mock_client.go | 158 +++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 17 deletions(-) diff --git a/provider/internal/mock_client.go b/provider/internal/mock_client.go index 8580658..7d74795 100644 --- a/provider/internal/mock_client.go +++ b/provider/internal/mock_client.go @@ -6,6 +6,7 @@ import ( "slices" "strconv" "strings" + "sync" "github.com/pkg/errors" @@ -15,20 +16,23 @@ import ( v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) -var instanceMap map[string]*v1pb.Instance -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 userMap map[string]*v1pb.User -var roleMap map[string]*v1pb.Role -var groupMap map[string]*v1pb.Group -var reviewConfigMap map[string]*v1pb.ReviewConfig -var riskMap map[string]*v1pb.Risk -var databaseGroupMap map[string]*v1pb.DatabaseGroup -var workspaceIAMPolicy *v1pb.IamPolicy +var ( + mu sync.RWMutex + instanceMap map[string]*v1pb.Instance + policyMap map[string]*v1pb.Policy + 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 + userMap map[string]*v1pb.User + roleMap map[string]*v1pb.Role + groupMap map[string]*v1pb.Group + reviewConfigMap map[string]*v1pb.ReviewConfig + riskMap map[string]*v1pb.Risk + databaseGroupMap map[string]*v1pb.DatabaseGroup + workspaceIAMPolicy *v1pb.IamPolicy +) func init() { instanceMap = map[string]*v1pb.Instance{} @@ -77,6 +81,8 @@ type mockClient struct { // newMockClient returns the new Bytebase API mock client. func newMockClient(_, _, _ string) (api.Client, error) { + mu.RLock() + defer mu.RUnlock() return &mockClient{ instanceMap: instanceMap, policyMap: policyMap, @@ -96,6 +102,8 @@ func newMockClient(_, _, _ string) (api.Client, error) { // ListInstance will return instances in environment. func (c *mockClient) ListInstance(_ context.Context, filter *api.InstanceFilter) ([]*v1pb.Instance, error) { + mu.RLock() + defer mu.RUnlock() instances := make([]*v1pb.Instance, 0) for _, ins := range c.instanceMap { if ins.State == v1pb.State_DELETED && filter.State != v1pb.State_DELETED { @@ -109,6 +117,8 @@ func (c *mockClient) ListInstance(_ context.Context, filter *api.InstanceFilter) // GetInstance gets the instance by id. func (c *mockClient) GetInstance(_ context.Context, instanceName string) (*v1pb.Instance, error) { + mu.RLock() + defer mu.RUnlock() ins, ok := c.instanceMap[instanceName] if !ok { return nil, errors.Errorf("Cannot found instance %s", instanceName) @@ -166,6 +176,8 @@ func (c *mockClient) CreateInstance(_ context.Context, instanceID string, instan }, } + mu.Lock() + defer mu.Unlock() c.instanceMap[ins.Name] = ins c.databaseMap[defaultDb.Name] = defaultDb c.databaseMap[testDb.Name] = testDb @@ -207,7 +219,9 @@ func (c *mockClient) UpdateInstance(ctx context.Context, patch *v1pb.Instance, u ins.MaximumConnections = patch.MaximumConnections } + mu.Lock() c.instanceMap[ins.Name] = ins + mu.Unlock() return ins, nil } @@ -219,7 +233,9 @@ func (c *mockClient) DeleteInstance(ctx context.Context, instanceName string) er } ins.State = v1pb.State_DELETED + mu.Lock() c.instanceMap[ins.Name] = ins + mu.Unlock() return nil } @@ -232,7 +248,9 @@ func (c *mockClient) UndeleteInstance(ctx context.Context, instanceName string) } ins.State = v1pb.State_ACTIVE + mu.Lock() c.instanceMap[ins.Name] = ins + mu.Unlock() return ins, nil } @@ -244,6 +262,8 @@ func (*mockClient) SyncInstanceSchema(_ context.Context, _ string) error { // ListPolicies lists policies in a specific resource. func (c *mockClient) ListPolicies(_ context.Context, parent string) (*v1pb.ListPoliciesResponse, error) { + mu.RLock() + defer mu.RUnlock() policies := make([]*v1pb.Policy, 0) for _, policy := range c.policyMap { if parent == "" || strings.HasPrefix(policy.Name, parent) { @@ -258,6 +278,8 @@ func (c *mockClient) ListPolicies(_ context.Context, parent string) (*v1pb.ListP // GetPolicy gets a policy in a specific resource. func (c *mockClient) GetPolicy(_ context.Context, policyName string) (*v1pb.Policy, error) { + mu.RLock() + defer mu.RUnlock() policy, ok := c.policyMap[policyName] if !ok { return nil, errors.Errorf("Cannot found policy %s", policyName) @@ -273,6 +295,8 @@ func (c *mockClient) UpsertPolicy(_ context.Context, patch *v1pb.Policy, updateM return nil, err } + mu.Lock() + defer mu.Unlock() policy, existed := c.policyMap[patch.Name] if !existed { @@ -335,12 +359,16 @@ func (c *mockClient) UpsertPolicy(_ context.Context, patch *v1pb.Policy, updateM // DeletePolicy deletes the policy. func (c *mockClient) DeletePolicy(_ context.Context, policyName string) error { + mu.Lock() + defer mu.Unlock() delete(c.policyMap, policyName) return nil } // GetDatabase gets the database by instance resource id and the database name. func (c *mockClient) GetDatabase(_ context.Context, databaseName string) (*v1pb.Database, error) { + mu.RLock() + defer mu.RUnlock() db, ok := c.databaseMap[databaseName] if !ok { return nil, errors.Errorf("Cannot found database %s", databaseName) @@ -351,6 +379,8 @@ func (c *mockClient) GetDatabase(_ context.Context, databaseName string) (*v1pb. // ListDatabase list the databases. func (c *mockClient) ListDatabase(_ context.Context, instaceID string, filter *api.DatabaseFilter, _ bool) ([]*v1pb.Database, error) { + mu.RLock() + defer mu.RUnlock() projectID := "-" if filter.Project != "" { projectID = filter.Project @@ -381,16 +411,20 @@ func (c *mockClient) UpdateDatabase(ctx context.Context, patch *v1pb.Database, u if slices.Contains(updateMasks, "labels") { db.Labels = patch.Labels } + mu.Lock() c.databaseMap[db.Name] = db + mu.Unlock() return db, nil } // BatchUpdateDatabases batch updates databases. func (c *mockClient) BatchUpdateDatabases(ctx context.Context, request *v1pb.BatchUpdateDatabasesRequest) (*v1pb.BatchUpdateDatabasesResponse, error) { + mu.Lock() + defer mu.Unlock() for _, req := range request.Requests { - db, err := c.GetDatabase(ctx, req.Database.Name) - if err != nil { - return nil, err + db, ok := c.databaseMap[req.Database.Name] + if !ok { + return nil, errors.Errorf("Cannot found database %s", req.Database.Name) } if slices.Contains(req.UpdateMask.Paths, "project") { db.Project = req.Database.Project @@ -403,6 +437,8 @@ func (c *mockClient) BatchUpdateDatabases(ctx context.Context, request *v1pb.Bat // GetDatabaseCatalog gets the database catalog by the database full name. func (c *mockClient) GetDatabaseCatalog(_ context.Context, databaseName string) (*v1pb.DatabaseCatalog, error) { + mu.RLock() + defer mu.RUnlock() db, ok := c.databaseCatalogMap[databaseName] if !ok { return nil, errors.Errorf("Cannot found database catalog %s", databaseName) @@ -413,12 +449,16 @@ func (c *mockClient) GetDatabaseCatalog(_ context.Context, databaseName string) // UpdateDatabaseCatalog patches the database catalog. func (c *mockClient) UpdateDatabaseCatalog(_ context.Context, patch *v1pb.DatabaseCatalog) (*v1pb.DatabaseCatalog, error) { + mu.Lock() + defer mu.Unlock() 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) { + mu.RLock() + defer mu.RUnlock() proj, ok := c.projectMap[projectName] if !ok { return nil, errors.Errorf("Cannot found project %s", projectName) @@ -429,6 +469,8 @@ func (c *mockClient) GetProject(_ context.Context, projectName string) (*v1pb.Pr // ListProject list the projects. func (c *mockClient) ListProject(_ context.Context, filter *api.ProjectFilter) ([]*v1pb.Project, error) { + mu.RLock() + defer mu.RUnlock() projects := make([]*v1pb.Project, 0) for _, proj := range c.projectMap { if proj.State == v1pb.State_DELETED && filter.State != v1pb.State_DELETED { @@ -442,6 +484,8 @@ func (c *mockClient) ListProject(_ context.Context, filter *api.ProjectFilter) ( // CreateProject creates the project. func (c *mockClient) CreateProject(_ context.Context, projectID string, project *v1pb.Project) (*v1pb.Project, error) { + mu.Lock() + defer mu.Unlock() proj := &v1pb.Project{ Name: fmt.Sprintf("%s%s", ProjectNamePrefix, projectID), State: v1pb.State_ACTIVE, @@ -463,7 +507,9 @@ func (c *mockClient) UpdateProject(ctx context.Context, patch *v1pb.Project, upd proj.Title = patch.Title } + mu.Lock() c.projectMap[proj.Name] = proj + mu.Unlock() return proj, nil } @@ -475,7 +521,9 @@ func (c *mockClient) DeleteProject(ctx context.Context, projectName string) erro } proj.State = v1pb.State_DELETED + mu.Lock() c.projectMap[proj.Name] = proj + mu.Unlock() return nil } @@ -488,13 +536,17 @@ func (c *mockClient) UndeleteProject(ctx context.Context, projectName string) (* } proj.State = v1pb.State_ACTIVE + mu.Lock() c.projectMap[proj.Name] = proj + mu.Unlock() return proj, nil } // GetProjectIAMPolicy gets the project IAM policy by project full name. func (c *mockClient) GetProjectIAMPolicy(_ context.Context, projectName string) (*v1pb.IamPolicy, error) { + mu.RLock() + defer mu.RUnlock() iamPolicy, ok := c.projectIAMMap[projectName] if !ok { return &v1pb.IamPolicy{}, nil @@ -504,12 +556,16 @@ func (c *mockClient) GetProjectIAMPolicy(_ context.Context, projectName string) // SetProjectIAMPolicy sets the project IAM policy. func (c *mockClient) SetProjectIAMPolicy(_ context.Context, projectName string, update *v1pb.SetIamPolicyRequest) (*v1pb.IamPolicy, error) { + mu.Lock() + defer mu.Unlock() c.projectIAMMap[projectName] = update.Policy return c.projectIAMMap[projectName], nil } // ListSettings lists all settings. func (c *mockClient) ListSettings(_ context.Context) (*v1pb.ListSettingsResponse, error) { + mu.RLock() + defer mu.RUnlock() settings := make([]*v1pb.Setting, 0) for _, setting := range c.settingMap { settings = append(settings, setting) @@ -522,6 +578,8 @@ func (c *mockClient) ListSettings(_ context.Context) (*v1pb.ListSettingsResponse // ListSettings lists all settings. func (c *mockClient) GetSetting(_ context.Context, settingName string) (*v1pb.Setting, error) { + mu.RLock() + defer mu.RUnlock() setting, ok := c.settingMap[settingName] if !ok { return nil, errors.Errorf("Cannot found setting %s", settingName) @@ -532,6 +590,8 @@ func (c *mockClient) GetSetting(_ context.Context, settingName string) (*v1pb.Se // UpsertSetting updates or creates the setting. func (c *mockClient) UpsertSetting(_ context.Context, upsert *v1pb.Setting, _ []string) (*v1pb.Setting, error) { + mu.Lock() + defer mu.Unlock() setting, ok := c.settingMap[upsert.Name] if !ok { c.settingMap[upsert.Name] = upsert @@ -685,6 +745,8 @@ func parseCondition(condition string, baseID int64) *v1alpha1.Expr { // ListUser list all users. func (c *mockClient) ListUser(_ context.Context, filter *api.UserFilter) ([]*v1pb.User, error) { + mu.RLock() + defer mu.RUnlock() users := make([]*v1pb.User, 0) for _, user := range c.userMap { if user.State == v1pb.State_DELETED && filter.State != v1pb.State_DELETED { @@ -698,6 +760,8 @@ func (c *mockClient) ListUser(_ context.Context, filter *api.UserFilter) ([]*v1p // GetUser gets the user by name. func (c *mockClient) GetUser(_ context.Context, userName string) (*v1pb.User, error) { + mu.RLock() + defer mu.RUnlock() user, ok := c.userMap[userName] if !ok { return nil, errors.Errorf("Cannot found user %s", userName) @@ -709,6 +773,8 @@ func (c *mockClient) GetUser(_ context.Context, userName string) (*v1pb.User, er // CreateUser creates the user. func (c *mockClient) CreateUser(_ context.Context, user *v1pb.User) (*v1pb.User, error) { // For service accounts, generate a service key + mu.Lock() + defer mu.Unlock() if user.UserType == v1pb.UserType_SERVICE_ACCOUNT && user.ServiceKey == "" { user.ServiceKey = fmt.Sprintf("bbs_%s_mock_service_key", strings.ReplaceAll(user.Email, "@", "_")) } @@ -735,7 +801,9 @@ func (c *mockClient) UpdateUser(ctx context.Context, user *v1pb.User, updateMask if slices.Contains(updateMasks, "phone") { existed.Phone = user.Phone } + mu.Lock() c.userMap[user.Name] = existed + mu.Unlock() return c.userMap[user.Name], nil } @@ -747,7 +815,9 @@ func (c *mockClient) DeleteUser(ctx context.Context, userName string) error { } user.State = v1pb.State_DELETED + mu.Lock() c.userMap[user.Name] = user + mu.Unlock() return nil } @@ -760,13 +830,17 @@ func (c *mockClient) UndeleteUser(ctx context.Context, userName string) (*v1pb.U } user.State = v1pb.State_ACTIVE + mu.Lock() c.userMap[user.Name] = user + mu.Unlock() return c.userMap[user.Name], nil } // ListGroup list all groups. func (c *mockClient) ListGroup(_ context.Context, _ *api.GroupFilter) ([]*v1pb.Group, error) { + mu.RLock() + defer mu.RUnlock() groups := make([]*v1pb.Group, 0) for _, group := range c.groupMap { groups = append(groups, group) @@ -777,6 +851,8 @@ func (c *mockClient) ListGroup(_ context.Context, _ *api.GroupFilter) ([]*v1pb.G // GetGroup gets the group by name. func (c *mockClient) GetGroup(_ context.Context, name string) (*v1pb.Group, error) { + mu.RLock() + defer mu.RUnlock() group, ok := c.groupMap[name] if !ok { return nil, errors.Errorf("Cannot found group %s", name) @@ -787,6 +863,8 @@ func (c *mockClient) GetGroup(_ context.Context, name string) (*v1pb.Group, erro // CreateGroup creates the group. func (c *mockClient) CreateGroup(_ context.Context, email string, group *v1pb.Group) (*v1pb.Group, error) { + mu.Lock() + defer mu.Unlock() groupName := fmt.Sprintf("%s%s", GroupNamePrefix, email) group.Name = groupName c.groupMap[groupName] = group @@ -808,23 +886,31 @@ func (c *mockClient) UpdateGroup(ctx context.Context, group *v1pb.Group, updateM if slices.Contains(updateMasks, "members") { existed.Members = group.Members } + mu.Lock() c.groupMap[existed.Name] = existed + mu.Unlock() return existed, nil } // DeleteGroup deletes the group by name. func (c *mockClient) DeleteGroup(_ context.Context, name string) error { + mu.Lock() + defer mu.Unlock() delete(c.groupMap, name) return nil } // GetWorkspaceIAMPolicy gets the workspace IAM policy. func (*mockClient) GetWorkspaceIAMPolicy(_ context.Context) (*v1pb.IamPolicy, error) { + mu.RLock() + defer mu.RUnlock() return workspaceIAMPolicy, nil } // SetWorkspaceIAMPolicy sets the workspace IAM policy. func (*mockClient) SetWorkspaceIAMPolicy(_ context.Context, update *v1pb.SetIamPolicyRequest) (*v1pb.IamPolicy, error) { + mu.Lock() + defer mu.Unlock() if v := update.Policy; v != nil { workspaceIAMPolicy = v } @@ -833,6 +919,8 @@ func (*mockClient) SetWorkspaceIAMPolicy(_ context.Context, update *v1pb.SetIamP // ListRole will returns all roles. func (c *mockClient) ListRole(_ context.Context) (*v1pb.ListRolesResponse, error) { + mu.RLock() + defer mu.RUnlock() roles := make([]*v1pb.Role, 0) for _, role := range c.roleMap { roles = append(roles, role) @@ -845,6 +933,8 @@ func (c *mockClient) ListRole(_ context.Context) (*v1pb.ListRolesResponse, error // GetRole gets the role by full name. func (c *mockClient) GetRole(_ context.Context, roleName string) (*v1pb.Role, error) { + mu.RLock() + defer mu.RUnlock() role, ok := c.roleMap[roleName] if !ok { return nil, errors.Errorf("Cannot found role %s", roleName) @@ -855,6 +945,8 @@ func (c *mockClient) GetRole(_ context.Context, roleName string) (*v1pb.Role, er // CreateRole creates the role. func (c *mockClient) CreateRole(_ context.Context, roleID string, role *v1pb.Role) (*v1pb.Role, error) { + mu.Lock() + defer mu.Unlock() roleName := fmt.Sprintf("%s%s", RoleNamePrefix, roleID) role.Name = roleName c.roleMap[roleName] = role @@ -876,18 +968,24 @@ func (c *mockClient) UpdateRole(ctx context.Context, role *v1pb.Role, updateMask if slices.Contains(updateMasks, "permissions") { existed.Permissions = role.Permissions } + mu.Lock() c.roleMap[existed.Name] = existed + mu.Unlock() return c.roleMap[existed.Name], nil } // DeleteRole deletes the role by name. func (c *mockClient) DeleteRole(_ context.Context, roleName string) error { + mu.Lock() + defer mu.Unlock() delete(c.roleMap, roleName) return nil } // ListReviewConfig will return review configs. func (c *mockClient) ListReviewConfig(_ context.Context) (*v1pb.ListReviewConfigsResponse, error) { + mu.RLock() + defer mu.RUnlock() configs := make([]*v1pb.ReviewConfig, 0) for _, config := range c.reviewConfigMap { configs = append(configs, config) @@ -899,6 +997,8 @@ func (c *mockClient) ListReviewConfig(_ context.Context) (*v1pb.ListReviewConfig // GetReviewConfig gets the review config by full name. func (c *mockClient) GetReviewConfig(_ context.Context, reviewConfigName string) (*v1pb.ReviewConfig, error) { + mu.RLock() + defer mu.RUnlock() config, ok := c.reviewConfigMap[reviewConfigName] if !ok { return nil, errors.Errorf("Cannot found review config %s", reviewConfigName) @@ -908,6 +1008,8 @@ func (c *mockClient) GetReviewConfig(_ context.Context, reviewConfigName string) // UpsertReviewConfig updates or creates the review config. func (c *mockClient) UpsertReviewConfig(_ context.Context, reviewConfig *v1pb.ReviewConfig, updateMasks []string) (*v1pb.ReviewConfig, error) { + mu.Lock() + defer mu.Unlock() existed, ok := c.reviewConfigMap[reviewConfig.Name] if !ok { // Create new review config @@ -936,12 +1038,16 @@ func (c *mockClient) UpsertReviewConfig(_ context.Context, reviewConfig *v1pb.Re // DeleteReviewConfig deletes the review config. func (c *mockClient) DeleteReviewConfig(_ context.Context, reviewConfigName string) error { + mu.Lock() + defer mu.Unlock() delete(c.reviewConfigMap, reviewConfigName) return nil } // ListRisk lists the risk. func (c *mockClient) ListRisk(_ context.Context) ([]*v1pb.Risk, error) { + mu.RLock() + defer mu.RUnlock() risks := make([]*v1pb.Risk, 0) for _, risk := range c.riskMap { risks = append(risks, risk) @@ -951,6 +1057,8 @@ func (c *mockClient) ListRisk(_ context.Context) ([]*v1pb.Risk, error) { // GetRisk gets the risk by full name. func (c *mockClient) GetRisk(_ context.Context, riskName string) (*v1pb.Risk, error) { + mu.RLock() + defer mu.RUnlock() risk, ok := c.riskMap[riskName] if !ok { return nil, errors.Errorf("Cannot found risk %s", riskName) @@ -961,6 +1069,8 @@ func (c *mockClient) GetRisk(_ context.Context, riskName string) (*v1pb.Risk, er // CreateRisk creates the risk. func (c *mockClient) CreateRisk(_ context.Context, risk *v1pb.Risk) (*v1pb.Risk, error) { // Generate a unique name for the risk if not set + mu.Lock() + defer mu.Unlock() if risk.Name == "" { risk.Name = fmt.Sprintf("risks/%d", len(c.riskMap)+1) } @@ -973,6 +1083,8 @@ func (c *mockClient) CreateRisk(_ context.Context, risk *v1pb.Risk) (*v1pb.Risk, // UpdateRisk updates the risk. func (c *mockClient) UpdateRisk(_ context.Context, risk *v1pb.Risk, updateMasks []string) (*v1pb.Risk, error) { + mu.Lock() + defer mu.Unlock() existed, ok := c.riskMap[risk.Name] if !ok { return nil, errors.Errorf("Cannot found risk %s", risk.Name) @@ -997,6 +1109,8 @@ func (c *mockClient) UpdateRisk(_ context.Context, risk *v1pb.Risk, updateMasks // DeleteRisk deletes the risk by name. func (c *mockClient) DeleteRisk(_ context.Context, riskName string) error { + mu.Lock() + defer mu.Unlock() delete(c.riskMap, riskName) return nil } @@ -1023,6 +1137,8 @@ func FindEnvironment(ctx context.Context, client api.Client, name string) (*v1pb // ListDatabaseGroup list all database groups in a project. func (c *mockClient) ListDatabaseGroup(_ context.Context, projectName string) (*v1pb.ListDatabaseGroupsResponse, error) { + mu.RLock() + defer mu.RUnlock() groups := make([]*v1pb.DatabaseGroup, 0) for name, group := range c.databaseGroupMap { // Only return groups that belong to the specified project @@ -1037,6 +1153,8 @@ func (c *mockClient) ListDatabaseGroup(_ context.Context, projectName string) (* // GetDatabaseGroup gets the database group by name. func (c *mockClient) GetDatabaseGroup(_ context.Context, groupName string, _ v1pb.DatabaseGroupView) (*v1pb.DatabaseGroup, error) { + mu.RLock() + defer mu.RUnlock() group, ok := c.databaseGroupMap[groupName] if !ok { return nil, errors.Errorf("Cannot found database group %s", groupName) @@ -1046,6 +1164,8 @@ func (c *mockClient) GetDatabaseGroup(_ context.Context, groupName string, _ v1p // CreateDatabaseGroup creates the database group. func (c *mockClient) CreateDatabaseGroup(_ context.Context, projectID, groupID string, group *v1pb.DatabaseGroup) (*v1pb.DatabaseGroup, error) { + mu.Lock() + defer mu.Unlock() groupName := fmt.Sprintf("%s%s/databaseGroups/%s", ProjectNamePrefix, projectID, groupID) group.Name = groupName if _, exists := c.databaseGroupMap[groupName]; exists { @@ -1057,6 +1177,8 @@ func (c *mockClient) CreateDatabaseGroup(_ context.Context, projectID, groupID s // UpdateDatabaseGroup updates the database group. func (c *mockClient) UpdateDatabaseGroup(_ context.Context, group *v1pb.DatabaseGroup, updateMasks []string) (*v1pb.DatabaseGroup, error) { + mu.Lock() + defer mu.Unlock() existed, ok := c.databaseGroupMap[group.Name] if !ok { return nil, errors.Errorf("Cannot found database group %s", group.Name) @@ -1077,6 +1199,8 @@ func (c *mockClient) UpdateDatabaseGroup(_ context.Context, group *v1pb.Database // DeleteDatabaseGroup deletes the database group by name. func (c *mockClient) DeleteDatabaseGroup(_ context.Context, groupName string) error { + mu.Lock() + defer mu.Unlock() delete(c.databaseGroupMap, groupName) return nil } From 68ae38d223790bd98890685c42aebcfadbc5f7a5 Mon Sep 17 00:00:00 2001 From: ecmadao Date: Fri, 10 Oct 2025 14:20:57 +0800 Subject: [PATCH 5/6] chore: update risk level --- docs/data-sources/policy.md | 5 +---- docs/resources/policy.md | 5 +---- docs/resources/risk.md | 2 +- examples/setup/risk.tf | 2 +- go.mod | 4 ++-- go.sum | 8 ++++---- provider/internal/mock_client.go | 2 +- provider/resource_risk.go | 18 ++++++++++-------- provider/resource_risk_test.go | 22 +++++++++++----------- tutorials/4-2-risk.tf | 6 +++--- 10 files changed, 35 insertions(+), 39 deletions(-) diff --git a/docs/data-sources/policy.md b/docs/data-sources/policy.md index 10bf91f..fc119bb 100644 --- a/docs/data-sources/policy.md +++ b/docs/data-sources/policy.md @@ -97,13 +97,10 @@ Optional: ### Nested Schema for `query_data_policy` -Required: +Optional: - `disable_copy_data` (Boolean) Disable copying data in the SQL editor - `disable_export` (Boolean) Disable export data in the SQL editor - -Optional: - - `maximum_result_rows` (Number) The return rows limit. If the value <= 0, will be treated as no limit. The default value is -1. - `maximum_result_size` (Number) The size limit in bytes. The default value is 100MB, we will use the default value if the limit <= 0. - `timeout_in_seconds` (Number) The maximum time allowed for a query to run in SQL Editor. No limit when the value <= 0 diff --git a/docs/resources/policy.md b/docs/resources/policy.md index 855f2bb..af05cb8 100644 --- a/docs/resources/policy.md +++ b/docs/resources/policy.md @@ -97,13 +97,10 @@ Optional: ### Nested Schema for `query_data_policy` -Required: +Optional: - `disable_copy_data` (Boolean) Disable copying data in the SQL editor - `disable_export` (Boolean) Disable export data in the SQL editor - -Optional: - - `maximum_result_rows` (Number) The return rows limit. If the value <= 0, will be treated as no limit. The default value is -1. - `maximum_result_size` (Number) The size limit in bytes. The default value is 100MB, we will use the default value if the limit <= 0. - `timeout_in_seconds` (Number) The maximum time allowed for a query to run in SQL Editor. No limit when the value <= 0 diff --git a/docs/resources/risk.md b/docs/resources/risk.md index 005dc70..138cd0d 100644 --- a/docs/resources/risk.md +++ b/docs/resources/risk.md @@ -18,7 +18,7 @@ The risk resource. Require ENTERPRISE subscription. Check the docs https://www.b ### Required - `condition` (String) The risk condition. Check the proto message https://github.com/bytebase/bytebase/blob/main/proto/v1/v1/risk_service.proto#L210 for details. -- `level` (Number) The risk level, should be 300, 200 or 100. Higher number means higher level. +- `level` (String) The risk level. Check https://github.com/bytebase/bytebase/blob/fd87c6bfe8a0d4883f25eb480a3b05ed3c2e1727/proto/v1/v1/common.proto#L93 for details - `source` (String) The risk source. Check https://github.com/bytebase/bytebase/blob/main/proto/v1/v1/risk_service.proto#L138 for details - `title` (String) The risk title. diff --git a/examples/setup/risk.tf b/examples/setup/risk.tf index bf5bb5b..b4484f4 100644 --- a/examples/setup/risk.tf +++ b/examples/setup/risk.tf @@ -1,7 +1,7 @@ resource "bytebase_risk" "risk" { title = "Risk for prod environment" source = "DML" - level = 300 + level = "HIGH" active = true condition = "resource.environment_id == \"prod\" && statement.affected_rows >= 100" } diff --git a/go.mod b/go.mod index bbfeaa5..ff1a436 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.24.4 toolchain go1.24.5 require ( - buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010010834-1aa0c6de76e4.1 - buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010010834-1aa0c6de76e4.1 + buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010051414-0534aa930702.1 + buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010051414-0534aa930702.1 connectrpc.com/connect v1.19.1 github.com/hashicorp/go-cty v1.5.0 github.com/hashicorp/terraform-plugin-docs v0.13.0 diff --git a/go.sum b/go.sum index 0193da4..b5c56b3 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010010834-1aa0c6de76e4.1 h1:WNQflXc5jqg/LZikcsJxwVFfJMB0i1O7olWXgBZ/DVM= -buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010010834-1aa0c6de76e4.1/go.mod h1:EhBo6m8asi2++hICIxqtAJdmw8SGYXdxZxPpytC1xUw= -buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010010834-1aa0c6de76e4.1 h1:LKV7Q1GToSWI0IFvWxfw5Ul8AyiR8ItAzq1cKbEGP8U= -buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010010834-1aa0c6de76e4.1/go.mod h1:hCjM3DsxzHQGb3QEnCNYrln9ZgB16AZLOVfRJZRKC0E= +buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010051414-0534aa930702.1 h1:T4v2vd8e4RkC6ccfOdsvmDZ7YnW2YyAT6OFMdwcPgOA= +buf.build/gen/go/bytebase/bytebase/connectrpc/go v1.19.1-20251010051414-0534aa930702.1/go.mod h1:A57O99YuSmuL9rUI39AMITcbFfJc6Ikv48Qb6Y2EgCw= +buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010051414-0534aa930702.1 h1:5bZgEFfHz4kUG6fkHH9wmBG3dxyaA5rKpaprSP0csTo= +buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.36.10-20251010051414-0534aa930702.1/go.mod h1:hCjM3DsxzHQGb3QEnCNYrln9ZgB16AZLOVfRJZRKC0E= connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= diff --git a/provider/internal/mock_client.go b/provider/internal/mock_client.go index 7d74795..449a4ee 100644 --- a/provider/internal/mock_client.go +++ b/provider/internal/mock_client.go @@ -418,7 +418,7 @@ func (c *mockClient) UpdateDatabase(ctx context.Context, patch *v1pb.Database, u } // BatchUpdateDatabases batch updates databases. -func (c *mockClient) BatchUpdateDatabases(ctx context.Context, request *v1pb.BatchUpdateDatabasesRequest) (*v1pb.BatchUpdateDatabasesResponse, error) { +func (c *mockClient) BatchUpdateDatabases(_ context.Context, request *v1pb.BatchUpdateDatabasesRequest) (*v1pb.BatchUpdateDatabasesResponse, error) { mu.Lock() defer mu.Unlock() for _, req := range request.Requests { diff --git a/provider/resource_risk.go b/provider/resource_risk.go index edd5526..923f92c 100644 --- a/provider/resource_risk.go +++ b/provider/resource_risk.go @@ -51,12 +51,14 @@ func resourceRisk() *schema.Resource { Description: "The risk source. Check https://github.com/bytebase/bytebase/blob/main/proto/v1/v1/risk_service.proto#L138 for details", }, "level": { - Type: schema.TypeInt, + Type: schema.TypeString, Required: true, - ValidateFunc: validation.IntInSlice([]int{ - 300, 200, 100, - }), - Description: "The risk level, should be 300, 200 or 100. Higher number means higher level.", + ValidateFunc: validation.StringInSlice([]string{ + v1pb.RiskLevel_HIGH.String(), + v1pb.RiskLevel_MODERATE.String(), + v1pb.RiskLevel_LOW.String(), + }, false), + Description: "The risk level. Check https://github.com/bytebase/bytebase/blob/fd87c6bfe8a0d4883f25eb480a3b05ed3c2e1727/proto/v1/v1/common.proto#L93 for details", }, "active": { Type: schema.TypeBool, @@ -102,7 +104,7 @@ func setRisk(d *schema.ResourceData, risk *v1pb.Risk) diag.Diagnostics { if err := d.Set("source", risk.Source.String()); err != nil { return diag.Errorf("cannot set source for risk: %s", err.Error()) } - if err := d.Set("level", int(risk.Level)); err != nil { + if err := d.Set("level", risk.Level.String()); err != nil { return diag.Errorf("cannot set level for risk: %s", err.Error()) } if err := d.Set("active", risk.Active); err != nil { @@ -121,7 +123,7 @@ func resourceRiskCreate(ctx context.Context, d *schema.ResourceData, m interface created, err := c.CreateRisk(ctx, &v1pb.Risk{ Title: d.Get("title").(string), Active: d.Get("active").(bool), - Level: int32(d.Get("level").(int)), + Level: v1pb.RiskLevel(v1pb.RiskLevel_value[d.Get("level").(string)]), Source: v1pb.Risk_Source(v1pb.Risk_Source_value[d.Get("source").(string)]), Condition: &expr.Expr{ Expression: d.Get("condition").(string), @@ -156,7 +158,7 @@ func resourceRiskUpdate(ctx context.Context, d *schema.ResourceData, m interface } if d.HasChange("level") { updateMasks = append(updateMasks, "level") - existedRisk.Level = int32(d.Get("level").(int)) + existedRisk.Level = v1pb.RiskLevel(v1pb.RiskLevel_value[d.Get("level").(string)]) } if d.HasChange("source") { updateMasks = append(updateMasks, "source") diff --git a/provider/resource_risk_test.go b/provider/resource_risk_test.go index 02dbe12..7f8c66d 100644 --- a/provider/resource_risk_test.go +++ b/provider/resource_risk_test.go @@ -22,8 +22,8 @@ func TestAccRisk(t *testing.T) { title := "Test Risk" titleUpdated := "Updated Test Risk" source := v1pb.Risk_DDL.String() - level := 300 - levelUpdated := 200 + level := v1pb.RiskLevel_HIGH.String() + levelUpdated := v1pb.RiskLevel_MODERATE.String() resource.Test(t, resource.TestCase{ PreCheck: func() { @@ -39,7 +39,7 @@ func TestAccRisk(t *testing.T) { internal.TestCheckResourceExists(resourceName), resource.TestCheckResourceAttr(resourceName, "title", title), resource.TestCheckResourceAttr(resourceName, "source", source), - resource.TestCheckResourceAttr(resourceName, "level", fmt.Sprintf("%d", level)), + resource.TestCheckResourceAttr(resourceName, "level", level), resource.TestCheckResourceAttr(resourceName, "active", "true"), resource.TestCheckResourceAttrSet(resourceName, "name"), ), @@ -51,7 +51,7 @@ func TestAccRisk(t *testing.T) { internal.TestCheckResourceExists(resourceName), resource.TestCheckResourceAttr(resourceName, "title", titleUpdated), resource.TestCheckResourceAttr(resourceName, "source", source), - resource.TestCheckResourceAttr(resourceName, "level", fmt.Sprintf("%d", levelUpdated)), + resource.TestCheckResourceAttr(resourceName, "level", levelUpdated), resource.TestCheckResourceAttr(resourceName, "active", "false"), ), }, @@ -62,7 +62,7 @@ func TestAccRisk(t *testing.T) { internal.TestCheckResourceExists(resourceName), resource.TestCheckResourceAttr(resourceName, "title", titleUpdated), resource.TestCheckResourceAttr(resourceName, "source", v1pb.Risk_DML.String()), - resource.TestCheckResourceAttr(resourceName, "level", fmt.Sprintf("%d", level)), + resource.TestCheckResourceAttr(resourceName, "level", level), resource.TestCheckResourceAttr(resourceName, "active", "true"), ), }, @@ -92,7 +92,7 @@ func TestAccRisk_AllSources(t *testing.T) { CheckDestroy: testAccCheckRiskDestroy, Steps: []resource.TestStep{ { - Config: testAccCheckRiskResource(identifier, fmt.Sprintf("Test Risk %s", source), source, 300, true), + Config: testAccCheckRiskResource(identifier, fmt.Sprintf("Test Risk %s", source), source, v1pb.RiskLevel_HIGH.String(), true), Check: resource.ComposeTestCheckFunc( internal.TestCheckResourceExists(resourceName), resource.TestCheckResourceAttr(resourceName, "source", source), @@ -120,7 +120,7 @@ func TestAccRisk_InvalidInput(t *testing.T) { resource "bytebase_risk" "%s" { title = "" source = "%s" - level = 300 + level = "HIGH" active = true condition = "{\"expressions\":[{\"title\":\"High risk database\",\"expression\":\"resource.database == 'production'\"}]}" } @@ -133,7 +133,7 @@ resource "bytebase_risk" "%s" { resource "bytebase_risk" "%s" { title = "Test Risk" source = "INVALID_SOURCE" - level = 300 + level = "HIGH" active = true condition = "{\"expressions\":[{\"title\":\"High risk database\",\"expression\":\"resource.database == 'production'\"}]}" } @@ -146,7 +146,7 @@ resource "bytebase_risk" "%s" { resource "bytebase_risk" "%s" { title = "Test Risk" source = "%s" - level = 150 + level = "RISK_LEVEL_UNSPECIFIED" active = true condition = "{\"expressions\":[{\"title\":\"High risk database\",\"expression\":\"resource.database == 'production'\"}]}" } @@ -157,7 +157,7 @@ resource "bytebase_risk" "%s" { }) } -func testAccCheckRiskResource(identifier, title, source string, level int, active bool) string { +func testAccCheckRiskResource(identifier, title, source string, level string, active bool) string { // Different conditions based on source type condition := "" switch source { @@ -209,7 +209,7 @@ func testAccCheckRiskResource(identifier, title, source string, level int, activ resource "bytebase_risk" "%s" { title = "%s" source = "%s" - level = %d + level = "%s" active = %t condition = jsonencode(%s) } diff --git a/tutorials/4-2-risk.tf b/tutorials/4-2-risk.tf index 4506445..b5dadbb 100644 --- a/tutorials/4-2-risk.tf +++ b/tutorials/4-2-risk.tf @@ -1,7 +1,7 @@ resource "bytebase_risk" "dml_moderate" { title = "DML Moderate Risk" source = "DML" - level = 200 + level = "MODERATE" active = true condition = "resource.environment_id == \"prod\" && statement.affected_rows >= 100" } @@ -9,7 +9,7 @@ resource "bytebase_risk" "dml_moderate" { resource "bytebase_risk" "ddl_high" { title = "DDL High Risk" source = "DDL" - level = 300 + level = "HIGH" active = true condition = "resource.environment_id == \"prod\"" -} \ No newline at end of file +} From e41a7f2427e2df4ffa1fb274d83bab2359f20dfe Mon Sep 17 00:00:00 2001 From: ecmadao Date: Fri, 10 Oct 2025 14:28:51 +0800 Subject: [PATCH 6/6] chore: update version --- README.md | 2 +- VERSION | 2 +- examples/database/main.tf | 2 +- examples/database_group/main.tf | 2 +- examples/environments/main.tf | 2 +- examples/groups/main.tf | 2 +- examples/iamPolicy/main.tf | 2 +- examples/instances/main.tf | 2 +- examples/policies/main.tf | 2 +- examples/projects/main.tf | 2 +- examples/risk/main.tf | 2 +- examples/roles/main.tf | 2 +- examples/settings/main.tf | 2 +- examples/setup/main.tf | 2 +- examples/sql_review/main.tf | 2 +- examples/users/main.tf | 2 +- provider/data_source_risk.go | 2 +- provider/data_source_risk_list.go | 4 ++-- tutorials/0-provider.tf | 2 +- 19 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index dce551e..9c63dd3 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ using Terraform Bytebase Provider to prepare those instances ready for applicati - [Go](https://golang.org/doc/install) (1.19 or later) - [Terraform](https://developer.hashicorp.com/terraform/downloads?product_intent=terraform) (1.3.5 or later) -- [Bytebase](https://github.com/bytebase/bytebase) (3.10.0 or later) +- [Bytebase](https://github.com/bytebase/bytebase) (3.11.1 or later) > If you have problems running `terraform` in MacOS with Apple Silicon, you can following https://stackoverflow.com/questions/66281882/how-can-i-get-terraform-init-to-run-on-my-apple-silicon-macbook-pro-for-the-go and use the `tfenv`. diff --git a/VERSION b/VERSION index e0af123..7a82656 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.11.0 \ No newline at end of file +3.11.1 \ No newline at end of file diff --git a/examples/database/main.tf b/examples/database/main.tf index e284bb0..2256c83 100644 --- a/examples/database/main.tf +++ b/examples/database/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/database_group/main.tf b/examples/database_group/main.tf index b0a34e1..eec14c4 100644 --- a/examples/database_group/main.tf +++ b/examples/database_group/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/environments/main.tf b/examples/environments/main.tf index b35235a..d9d4310 100644 --- a/examples/environments/main.tf +++ b/examples/environments/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # 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 6977b0d..4849143 100644 --- a/examples/groups/main.tf +++ b/examples/groups/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/iamPolicy/main.tf b/examples/iamPolicy/main.tf index be14b6a..7872f9f 100644 --- a/examples/iamPolicy/main.tf +++ b/examples/iamPolicy/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # 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 c2aebd5..f91d42e 100644 --- a/examples/instances/main.tf +++ b/examples/instances/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # 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 6b24618..73ff0fb 100644 --- a/examples/policies/main.tf +++ b/examples/policies/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # 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 1c9f744..ee767fc 100644 --- a/examples/projects/main.tf +++ b/examples/projects/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/risk/main.tf b/examples/risk/main.tf index 5592202..234e9c8 100644 --- a/examples/risk/main.tf +++ b/examples/risk/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # 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 1ee2b72..8852b38 100644 --- a/examples/roles/main.tf +++ b/examples/roles/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # 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 fef292f..c98933f 100644 --- a/examples/settings/main.tf +++ b/examples/settings/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/setup/main.tf b/examples/setup/main.tf index a0a1da6..b82f293 100644 --- a/examples/setup/main.tf +++ b/examples/setup/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/sql_review/main.tf b/examples/sql_review/main.tf index 11c7506..0c59570 100644 --- a/examples/sql_review/main.tf +++ b/examples/sql_review/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # 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 12a0bac..92907e2 100644 --- a/examples/users/main.tf +++ b/examples/users/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/provider/data_source_risk.go b/provider/data_source_risk.go index 2d3b4f7..a52d62b 100644 --- a/provider/data_source_risk.go +++ b/provider/data_source_risk.go @@ -30,7 +30,7 @@ func dataSourceRisk() *schema.Resource { Description: "The risk source.", }, "level": { - Type: schema.TypeInt, + Type: schema.TypeString, Computed: true, Description: "The risk level.", }, diff --git a/provider/data_source_risk_list.go b/provider/data_source_risk_list.go index 3089952..1c4ee99 100644 --- a/provider/data_source_risk_list.go +++ b/provider/data_source_risk_list.go @@ -37,7 +37,7 @@ func dataSourceRiskList() *schema.Resource { Description: "The risk source.", }, "level": { - Type: schema.TypeInt, + Type: schema.TypeString, Computed: true, Description: "The risk level.", }, @@ -75,7 +75,7 @@ func dataSourceRiskListRead(ctx context.Context, d *schema.ResourceData, m inter raw["name"] = risk.Name raw["title"] = risk.Title raw["source"] = risk.Source.String() - raw["level"] = int(risk.Level) + raw["level"] = risk.Level.String() raw["active"] = risk.Active raw["condition"] = risk.Condition.Expression diff --git a/tutorials/0-provider.tf b/tutorials/0-provider.tf index 0e98f00..4ab62b9 100644 --- a/tutorials/0-provider.tf +++ b/tutorials/0-provider.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "3.10.0" + version = "3.11.1" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" }