Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ alias = [

### Changes

- Fix an issue where the `elasticstack_fleet_output` resource would error due to inconsistent state after an ouptut was edited in the Kibana UI ([#1506](https://github.com/elastic/terraform-provider-elasticstack/pull/1506))
- Allow `index` and `data_view_id` values to both be unknown during planning in `elasticstack_kibana_security_detection_rule` ([#1499](https://github.com/elastic/terraform-provider-elasticstack/pull/1499))
- Support `.bedrock` and `.gen-ai` connectors ([#1467](https://github.com/elastic/terraform-provider-elasticstack/pull/1467))
- Support the `solution` attribute in `elasticstack_kibana_space` from 8.16 ([#1486](https://github.com/elastic/terraform-provider-elasticstack/pull/1486))
Expand Down
17 changes: 6 additions & 11 deletions internal/elasticsearch/index/alias/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"reflect"

"github.com/elastic/terraform-provider-elasticstack/internal/models"
"github.com/elastic/terraform-provider-elasticstack/internal/utils/typeutils"
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand Down Expand Up @@ -95,19 +96,13 @@ func (model *tfModel) populateFromAPI(ctx context.Context, aliasName string, ind
// indexFromAlias converts a models.IndexAlias to an indexModel
func indexFromAlias(indexName string, aliasData models.IndexAlias) (indexModel, diag.Diagnostics) {
index := indexModel{
Name: types.StringValue(indexName),
IsHidden: types.BoolValue(aliasData.IsHidden),
Name: types.StringValue(indexName),
IsHidden: types.BoolValue(aliasData.IsHidden),
IndexRouting: typeutils.NonEmptyStringishValue(aliasData.IndexRouting),
Routing: typeutils.NonEmptyStringishValue(aliasData.Routing),
SearchRouting: typeutils.NonEmptyStringishValue(aliasData.SearchRouting),
}

if aliasData.IndexRouting != "" {
index.IndexRouting = types.StringValue(aliasData.IndexRouting)
}
if aliasData.Routing != "" {
index.Routing = types.StringValue(aliasData.Routing)
}
if aliasData.SearchRouting != "" {
index.SearchRouting = types.StringValue(aliasData.SearchRouting)
}
if aliasData.Filter != nil {
filterBytes, err := json.Marshal(aliasData.Filter)
if err != nil {
Expand Down
101 changes: 17 additions & 84 deletions internal/elasticsearch/ml/anomaly_detection_job/models_tf.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/elastic/terraform-provider-elasticstack/internal/utils/customtypes"
"github.com/elastic/terraform-provider-elasticstack/internal/utils/typeutils"
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
fwdiags "github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand Down Expand Up @@ -300,10 +301,7 @@ func (tfModel *AnomalyDetectionJobTFModel) fromAPIModel(ctx context.Context, api

// Convert optional fields
tfModel.AllowLazyOpen = types.BoolPointerValue(apiModel.AllowLazyOpen)

if apiModel.BackgroundPersistInterval != "" {
tfModel.BackgroundPersistInterval = types.StringValue(apiModel.BackgroundPersistInterval)
}
tfModel.BackgroundPersistInterval = typeutils.NonEmptyStringishValue(apiModel.BackgroundPersistInterval)

if apiModel.CustomSettings != nil {
customSettingsJSON, err := json.Marshal(apiModel.CustomSettings)
Expand All @@ -324,10 +322,7 @@ func (tfModel *AnomalyDetectionJobTFModel) fromAPIModel(ctx context.Context, api
tfModel.RenormalizationWindowDays = types.Int64Value(*apiModel.RenormalizationWindowDays)
}

if apiModel.ResultsIndexName != "" {
tfModel.ResultsIndexName = types.StringValue(apiModel.ResultsIndexName)
}

tfModel.ResultsIndexName = typeutils.NonEmptyStringishValue(apiModel.ResultsIndexName)
tfModel.ResultsRetentionDays = types.Int64PointerValue(apiModel.ResultsRetentionDays)

// Convert analysis_config
Expand Down Expand Up @@ -363,29 +358,10 @@ func (tfModel *AnomalyDetectionJobTFModel) convertAnalysisConfigFromAPI(ctx cont
}

// Convert optional string fields
if apiConfig.CategorizationFieldName != "" {
analysisConfigTF.CategorizationFieldName = types.StringValue(apiConfig.CategorizationFieldName)
} else {
analysisConfigTF.CategorizationFieldName = types.StringNull()
}

if apiConfig.Latency != "" {
analysisConfigTF.Latency = types.StringValue(apiConfig.Latency)
} else {
analysisConfigTF.Latency = types.StringNull()
}

if apiConfig.ModelPruneWindow != "" {
analysisConfigTF.ModelPruneWindow = types.StringValue(apiConfig.ModelPruneWindow)
} else {
analysisConfigTF.ModelPruneWindow = types.StringNull()
}

if apiConfig.SummaryCountFieldName != "" {
analysisConfigTF.SummaryCountFieldName = types.StringValue(apiConfig.SummaryCountFieldName)
} else {
analysisConfigTF.SummaryCountFieldName = types.StringNull()
}
analysisConfigTF.CategorizationFieldName = typeutils.NonEmptyStringishValue(apiConfig.CategorizationFieldName)
analysisConfigTF.Latency = typeutils.NonEmptyStringishValue(apiConfig.Latency)
analysisConfigTF.ModelPruneWindow = typeutils.NonEmptyStringishValue(apiConfig.ModelPruneWindow)
analysisConfigTF.SummaryCountFieldName = typeutils.NonEmptyStringishValue(apiConfig.SummaryCountFieldName)

// Convert boolean fields
analysisConfigTF.MultivariateByFields = types.BoolPointerValue(apiConfig.MultivariateByFields)
Expand Down Expand Up @@ -417,41 +393,12 @@ func (tfModel *AnomalyDetectionJobTFModel) convertAnalysisConfigFromAPI(ctx cont
}

// Convert optional string fields
if detector.FieldName != "" {
detectorsTF[i].FieldName = types.StringValue(detector.FieldName)
} else {
detectorsTF[i].FieldName = types.StringNull()
}

if detector.ByFieldName != "" {
detectorsTF[i].ByFieldName = types.StringValue(detector.ByFieldName)
} else {
detectorsTF[i].ByFieldName = types.StringNull()
}

if detector.OverFieldName != "" {
detectorsTF[i].OverFieldName = types.StringValue(detector.OverFieldName)
} else {
detectorsTF[i].OverFieldName = types.StringNull()
}

if detector.PartitionFieldName != "" {
detectorsTF[i].PartitionFieldName = types.StringValue(detector.PartitionFieldName)
} else {
detectorsTF[i].PartitionFieldName = types.StringNull()
}

if detector.DetectorDescription != "" {
detectorsTF[i].DetectorDescription = types.StringValue(detector.DetectorDescription)
} else {
detectorsTF[i].DetectorDescription = types.StringNull()
}

if detector.ExcludeFrequent != "" {
detectorsTF[i].ExcludeFrequent = types.StringValue(detector.ExcludeFrequent)
} else {
detectorsTF[i].ExcludeFrequent = types.StringNull()
}
detectorsTF[i].FieldName = typeutils.NonEmptyStringishValue(detector.FieldName)
detectorsTF[i].ByFieldName = typeutils.NonEmptyStringishValue(detector.ByFieldName)
detectorsTF[i].OverFieldName = typeutils.NonEmptyStringishValue(detector.OverFieldName)
detectorsTF[i].PartitionFieldName = typeutils.NonEmptyStringishValue(detector.PartitionFieldName)
detectorsTF[i].DetectorDescription = typeutils.NonEmptyStringishValue(detector.DetectorDescription)
detectorsTF[i].ExcludeFrequent = typeutils.NonEmptyStringishValue(detector.ExcludeFrequent)

// Convert boolean field
detectorsTF[i].UseNull = types.BoolPointerValue(detector.UseNull)
Expand Down Expand Up @@ -530,18 +477,9 @@ func (tfModel *AnomalyDetectionJobTFModel) convertDataDescriptionFromAPI(ctx con
return types.ObjectNull(getDataDescriptionAttrTypes())
}

dataDescriptionTF := DataDescriptionTFModel{}

if apiDataDescription.TimeField != "" {
dataDescriptionTF.TimeField = types.StringValue(apiDataDescription.TimeField)
} else {
dataDescriptionTF.TimeField = types.StringNull()
}

if apiDataDescription.TimeFormat != "" {
dataDescriptionTF.TimeFormat = types.StringValue(apiDataDescription.TimeFormat)
} else {
dataDescriptionTF.TimeFormat = types.StringNull()
dataDescriptionTF := DataDescriptionTFModel{
TimeField: typeutils.NonEmptyStringishValue(apiDataDescription.TimeField),
TimeFormat: typeutils.NonEmptyStringishValue(apiDataDescription.TimeFormat),
}

dataDescriptionObjectValue, d := types.ObjectValueFrom(ctx, getDataDescriptionAttrTypes(), dataDescriptionTF)
Expand Down Expand Up @@ -576,12 +514,7 @@ func (tfModel *AnomalyDetectionJobTFModel) convertModelPlotConfigFromAPI(ctx con

modelPlotConfigTF := ModelPlotConfigTFModel{
Enabled: types.BoolValue(apiModelPlotConfig.Enabled),
}

if apiModelPlotConfig.Terms != "" {
modelPlotConfigTF.Terms = types.StringValue(apiModelPlotConfig.Terms)
} else {
modelPlotConfigTF.Terms = types.StringNull()
Terms: typeutils.NonEmptyStringishValue(apiModelPlotConfig.Terms),
}

modelPlotConfigTF.AnnotationsEnabled = types.BoolPointerValue(apiModelPlotConfig.AnnotationsEnabled)
Expand Down
3 changes: 2 additions & 1 deletion internal/fleet/output/models_elasticsearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/elastic/terraform-provider-elasticstack/generated/kbapi"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/elastic/terraform-provider-elasticstack/internal/utils/typeutils"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand All @@ -17,7 +18,7 @@ func (model *outputModel) fromAPIElasticsearchModel(ctx context.Context, data *k
model.Type = types.StringValue(string(data.Type))
model.Hosts = utils.SliceToListType_String(ctx, data.Hosts, path.Root("hosts"), &diags)
model.CaSha256 = types.StringPointerValue(data.CaSha256)
model.CaTrustedFingerprint = types.StringPointerValue(data.CaTrustedFingerprint)
model.CaTrustedFingerprint = typeutils.NonEmptyStringishPointerValue(data.CaTrustedFingerprint)
model.DefaultIntegrations = types.BoolPointerValue(data.IsDefault)
model.DefaultMonitoring = types.BoolPointerValue(data.IsDefaultMonitoring)
model.ConfigYaml = types.StringPointerValue(data.ConfigYaml)
Expand Down
3 changes: 2 additions & 1 deletion internal/fleet/output/models_kafka.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/elastic/terraform-provider-elasticstack/generated/kbapi"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/elastic/terraform-provider-elasticstack/internal/utils/typeutils"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand Down Expand Up @@ -437,7 +438,7 @@ func (model *outputModel) fromAPIKafkaModel(ctx context.Context, data *kbapi.Out
model.Type = types.StringValue(string(data.Type))
model.Hosts = utils.SliceToListType_String(ctx, data.Hosts, path.Root("hosts"), &diags)
model.CaSha256 = types.StringPointerValue(data.CaSha256)
model.CaTrustedFingerprint = types.StringPointerValue(data.CaTrustedFingerprint)
model.CaTrustedFingerprint = typeutils.NonEmptyStringishPointerValue(data.CaTrustedFingerprint)
model.DefaultIntegrations = types.BoolPointerValue(data.IsDefault)
model.DefaultMonitoring = types.BoolPointerValue(data.IsDefaultMonitoring)
model.ConfigYaml = types.StringPointerValue(data.ConfigYaml)
Expand Down
3 changes: 2 additions & 1 deletion internal/fleet/output/models_logstash.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/elastic/terraform-provider-elasticstack/generated/kbapi"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/elastic/terraform-provider-elasticstack/internal/utils/typeutils"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand All @@ -17,7 +18,7 @@ func (model *outputModel) fromAPILogstashModel(ctx context.Context, data *kbapi.
model.Type = types.StringValue(string(data.Type))
model.Hosts = utils.SliceToListType_String(ctx, data.Hosts, path.Root("hosts"), &diags)
model.CaSha256 = types.StringPointerValue(data.CaSha256)
model.CaTrustedFingerprint = types.StringPointerValue(data.CaTrustedFingerprint)
model.CaTrustedFingerprint = typeutils.NonEmptyStringishPointerValue(data.CaTrustedFingerprint)
model.DefaultIntegrations = types.BoolPointerValue(data.IsDefault)
model.DefaultMonitoring = types.BoolPointerValue(data.IsDefaultMonitoring)
model.ConfigYaml = types.StringPointerValue(data.ConfigYaml)
Expand Down
18 changes: 14 additions & 4 deletions internal/fleet/output/models_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/elastic/terraform-provider-elasticstack/generated/kbapi"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/elastic/terraform-provider-elasticstack/internal/utils/typeutils"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand Down Expand Up @@ -57,12 +58,21 @@ func sslToObjectValue(ctx context.Context, ssl *kbapi.OutputSsl) (types.Object,
}

var diags diag.Diagnostics
p := path.Root("ssl")
sslModel := outputSslModel{
CertificateAuthorities: utils.SliceToListType_String(ctx, utils.Deref(ssl.CertificateAuthorities), p.AtName("certificate_authorities"), &diags),
Certificate: types.StringPointerValue(ssl.Certificate),
Key: types.StringPointerValue(ssl.Key),
Certificate: typeutils.NonEmptyStringishPointerValue(ssl.Certificate),
Key: typeutils.NonEmptyStringishPointerValue(ssl.Key),
}

if cas := utils.Deref(ssl.CertificateAuthorities); len(cas) > 0 {
sslModel.CertificateAuthorities = utils.SliceToListType_String(ctx, cas, path.Root("ssl").AtName("certificate_authorities"), &diags)
} else {
sslModel.CertificateAuthorities = types.ListNull(types.StringType)
}

if sslModel.CertificateAuthorities.IsNull() && sslModel.Certificate.IsNull() && sslModel.Key.IsNull() {
return types.ObjectNull(getSslAttrTypes()), nil
}

obj, diagTemp := types.ObjectValueFrom(ctx, getSslAttrTypes(), sslModel)
diags.Append(diagTemp...)
return obj, diags
Expand Down
22 changes: 22 additions & 0 deletions internal/fleet/output/models_ssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,28 @@ func Test_sslToObjectValue(t *testing.T) {
},
want: types.ObjectNull(getSslAttrTypes()),
},
{
name: "returns null object when ssl has all empty fields",
args: args{
ssl: &kbapi.OutputSsl{
Certificate: nil,
CertificateAuthorities: nil,
Key: nil,
},
},
want: types.ObjectNull(getSslAttrTypes()),
},
{
name: "returns null object when ssl has empty string pointers and empty slice",
args: args{
ssl: &kbapi.OutputSsl{
Certificate: utils.Pointer(""),
CertificateAuthorities: &[]string{},
Key: utils.Pointer(""),
},
},
want: types.ObjectNull(getSslAttrTypes()),
},
{
name: "returns an object when populated",
args: args{
Expand Down
9 changes: 9 additions & 0 deletions internal/fleet/output/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,24 @@ func getSchema() schema.Schema {
Description: "Server SSL certificate authorities.",
Optional: true,
ElementType: types.StringType,
Validators: []validator.List{
listvalidator.SizeAtLeast(1),
},
},
"certificate": schema.StringAttribute{
Description: "Client SSL certificate.",
Required: true,
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
"key": schema.StringAttribute{
Description: "Client SSL certificate key.",
Required: true,
Sensitive: true,
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,17 @@ func StringishPointerValue[T ~string](ptr *T) types.String {
func StringishValue[T ~string](value T) types.String {
return types.StringValue(string(value))
}

func NonEmptyStringishValue[T ~string](value T) types.String {
if value == "" {
return types.StringNull()
}
return types.StringValue(string(value))
}

func NonEmptyStringishPointerValue[T ~string](ptr *T) types.String {
if ptr == nil {
return types.StringNull()
}
return NonEmptyStringishValue(*ptr)
}