ec/ecresource/deploymentresource/apm/v2/apm_payload.go (114 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package v2
import (
"context"
"github.com/elastic/cloud-sdk-go/pkg/models"
"github.com/elastic/cloud-sdk-go/pkg/util/ec"
v1 "github.com/elastic/terraform-provider-ec/ec/ecresource/deploymentresource/apm/v1"
topologyv1 "github.com/elastic/terraform-provider-ec/ec/ecresource/deploymentresource/topology/v1"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"
)
type ApmTF struct {
ElasticsearchClusterRefId types.String `tfsdk:"elasticsearch_cluster_ref_id"`
RefId types.String `tfsdk:"ref_id"`
ResourceId types.String `tfsdk:"resource_id"`
Region types.String `tfsdk:"region"`
HttpEndpoint types.String `tfsdk:"http_endpoint"`
HttpsEndpoint types.String `tfsdk:"https_endpoint"`
InstanceConfigurationId types.String `tfsdk:"instance_configuration_id"`
LatestInstanceConfigurationId types.String `tfsdk:"latest_instance_configuration_id"`
InstanceConfigurationVersion types.Int64 `tfsdk:"instance_configuration_version"`
LatestInstanceConfigurationVersion types.Int64 `tfsdk:"latest_instance_configuration_version"`
Size types.String `tfsdk:"size"`
SizeResource types.String `tfsdk:"size_resource"`
ZoneCount types.Int64 `tfsdk:"zone_count"`
Config types.Object `tfsdk:"config"`
}
func (apm ApmTF) payload(ctx context.Context, payload models.ApmPayload) (*models.ApmPayload, diag.Diagnostics) {
var diags diag.Diagnostics
if !apm.ElasticsearchClusterRefId.IsNull() {
payload.ElasticsearchClusterRefID = ec.String(apm.ElasticsearchClusterRefId.ValueString())
}
if !apm.RefId.IsNull() {
payload.RefID = ec.String(apm.RefId.ValueString())
}
if apm.Region.ValueString() != "" {
payload.Region = ec.String(apm.Region.ValueString())
}
if !apm.Config.IsNull() && !apm.Config.IsUnknown() {
var cfg v1.ApmConfigTF
ds := tfsdk.ValueAs(ctx, apm.Config, &cfg)
diags.Append(ds...)
if !ds.HasError() {
diags.Append(apmConfigPayload(ctx, cfg, payload.Plan.Apm)...)
}
}
topology := topologyv1.TopologyTF{
InstanceConfigurationId: apm.InstanceConfigurationId,
InstanceConfigurationVersion: apm.InstanceConfigurationVersion,
Size: apm.Size,
SizeResource: apm.SizeResource,
ZoneCount: apm.ZoneCount,
}
// Always use the first topology element - discard any other topology elements
topologyPayload, ds := apmTopologyPayload(ctx, topology, defaultApmTopology(payload.Plan.ClusterTopology)[0])
diags.Append(ds...)
if !ds.HasError() && topologyPayload != nil {
payload.Plan.ClusterTopology = []*models.ApmTopologyElement{topologyPayload}
}
return &payload, diags
}
func ApmPayload(ctx context.Context, apmObj types.Object, updateResources *models.DeploymentUpdateResources) (*models.ApmPayload, diag.Diagnostics) {
apm, diags := objectToApm(ctx, apmObj)
if diags.HasError() {
return nil, diags
}
if apm == nil {
return nil, nil
}
templatePayload := payloadFromUpdate(updateResources)
if templatePayload == nil {
diags.AddError("apm payload error", "apm specified but deployment template is not configured for it. Use a different template if you wish to add apm")
return nil, diags
}
payload, diags := apm.payload(ctx, *templatePayload)
if diags.HasError() {
return nil, diags
}
return payload, nil
}
func objectToApm(ctx context.Context, plan types.Object) (*ApmTF, diag.Diagnostics) {
var apm *ApmTF
if plan.IsNull() || plan.IsUnknown() {
return nil, nil
}
if diags := tfsdk.ValueAs(ctx, plan, &apm); diags.HasError() {
return nil, diags
}
return apm, nil
}
func CheckAvailableMigration(ctx context.Context, plan types.Object, state types.Object) (bool, diag.Diagnostics) {
apmPlan, diags := objectToApm(ctx, plan)
if diags.HasError() {
return false, diags
}
apmState, diags := objectToApm(ctx, state)
if diags.HasError() {
return false, diags
}
if apmPlan == nil || apmState == nil {
return false, nil
}
// We won't migrate this topology element if 'instance_configuration_id' or 'instance_configuration_version' are
// defined on the TF configuration. Otherwise, we may be setting an incorrect value for 'size', in case the
// template IC has different size increments
if !apmPlan.InstanceConfigurationId.IsUnknown() || !apmPlan.InstanceConfigurationVersion.IsUnknown() {
return false, nil
}
instanceConfigIdsDiff := apmState.InstanceConfigurationId != apmState.LatestInstanceConfigurationId
instanceConfigVersionsDiff := apmState.InstanceConfigurationVersion != apmState.LatestInstanceConfigurationVersion
// We consider that a migration is available when:
// * the current instance config ID doesn't match the one in the template
// * the instance config IDs match but the instance config versions differ
return instanceConfigIdsDiff || instanceConfigVersionsDiff, nil
}
// payloadFromUpdate returns the ApmPayload from a deployment
// template or an empty version of the payload.
func payloadFromUpdate(updateResources *models.DeploymentUpdateResources) *models.ApmPayload {
if updateResources == nil || len(updateResources.Apm) == 0 {
return nil
}
return updateResources.Apm[0]
}