Skip to content

Commit c62d386

Browse files
authored
Don't populate settings during read (#779)
* Don't populate settings during read There's a fair bit of complexity around handling computed settings. I'll look at adding this back in a follow up PR, but want to get this resource in a working state so we can make a new release. This covers the case where settings are applied as part of an index template * Remove other Default attributes
1 parent 5874203 commit c62d386

File tree

6 files changed

+173
-170
lines changed

6 files changed

+173
-170
lines changed

internal/clients/elasticsearch/index.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,11 @@ func GetIndex(ctx context.Context, apiClient *clients.ApiClient, name string) (*
309309
return nil, diags
310310
}
311311

312-
index := indices[name]
313-
return &index, diags
312+
if index, ok := indices[name]; ok {
313+
return &index, nil
314+
}
315+
316+
return nil, nil
314317
}
315318

316319
func GetIndices(ctx context.Context, apiClient *clients.ApiClient, name string) (map[string]models.Index, fwdiags.Diagnostics) {

internal/elasticsearch/index/index/acc_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,25 @@ func TestAccResourceIndexSettings(t *testing.T) {
225225
})
226226
}
227227

228+
func TestAccResourceIndexWithTemplate(t *testing.T) {
229+
indexName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum)
230+
231+
resource.Test(t, resource.TestCase{
232+
PreCheck: func() { acctest.PreCheck(t) },
233+
CheckDestroy: checkResourceIndexDestroy,
234+
ProtoV6ProviderFactories: acctest.Providers,
235+
Steps: []resource.TestStep{
236+
{
237+
Config: testAccResourceIndexWithTemplate(indexName),
238+
Check: resource.ComposeTestCheckFunc(
239+
resource.TestCheckResourceAttr("elasticstack_elasticsearch_index.test", "name", indexName),
240+
resource.TestCheckNoResourceAttr("elasticstack_elasticsearch_index.test", "default_pipeline"),
241+
),
242+
},
243+
},
244+
})
245+
}
246+
228247
func TestAccResourceIndexRemovingField(t *testing.T) {
229248
indexName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum)
230249

