Skip to content

Commit 1191aa1

Browse files
vsai12claude
andauthored
fix: resolve perpetual Terraform plan diffs for workspace_profile and data_sources (#176)
* fix: add Computed to optional workspace_profile fields to prevent perpetual diff Fields like sql_result_size, query_timeout_in_seconds, password_restriction, external_url, and token duration fields are server-managed with defaults. Without Computed, Terraform plans to null them when absent from HCL, but the update never sends the change (field mask excludes them), causing a perpetual diff. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: prevent data_sources perpetual diff from spurious redisType default The API returns redisType: STANDALONE for all data sources regardless of engine. The flatten function wrote this to state, changing the TypeSet hash vs the config-side hash (which lacks redis_type for non-Redis instances). Guard redis-specific fields behind an engine check so they only appear in state for Redis instances. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b106656 commit 1191aa1

3 files changed

Lines changed: 25 additions & 8 deletions

File tree

provider/data_source_instance_list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ func dataSourceInstanceListRead(ctx context.Context, d *schema.ResourceData, m i
193193
}
194194
ins["sync_databases"] = instance.SyncDatabases
195195

196-
dataSources, err := flattenDataSourceList(d, instance.DataSources)
196+
dataSources, err := flattenDataSourceList(d, instance.DataSources, instance.Engine)
197197
if err != nil {
198198
return diag.FromErr(err)
199199
}

provider/data_source_setting.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ func getWorkspaceProfileSetting(computed bool) *schema.Schema {
283283
"external_url": {
284284
Type: schema.TypeString,
285285
Optional: true,
286+
Computed: true,
286287
Description: "The URL user visits Bytebase. The external URL is used for: 1. Constructing the correct callback URL when configuring the VCS provider. The callback URL points to the frontend; 2. Creating the correct webhook endpoint when configuring the project GitOps workflow. The webhook endpoint points to the backend.",
287288
},
288289
"disallow_signup": {
@@ -321,6 +322,7 @@ func getWorkspaceProfileSetting(computed bool) *schema.Schema {
321322
"maximum_role_expiration_in_seconds": {
322323
Type: schema.TypeInt,
323324
Optional: true,
325+
Computed: true,
324326
Description: "The max duration in seconds for role expired. If the value is less than or equal to 0, we will remove the setting. AKA no limit.",
325327
},
326328
"announcement": {
@@ -373,66 +375,78 @@ func getWorkspaceProfileSetting(computed bool) *schema.Schema {
373375
"sql_result_size": {
374376
Type: schema.TypeInt,
375377
Optional: true,
378+
Computed: true,
376379
Description: "The size limit in bytes for SQL query results. The default value is 100MB.",
377380
},
378381
"query_timeout_in_seconds": {
379382
Type: schema.TypeInt,
380383
Optional: true,
384+
Computed: true,
381385
Description: "The maximum time allowed for a query to run in SQL Editor, in seconds. No limit when the value <= 0.",
382386
},
383387
"refresh_token_duration_in_seconds": {
384388
Type: schema.TypeInt,
385389
Optional: true,
390+
Computed: true,
386391
ValidateFunc: validation.IntAtLeast(3600),
387392
Description: "The duration for refresh token in seconds. Default is 604800 (7 days). The duration should be at least 3600 (one hour).",
388393
},
389394
"access_token_duration_in_seconds": {
390395
Type: schema.TypeInt,
391396
Optional: true,
397+
Computed: true,
392398
ValidateFunc: validation.IntAtLeast(60),
393399
Description: "The duration for access token in seconds. Default is 3600 (1 hour). The duration should be at least 60 (one minute).",
394400
},
395401
"password_restriction": {
396402
Type: schema.TypeList,
397403
Optional: true,
404+
Computed: true,
398405
MaxItems: 1,
399406
Description: "Password restriction settings.",
400407
Elem: &schema.Resource{
401408
Schema: map[string]*schema.Schema{
402409
"min_length": {
403410
Type: schema.TypeInt,
404411
Optional: true,
412+
Computed: true,
405413
Description: "min_length is the minimum length for password, should be no less than 8.",
406414
ValidateFunc: validation.IntAtLeast(8),
407415
},
408416
"require_number": {
409417
Type: schema.TypeBool,
410418
Optional: true,
419+
Computed: true,
411420
Description: "require_number requires the password must contain at least one number.",
412421
},
413422
"require_letter": {
414423
Type: schema.TypeBool,
415424
Optional: true,
425+
Computed: true,
416426
Description: "require_letter requires the password must contain at least one letter, regardless of upper case or lower case.",
417427
},
418428
"require_uppercase_letter": {
419429
Type: schema.TypeBool,
420430
Optional: true,
431+
Computed: true,
421432
Description: "require_uppercase_letter requires the password must contain at least one upper case letter.",
422433
},
423434
"require_special_character": {
424435
Type: schema.TypeBool,
425436
Optional: true,
437+
Computed: true,
426438
Description: "require_special_character requires the password must contain at least one special character.",
427439
},
428440
"require_reset_password_for_first_login": {
429441
Type: schema.TypeBool,
430442
Optional: true,
443+
Computed: true,
431444
Description: "require_reset_password_for_first_login requires users to reset their password after the 1st login.",
432445
},
433446
"password_rotation_in_seconds": {
434447
Type: schema.TypeInt,
435448
Optional: true,
449+
Computed: true,
436450
ValidateFunc: validation.IntAtLeast(86400),
437451
Description: "password_rotation requires users to reset their password after the duration. The duration should be at least 86400 (one day).",
438452
},

provider/resource_instance.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ func setInstanceMessage(
965965
}
966966
}
967967

968-
dataSources, err := flattenDataSourceList(d, instance.DataSources)
968+
dataSources, err := flattenDataSourceList(d, instance.DataSources, instance.Engine)
969969
if err != nil {
970970
return diag.FromErr(err)
971971
}
@@ -994,7 +994,7 @@ func setInstanceMessage(
994994
return nil
995995
}
996996

997-
func flattenDataSourceList(d *schema.ResourceData, dataSourceList []*v1pb.DataSource) ([]interface{}, error) {
997+
func flattenDataSourceList(d *schema.ResourceData, dataSourceList []*v1pb.DataSource, engine v1pb.Engine) ([]interface{}, error) {
998998
oldDataSourceList, err := convertDataSourceCreateList(d, false)
999999
if err != nil {
10001000
return nil, err
@@ -1050,12 +1050,15 @@ func flattenDataSourceList(d *schema.ResourceData, dataSourceList []*v1pb.DataSo
10501050
raw["authentication_type"] = dataSource.AuthenticationType.String()
10511051
}
10521052

1053-
// Redis - non-sensitive fields
1054-
if dataSource.RedisType != v1pb.DataSource_REDIS_TYPE_UNSPECIFIED {
1055-
raw["redis_type"] = dataSource.RedisType.String()
1053+
// Redis - non-sensitive fields (only for Redis engine to avoid hash mismatch
1054+
// from spurious default values the API returns for non-Redis instances)
1055+
if engine == v1pb.Engine_REDIS {
1056+
if dataSource.RedisType != v1pb.DataSource_REDIS_TYPE_UNSPECIFIED {
1057+
raw["redis_type"] = dataSource.RedisType.String()
1058+
}
1059+
raw["master_name"] = dataSource.MasterName
1060+
raw["master_username"] = dataSource.MasterUsername
10561061
}
1057-
raw["master_name"] = dataSource.MasterName
1058-
raw["master_username"] = dataSource.MasterUsername
10591062

10601063
// Cloud-specific
10611064
raw["region"] = dataSource.Region

0 commit comments

Comments
 (0)