Skip to content

Commit fd7aad4

Browse files
authored
fix(warehouse): BQ credentials drift changes (#84)
* feat(warehouse): credentials drift changes * feat(warehouse): BQ credentials drift detection finished * fix(warehouse): BQ tests with new credentials structure
1 parent 0488b11 commit fd7aad4

File tree

5 files changed

+138
-39
lines changed

5 files changed

+138
-39
lines changed

client/monte_carlo_client.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ type AddConnection struct {
8383
AddConnection struct {
8484
Connection struct {
8585
Uuid string
86+
CreatedOn string
87+
UpdatedOn string
8688
Warehouse struct {
8789
Name string
8890
Uuid string
@@ -95,8 +97,10 @@ type GetWarehouse struct {
9597
GetWarehouse *struct {
9698
Name string `json:"name"`
9799
Connections []struct {
98-
Uuid string `json:"uuid"`
99-
Type string `json:"type"`
100+
Uuid string `json:"uuid"`
101+
Type string `json:"type"`
102+
CreatedOn string `json:"createdOn"`
103+
UpdatedOn string `json:"updatedOn"`
100104
} `json:"connections"`
101105
DataCollector struct {
102106
Uuid string `json:"uuid"`
@@ -108,7 +112,7 @@ const BigQueryConnectionType = "bigquery"
108112
const BigQueryConnectionTypeResponse = "BIGQUERY"
109113
const TransactionalConnectionType = "transactional-db"
110114
const TransactionalConnectionTypeResponse = "TRANSACTIONAL_DB"
111-
const GetWarehouseQuery string = "query getWarehouse($uuid: UUID) { getWarehouse(uuid: $uuid) { name,connections{uuid,type},dataCollector{uuid} } }"
115+
const GetWarehouseQuery string = "query getWarehouse($uuid: UUID) { getWarehouse(uuid: $uuid) { name,connections{uuid,type,createdOn,updatedOn},dataCollector{uuid} } }"
112116

113117
type RemoveConnection struct {
114118
RemoveConnection struct {
@@ -127,7 +131,8 @@ type SetWarehouseName struct {
127131

128132
type UpdateCredentials struct {
129133
UpdateCredentials struct {
130-
Success bool
134+
Success bool
135+
UpdatedAt string
131136
} `graphql:"updateCredentials(changes: $changes, connectionId: $connectionId, shouldReplace: $shouldReplace, shouldValidate: $shouldValidate)"`
132137
}
133138

@@ -299,15 +304,14 @@ type CreateOrUpdateComparisonRule struct {
299304
Severity string
300305
RuleType string
301306
WarehouseUuid string
302-
Comparisons []struct {
307+
Comparisons []struct {
303308
ComparisonType string
304309
FullTableId string
305310
FullTableIds []string
306311
Field string
307312
Metric string
308313
Operator string
309314
Threshold float64
310-
311315
}
312316
}
313317
} `graphql:"createOrUpdateComparisonRule(comparisons: $comparisons, customRuleUuid: $customRuleUuid, description: $description, queryResultType: $queryResultType, scheduleConfig: $scheduleConfig, sourceConnectionId: $sourceConnectionId, sourceDwId: $sourceDwId, sourceSqlQuery: $sourceSqlQuery, targetConnectionId: $targetConnectionId, targetDwId: $targetDwId, targetSqlQuery: $targetSqlQuery)"`

internal/warehouse/bigquery_warehouse.go

Lines changed: 122 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ type BigQueryWarehouseResource struct {
3838

3939
// BigQueryWarehouseResourceModel describes the resource data model according to its Schema.
4040
type BigQueryWarehouseResourceModel struct {
41+
Uuid types.String `tfsdk:"uuid"`
42+
Credentials Credentials `tfsdk:"credentials"`
43+
Name types.String `tfsdk:"name"`
44+
CollectorUuid types.String `tfsdk:"collector_uuid"`
45+
DeletionProtection types.Bool `tfsdk:"deletion_protection"`
46+
}
47+
48+
type Credentials struct {
49+
ConnectionUuid types.String `tfsdk:"connection_uuid"`
50+
ServiceAccountKey types.String `tfsdk:"service_account_key"`
51+
UpdatedAt types.String `tfsdk:"updated_at"`
52+
}
53+
54+
type BigQueryWarehouseResourceModelV1 struct {
4155
Uuid types.String `tfsdk:"uuid"`
4256
ConnectionUuid types.String `tfsdk:"connection_uuid"`
4357
Name types.String `tfsdk:"name"`
@@ -70,11 +84,24 @@ func (r *BigQueryWarehouseResource) Schema(ctx context.Context, req resource.Sch
7084
stringplanmodifier.UseStateForUnknown(),
7185
},
7286
},
73-
"connection_uuid": schema.StringAttribute{
74-
Computed: true,
75-
Optional: false,
76-
PlanModifiers: []planmodifier.String{
77-
stringplanmodifier.UseStateForUnknown(),
87+
"credentials": schema.SingleNestedAttribute{
88+
Required: true,
89+
Attributes: map[string]schema.Attribute{
90+
"connection_uuid": schema.StringAttribute{
91+
Computed: true,
92+
Optional: false,
93+
PlanModifiers: []planmodifier.String{
94+
stringplanmodifier.UseStateForUnknown(),
95+
},
96+
},
97+
"service_account_key": schema.StringAttribute{
98+
Required: true,
99+
Sensitive: true,
100+
},
101+
"updated_at": schema.StringAttribute{
102+
Computed: true,
103+
Optional: false,
104+
},
78105
},
79106
},
80107
"name": schema.StringAttribute{
@@ -87,10 +114,6 @@ func (r *BigQueryWarehouseResource) Schema(ctx context.Context, req resource.Sch
87114
stringplanmodifier.RequiresReplaceIfConfigured(),
88115
},
89116
},
90-
"service_account_key": schema.StringAttribute{
91-
Required: true,
92-
Sensitive: true,
93-
},
94117
"deletion_protection": schema.BoolAttribute{
95118
Optional: true,
96119
Computed: true,
@@ -120,8 +143,8 @@ func (r *BigQueryWarehouseResource) Create(ctx context.Context, req resource.Cre
120143
}
121144

122145
data.Uuid = result.Uuid
123-
data.ConnectionUuid = result.ConnectionUuid
124-
data.Name = result.Name
146+
data.Credentials.UpdatedAt = result.Credentials.UpdatedAt
147+
data.Credentials.ConnectionUuid = result.Credentials.ConnectionUuid
125148
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
126149
}
127150

@@ -167,23 +190,35 @@ func (r *BigQueryWarehouseResource) Read(ctx context.Context, req resource.ReadR
167190
}
168191

169192
readConnectionUuid := types.StringNull()
170-
readServiceAccountKey := types.StringNull()
193+
readConnectionSAKey := types.StringNull()
194+
readConnectionUpdatedAt := types.StringNull()
195+
171196
for _, connection := range getResult.GetWarehouse.Connections {
172-
if connection.Uuid == data.ConnectionUuid.ValueString() {
197+
if connection.Uuid == data.Credentials.ConnectionUuid.ValueString() {
173198
if connection.Type != client.BigQueryConnectionTypeResponse {
174199
resp.Diagnostics.AddError(
175200
fmt.Sprintf("Obtained Warehouse [uuid: %s, connection_uuid: %s] but got unexpected connection "+
176201
"type '%s'.", data.Uuid.ValueString(), connection.Uuid, connection.Type),
177202
"Users can manually fix remote state or delete this resource from the Terraform configuration.")
178203
return
179204
}
180-
readConnectionUuid = data.ConnectionUuid
181-
readServiceAccountKey = data.ServiceAccountKey
205+
206+
readConnectionUuid = data.Credentials.ConnectionUuid
207+
readConnectionSAKey = data.Credentials.ServiceAccountKey
208+
readConnectionUpdatedAt = types.StringValue(connection.UpdatedOn)
209+
if connection.UpdatedOn == "" {
210+
readConnectionUpdatedAt = types.StringValue(connection.CreatedOn)
211+
}
182212
}
183213
}
184214

185-
data.ConnectionUuid = readConnectionUuid
186-
data.ServiceAccountKey = readServiceAccountKey
215+
if !readConnectionSAKey.IsNull() && !readConnectionUpdatedAt.Equal(data.Credentials.UpdatedAt) {
216+
readConnectionSAKey = types.StringValue("(unknown external value)")
217+
}
218+
219+
data.Credentials.UpdatedAt = readConnectionUpdatedAt
220+
data.Credentials.ConnectionUuid = readConnectionUuid
221+
data.Credentials.ServiceAccountKey = readConnectionSAKey
187222
data.Name = types.StringValue(getResult.GetWarehouse.Name)
188223
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
189224
}
@@ -207,10 +242,11 @@ func (r *BigQueryWarehouseResource) Update(ctx context.Context, req resource.Upd
207242
return
208243
}
209244

210-
if data.ConnectionUuid.IsUnknown() || data.ConnectionUuid.IsNull() {
245+
if data.Credentials.ConnectionUuid.IsUnknown() || data.Credentials.ConnectionUuid.IsNull() {
211246
if result, diags := r.addConnection(ctx, data); result != nil {
212247
resp.Diagnostics.Append(diags...)
213-
data.ConnectionUuid = result.ConnectionUuid
248+
data.Credentials.UpdatedAt = result.Credentials.UpdatedAt
249+
data.Credentials.ConnectionUuid = result.Credentials.ConnectionUuid
214250
} else {
215251
resp.Diagnostics.Append(diags...)
216252
return
@@ -219,8 +255,8 @@ func (r *BigQueryWarehouseResource) Update(ctx context.Context, req resource.Upd
219255

220256
updateResult := client.UpdateCredentials{}
221257
variables = map[string]interface{}{
222-
"changes": client.JSONString(data.ServiceAccountKey.ValueString()),
223-
"connectionId": client.UUID(data.ConnectionUuid.ValueString()),
258+
"changes": client.JSONString(data.Credentials.ServiceAccountKey.ValueString()),
259+
"connectionId": client.UUID(data.Credentials.ConnectionUuid.ValueString()),
224260
"shouldReplace": true,
225261
"shouldValidate": true,
226262
}
@@ -235,6 +271,8 @@ func (r *BigQueryWarehouseResource) Update(ctx context.Context, req resource.Upd
235271
resp.Diagnostics.AddError(toPrint, "")
236272
return
237273
}
274+
275+
data.Credentials.UpdatedAt = types.StringValue(updateResult.UpdateCredentials.UpdatedAt)
238276
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
239277
}
240278

@@ -256,7 +294,7 @@ func (r *BigQueryWarehouseResource) Delete(ctx context.Context, req resource.Del
256294
}
257295

258296
removeResult := client.RemoveConnection{}
259-
variables := map[string]interface{}{"connectionId": client.UUID(data.ConnectionUuid.ValueString())}
297+
variables := map[string]interface{}{"connectionId": client.UUID(data.Credentials.ConnectionUuid.ValueString())}
260298
if err := r.client.Mutate(ctx, &removeResult, variables); err != nil {
261299
toPrint := fmt.Sprintf("MC client 'RemoveConnection' mutation result - %s", err.Error())
262300
resp.Diagnostics.AddError(toPrint, "")
@@ -272,7 +310,7 @@ func (r *BigQueryWarehouseResource) ImportState(ctx context.Context, req resourc
272310
idsImported := strings.Split(req.ID, ",")
273311
if len(idsImported) == 3 && idsImported[0] != "" && idsImported[1] != "" && idsImported[2] != "" {
274312
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("uuid"), idsImported[0])...)
275-
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("connection_uuid"), idsImported[1])...)
313+
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("credentials").AtName("connection_uuid"), idsImported[1])...)
276314
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("collector_uuid"), idsImported[2])...)
277315
} else {
278316
resp.Diagnostics.AddError("Unexpected Import Identifier", fmt.Sprintf(
@@ -289,7 +327,7 @@ func (r *BigQueryWarehouseResource) addConnection(ctx context.Context, data BigQ
289327
"validationName": "save_credentials",
290328
"connectionDetails": BqConnectionDetails{
291329
"serviceJson": b64.StdEncoding.EncodeToString(
292-
[]byte(data.ServiceAccountKey.ValueString()),
330+
[]byte(data.Credentials.ServiceAccountKey.ValueString()),
293331
),
294332
},
295333
}
@@ -334,8 +372,8 @@ func (r *BigQueryWarehouseResource) addConnection(ctx context.Context, data BigQ
334372
}
335373

336374
data.Uuid = types.StringValue(addResult.AddConnection.Connection.Warehouse.Uuid)
337-
data.ConnectionUuid = types.StringValue(addResult.AddConnection.Connection.Uuid)
338-
data.Name = types.StringValue(addResult.AddConnection.Connection.Warehouse.Name)
375+
data.Credentials.UpdatedAt = types.StringValue(addResult.AddConnection.Connection.CreatedOn)
376+
data.Credentials.ConnectionUuid = types.StringValue(addResult.AddConnection.Connection.Uuid)
339377
return &data, diagsResult
340378
}
341379

@@ -398,7 +436,7 @@ func (r *BigQueryWarehouseResource) UpgradeState(ctx context.Context) map[int64]
398436
var priorStateData BigQueryWarehouseResourceModelV0
399437
resp.Diagnostics.Append(req.State.Get(ctx, &priorStateData)...)
400438
if !resp.Diagnostics.HasError() {
401-
upgradedStateData := BigQueryWarehouseResourceModel{
439+
upgradedStateData := BigQueryWarehouseResourceModelV1{
402440
Uuid: priorStateData.Uuid,
403441
ConnectionUuid: priorStateData.ConnectionUuid,
404442
CollectorUuid: priorStateData.DataCollectorUuid,
@@ -410,5 +448,62 @@ func (r *BigQueryWarehouseResource) UpgradeState(ctx context.Context) map[int64]
410448
}
411449
},
412450
},
451+
1: {
452+
PriorSchema: &schema.Schema{
453+
Attributes: map[string]schema.Attribute{
454+
"uuid": schema.StringAttribute{
455+
Computed: true,
456+
Optional: false,
457+
PlanModifiers: []planmodifier.String{
458+
stringplanmodifier.UseStateForUnknown(),
459+
},
460+
},
461+
"connection_uuid": schema.StringAttribute{
462+
Computed: true,
463+
Optional: false,
464+
PlanModifiers: []planmodifier.String{
465+
stringplanmodifier.UseStateForUnknown(),
466+
},
467+
},
468+
"name": schema.StringAttribute{
469+
Required: true,
470+
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
471+
},
472+
"collector_uuid": schema.StringAttribute{
473+
Required: true,
474+
PlanModifiers: []planmodifier.String{
475+
stringplanmodifier.RequiresReplaceIfConfigured(),
476+
},
477+
},
478+
"service_account_key": schema.StringAttribute{
479+
Required: true,
480+
Sensitive: true,
481+
},
482+
"deletion_protection": schema.BoolAttribute{
483+
Optional: true,
484+
Computed: true,
485+
Default: booldefault.StaticBool(true),
486+
},
487+
},
488+
},
489+
StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
490+
var priorStateData BigQueryWarehouseResourceModelV1
491+
resp.Diagnostics.Append(req.State.Get(ctx, &priorStateData)...)
492+
if !resp.Diagnostics.HasError() {
493+
upgradedStateData := BigQueryWarehouseResourceModel{
494+
Uuid: priorStateData.Uuid,
495+
CollectorUuid: priorStateData.CollectorUuid,
496+
Name: priorStateData.Name,
497+
DeletionProtection: priorStateData.DeletionProtection,
498+
Credentials: Credentials{
499+
ConnectionUuid: priorStateData.ConnectionUuid,
500+
ServiceAccountKey: priorStateData.ServiceAccountKey,
501+
UpdatedAt: types.StringNull(),
502+
},
503+
}
504+
resp.Diagnostics.Append(resp.State.Set(ctx, upgradedStateData)...)
505+
}
506+
},
507+
},
413508
}
414509
}

internal/warehouse/bigquery_warehouse_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func TestAccBigQueryWarehouseResource(t *testing.T) {
3737
Check: resource.ComposeAggregateTestCheckFunc(
3838
resource.TestCheckResourceAttr("montecarlo_bigquery_warehouse.test", "name", "test-warehouse"),
3939
resource.TestCheckResourceAttr("montecarlo_bigquery_warehouse.test", "collector_uuid", collectorUuid),
40-
resource.TestCheckResourceAttr("montecarlo_bigquery_warehouse.test", "service_account_key", serviceAccount),
40+
resource.TestCheckResourceAttr("montecarlo_bigquery_warehouse.test", "credentials.service_account_key", serviceAccount),
4141
resource.TestCheckResourceAttr("montecarlo_bigquery_warehouse.test", "deletion_protection", "false"),
4242
),
4343
},
@@ -53,11 +53,11 @@ func TestAccBigQueryWarehouseResource(t *testing.T) {
5353
ImportStateVerify: true,
5454
ImportStateIdFunc: func(s *terraform.State) (string, error) {
5555
uuid := s.RootModule().Resources["montecarlo_bigquery_warehouse.test"].Primary.Attributes["uuid"]
56-
connectionUuid := s.RootModule().Resources["montecarlo_bigquery_warehouse.test"].Primary.Attributes["connection_uuid"]
56+
connectionUuid := s.RootModule().Resources["montecarlo_bigquery_warehouse.test"].Primary.Attributes["credentials.connection_uuid"]
5757
return fmt.Sprintf("%[1]s,%[2]s,%[3]s", uuid, connectionUuid, collectorUuid), nil
5858
},
5959
ImportStateVerifyIdentifierAttribute: "uuid",
60-
ImportStateVerifyIgnore: []string{"deletion_protection", "service_account_key"},
60+
ImportStateVerifyIgnore: []string{"deletion_protection", "credentials.service_account_key"},
6161
},
6262
{ // Update and Read testing
6363
ProtoV6ProviderFactories: acctest.TestAccProviderFactories,
@@ -70,7 +70,7 @@ func TestAccBigQueryWarehouseResource(t *testing.T) {
7070
Check: resource.ComposeAggregateTestCheckFunc(
7171
resource.TestCheckResourceAttr("montecarlo_bigquery_warehouse.test", "name", "test-warehouse-updated"),
7272
resource.TestCheckResourceAttr("montecarlo_bigquery_warehouse.test", "collector_uuid", collectorUuid),
73-
resource.TestCheckResourceAttr("montecarlo_bigquery_warehouse.test", "service_account_key", serviceAccount),
73+
resource.TestCheckResourceAttr("montecarlo_bigquery_warehouse.test", "credentials.service_account_key", serviceAccount),
7474
resource.TestCheckResourceAttr("montecarlo_bigquery_warehouse.test", "deletion_protection", "false"),
7575
),
7676
},

internal/warehouse/testdata/TestAccBigQueryWarehouseResource/create.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ variable "bq_service_account" {
2020
resource "montecarlo_bigquery_warehouse" "test" {
2121
name = "test-warehouse"
2222
collector_uuid = "a08d23fc-00a0-4c36-b568-82e9d0e67ad8"
23-
service_account_key = var.bq_service_account
23+
credentials = { service_account_key = var.bq_service_account }
2424
deletion_protection = false
2525
}

internal/warehouse/testdata/TestAccBigQueryWarehouseResource/update.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ variable "bq_service_account" {
2020
resource "montecarlo_bigquery_warehouse" "test" {
2121
name = "test-warehouse-updated"
2222
collector_uuid = "a08d23fc-00a0-4c36-b568-82e9d0e67ad8"
23-
service_account_key = var.bq_service_account
23+
credentials = { service_account_key = var.bq_service_account }
2424
deletion_protection = false
2525
}

0 commit comments

Comments
 (0)