@@ -463,6 +482,38 @@ resource "elasticstack_elasticsearch_index" "test_settings_removing_field" {
463482
`, name)
464483
}
465484

485+
func testAccResourceIndexWithTemplate(name string) string {
486+
return fmt.Sprintf(`
487+
provider "elasticstack" {
488+
elasticsearch {}
489+
}
490+
491+
resource "elasticstack_elasticsearch_index_template" "test" {
492+
name = "%s"
493+
index_patterns = ["%s"]
494+
template {
495+
settings = jsonencode({
496+
default_pipeline = ".fleet_final_pipeline-1"
497+
lifecycle = { name = ".monitoring-8-ilm-policy" }
498+
})
499+
}
500+
}
501+
502+
resource "elasticstack_elasticsearch_index" "test" {
503+
name = "%s"
504+
deletion_protection = false
505+
alias {
506+
name = "%s-alias"
507+
is_write_index = true
508+
}
509+
lifecycle {
510+
ignore_changes = [mappings]
511+
}
512+
depends_on = [elasticstack_elasticsearch_index_template.test]
513+
}
514+
`, name, name, name, name)
515+
}
516+
466517
func checkResourceIndexDestroy(s *terraform.State) error {
467518
client, err := clients.NewAcceptanceTestingClient()
468519
if err != nil {

internal/elasticsearch/index/index/models.go

Lines changed: 0 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"encoding/json"
66
"fmt"
77
"reflect"
8-
"strconv"
98
"strings"
109

1110
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
@@ -223,141 +222,6 @@ func aliasesFromAPI(ctx context.Context, apiModel models.Index) (basetypes.SetVa
223222
}
224223

225224
func setSettingsFromAPI(ctx context.Context, model *tfModel, apiModel models.Index) diag.Diagnostics {
226-
modelType := reflect.TypeOf(*model)
227-
228-
for _, key := range dynamicSettingsKeys {
229-
settingsValue, ok := apiModel.Settings["index."+key]
230-
var tfValue attr.Value
231-
if !ok {
232-
continue
233-
}
234-
235-
tfFieldKey := convertSettingsKeyToTFFieldKey(key)
236-
value, ok := model.getFieldValueByTagValue(tfFieldKey, modelType)
237-
if !ok {
238-
return diag.Diagnostics{
239-
diag.NewErrorDiagnostic(
240-
"failed to find setting value",
241-
fmt.Sprintf("expected setting with key %s", tfFieldKey),
242-
),
243-
}
244-
}
245-
246-
switch a := value.(type) {
247-
case types.String:
248-
settingStr, ok := settingsValue.(string)
249-
if !ok {
250-
return diag.Diagnostics{
251-
diag.NewErrorDiagnostic(
252-
"failed to convert setting to string",
253-
fmt.Sprintf("expected setting to be a string but got %t", settingsValue),
254-
)}
255-
}
256-
tfValue = basetypes.NewStringValue(settingStr)
257-
case types.Bool:
258-
settingBool, ok := settingsValue.(bool)
259-
if !ok {
260-
return diag.Diagnostics{
261-
diag.NewErrorDiagnostic(
262-
"failed to convert setting to bool",
263-
fmt.Sprintf("expected setting to be a bool but got %t", settingsValue),
264-
)}
265-
}
266-
tfValue = basetypes.NewBoolValue(settingBool)
267-
case types.Int64:
268-
if settingStr, ok := settingsValue.(string); ok {
269-
settingInt, err := strconv.Atoi(settingStr)
270-
if err != nil {
271-
return diag.Diagnostics{
272-
diag.NewErrorDiagnostic(
273-
"failed to convert setting to int",
274-
fmt.Sprintf("expected setting to be an int but it was a string. Attempted to parse it but got %s", err.Error()),
275-
),
276-
}
277-
}
278-
279-
settingsValue = int64(settingInt)
280-
}
281-
282-
settingInt, ok := settingsValue.(int64)
283-
if !ok {
284-
return diag.Diagnostics{
285-
diag.NewErrorDiagnostic(
286-
"failed to convert setting to int",
287-
fmt.Sprintf("expected setting to be a int but got %t", settingsValue),
288-
)}
289-
}
290-
tfValue = basetypes.NewInt64Value(settingInt)
291-
case types.List:
292-
elemType := a.ElementType(ctx)
293-
if elemType != types.StringType {
294-
return diag.Diagnostics{
295-
diag.NewErrorDiagnostic(
296-
"expected list of string",
297-
fmt.Sprintf("expected list element type to be string but got %s", elemType),
298-
),
299-
}
300-
}
301-
302-
elems, ok := settingsValue.([]interface{})
303-
if !ok {
304-
return diag.Diagnostics{
305-
diag.NewErrorDiagnostic(
306-
"failed to convert setting to []string",
307-
fmt.Sprintf("expected setting to be a []string but got %#v", settingsValue),
308-
)}
309-
}
310-
311-
var diags diag.Diagnostics
312-
tfValue, diags = basetypes.NewListValueFrom(ctx, basetypes.StringType{}, elems)
313-
if diags.HasError() {
314-
return diags
315-
}
316-
case types.Set:
317-
elemType := a.ElementType(ctx)
318-
if elemType != types.StringType {
319-
return diag.Diagnostics{
320-
diag.NewErrorDiagnostic(
321-
"expected set of string",
322-
fmt.Sprintf("expected set element type to be string but got %s", elemType),
323-
),
324-
}
325-
}
326-
327-
elems, ok := settingsValue.([]interface{})
328-
if !ok {
329-
return diag.Diagnostics{
330-
diag.NewErrorDiagnostic(
331-
"failed to convert setting to []string",
332-
fmt.Sprintf("expected setting to be a thing []string but got %#v", settingsValue),
333-
)}
334-
}
335-
336-
var diags diag.Diagnostics
337-
tfValue, diags = basetypes.NewSetValueFrom(ctx, basetypes.StringType{}, elems)
338-
if diags.HasError() {
339-
return diags
340-
}
341-
default:
342-
return diag.Diagnostics{
343-
diag.NewErrorDiagnostic(
344-
"unknown value type",
345-
fmt.Sprintf("unknown index setting value type %s", a.Type(ctx)),
346-
),
347-
}
348-
}
349-
350-
ok = model.setFieldValueByTagValue(tfFieldKey, modelType, tfValue)
351-
if !ok {
352-
return diag.Diagnostics{
353-
diag.NewErrorDiagnostic(
354-
"failed to find setting value",
355-
fmt.Sprintf("expected setting with key %s", tfFieldKey),
356-
),
357-
}
358-
}
359-
}
360-
361225
settingsBytes, err := json.Marshal(apiModel.Settings)
362226
if err != nil {
363227
return diag.Diagnostics{
@@ -569,19 +433,6 @@ func (model tfModel) toIndexSettings(ctx context.Context) (map[string]interface{
569433
return settings, nil
570434
}
571435

572-
func (model *tfModel) setFieldValueByTagValue(tagName string, t reflect.Type, value attr.Value) bool {
573-
numField := t.NumField()
574-
for i := 0; i < numField; i++ {
575-
field := t.Field(i)
576-
if field.Tag.Get("tfsdk") == tagName {
577-
reflect.ValueOf(model).Elem().Field(i).Set(reflect.ValueOf(value))
578-
return true
579-
}
580-
}
581-
582-
return false
583-
}
584-
585436
func (model tfModel) getFieldValueByTagValue(tagName string, t reflect.Type) (attr.Value, bool) {
586437
numField := t.NumField()
587438
for i := 0; i < numField; i++ {

internal/elasticsearch/index/index/schema.go

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,19 @@ import (
77
"github.com/elastic/terraform-provider-elasticstack/internal/elasticsearch/index"
88
providerschema "github.com/elastic/terraform-provider-elasticstack/internal/schema"
99
"github.com/elastic/terraform-provider-elasticstack/internal/utils/customtypes"
10+
"github.com/elastic/terraform-provider-elasticstack/internal/utils/planmodifiers"
1011
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
1112
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
1213
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
1314
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
1415
"github.com/hashicorp/terraform-plugin-framework/attr"
1516
"github.com/hashicorp/terraform-plugin-framework/resource"
1617
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
17-
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
1818
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
1919
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
2020
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
2121
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
2222
"github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier"
23-
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
2423
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
2524
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
2625
"github.com/hashicorp/terraform-plugin-framework/types"
@@ -52,31 +51,46 @@ func getSchema() schema.Schema {
5251
Description: "Value used to route indexing operations to a specific shard. If specified, this overwrites the `routing` value for indexing operations.",
5352
Optional: true,
5453
Computed: true,
55-
Default: stringdefault.StaticString(""),
54+
PlanModifiers: []planmodifier.String{
55+
stringplanmodifier.UseStateForUnknown(),
56+
planmodifiers.StringUseDefaultIfUnknown(""),
57+
},
5658
},
5759
"is_hidden": schema.BoolAttribute{
5860
Description: "If true, the alias is hidden.",
5961
Optional: true,
6062
Computed: true,
61-
Default: booldefault.StaticBool(false),
63+
PlanModifiers: []planmodifier.Bool{
64+
boolplanmodifier.UseStateForUnknown(),
65+
planmodifiers.BoolUseDefaultIfUnknown(false),
66+
},
6267
},
6368
"is_write_index": schema.BoolAttribute{
6469
Description: "If true, the index is the write index for the alias.",
6570
Optional: true,
6671
Computed: true,
67-
Default: booldefault.StaticBool(false),
72+
PlanModifiers: []planmodifier.Bool{
73+
boolplanmodifier.UseStateForUnknown(),
74+
planmodifiers.BoolUseDefaultIfUnknown(false),
75+
},
6876
},
6977
"routing": schema.StringAttribute{
7078
Description: "Value used to route indexing and search operations to a specific shard.",
7179
Optional: true,
7280
Computed: true,
73-
Default: stringdefault.StaticString(""),
81+
PlanModifiers: []planmodifier.String{
82+
stringplanmodifier.UseStateForUnknown(),
83+
planmodifiers.StringUseDefaultIfUnknown(""),
84+
},
7485
},
7586
"search_routing": schema.StringAttribute{
7687
Description: "Value used to route search operations to a specific shard. If specified, this overwrites the routing value for search operations.",
7788
Optional: true,
7889
Computed: true,
79-
Default: stringdefault.StaticString(""),
90+
PlanModifiers: []planmodifier.String{
91+
stringplanmodifier.UseStateForUnknown(),
92+
planmodifiers.StringUseDefaultIfUnknown(""),
93+
},
8094
},
8195
},
8296
},
@@ -210,10 +224,6 @@ func getSchema() schema.Schema {
210224
"number_of_replicas": schema.Int64Attribute{
211225
Description: "Number of shard replicas.",
212226
Optional: true,
213-
Computed: true,
214-
PlanModifiers: []planmodifier.Int64{
215-
int64planmodifier.UseStateForUnknown(),
216-
},
217227
},
218228
"auto_expand_replicas": schema.StringAttribute{
219229
Description: "Set the number of replicas to the node count in the cluster. Set to a dash delimited lower and upper bound (e.g. 0-5) or use all for the upper bound (e.g. 0-all)",
@@ -451,8 +461,8 @@ func getSchema() schema.Schema {
451461
Validators: []validator.String{
452462
index.StringIsJSONObject{},
453463
},
454-
Default: stringdefault.StaticString("{}"),
455464
PlanModifiers: []planmodifier.String{
465+
planmodifiers.StringUseDefaultIfUnknown("{}"),
456466
mappingsPlanModifier{},
457467
},
458468
},
@@ -465,34 +475,44 @@ func getSchema() schema.Schema {
465475
"deletion_protection": schema.BoolAttribute{
466476
Optional: true,
467477
Computed: true,
468-
Default: booldefault.StaticBool(true),
469478
Description: "Whether to allow Terraform to destroy the index. Unless this field is set to false in Terraform state, a terraform destroy or terraform apply command that deletes the instance will fail.",
479+
PlanModifiers: []planmodifier.Bool{
480+
planmodifiers.BoolUseDefaultIfUnknown(true),
481+
},
470482
},
471483
"include_type_name": schema.BoolAttribute{
472484
Description: "If true, a mapping type is expected in the body of mappings. Defaults to false. Supported for Elasticsearch 7.x.",
473485
Optional: true,
474486
Computed: true,
475-
Default: booldefault.StaticBool(false),
487+
PlanModifiers: []planmodifier.Bool{
488+
planmodifiers.BoolUseDefaultIfUnknown(false),
489+
},
476490
},
477491
"wait_for_active_shards": schema.StringAttribute{
478492
Description: "The number of shard copies that must be active before proceeding with the operation. Set to `all` or any positive integer up to the total number of shards in the index (number_of_replicas+1). Default: `1`, the primary shard. This value is ignored when running against Serverless projects.",
479493
Optional: true,
480494
Computed: true,
481-
Default: stringdefault.StaticString("1"),
495+
PlanModifiers: []planmodifier.String{
496+
planmodifiers.StringUseDefaultIfUnknown("1"),
497+
},
482498
},
483499
"master_timeout": schema.StringAttribute{
484500
Description: "Period to wait for a connection to the master node. If no response is received before the timeout expires, the request fails and returns an error. Defaults to `30s`. This value is ignored when running against Serverless projects.",
485501
Optional: true,
486502
Computed: true,
487-
Default: stringdefault.StaticString("30s"),
488-
CustomType: customtypes.DurationType{},
503+
PlanModifiers: []planmodifier.String{
504+
planmodifiers.StringUseDefaultIfUnknown("30s"),
505+
},
506+
CustomType: customtypes.DurationType{},
489507
},
490508
"timeout": schema.StringAttribute{
491509
Description: "Period to wait for a response. If no response is received before the timeout expires, the request fails and returns an error. Defaults to `30s`.",
492510
Optional: true,
493511
Computed: true,
494-
Default: stringdefault.StaticString("30s"),
495-
CustomType: customtypes.DurationType{},
512+
PlanModifiers: []planmodifier.String{
513+
planmodifiers.StringUseDefaultIfUnknown("30s"),
514+
},
515+
CustomType: customtypes.DurationType{},
496516
},
497517
},
498518
}

0 commit comments

Comments
 (0)