fix(observability): Some plans do not offer to configure alert configs or store metrics. (#964)

Signed-off-by: Alexander Dahmen <alexander.dahmen@inovex.de>
This commit is contained in:
Alexander Dahmen 2025-09-09 13:42:08 +02:00 committed by GitHub
parent bb09c8539d
commit 8f8124e32d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 278 additions and 233 deletions

View file

@ -400,18 +400,6 @@ func (d *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
return
}
metricsRetentionResp, err := d.client.GetMetricsStorageRetention(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get metrics retention: %v", err))
return
}
alertConfigResp, err := d.client.GetAlertConfigs(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get alert config: %v", err))
return
}
// Map response body to schema
err = mapFields(ctx, instanceResp, &model)
if err != nil {
@ -426,6 +414,12 @@ func (d *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
return
}
plan, err := loadPlanId(ctx, *d.client, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Loading service plan: %v", err))
return
}
err = mapACLField(aclListResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API response for the ACL: %v", err))
@ -439,31 +433,48 @@ func (d *instanceDataSource) Read(ctx context.Context, req datasource.ReadReques
return
}
// Map response body to schema
err = mapMetricsRetentionField(metricsRetentionResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API response for the metrics retention: %v", err))
return
// There are some plans which does not offer storage e.g. like Observability-Metrics-Endpoint-100k-EU01
if plan.GetLogsStorage() != 0 && plan.GetTracesStorage() != 0 {
metricsRetentionResp, err := d.client.GetMetricsStorageRetention(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get metrics retention: %v", err))
return
}
// Map response body to schema
err = mapMetricsRetentionField(metricsRetentionResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API response for the metrics retention: %v", err))
return
}
// Set state to fully populated data
diags := setMetricsRetentions(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
// Set state to fully populated data
diags = setMetricsRetentions(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
// There are plans where no alert matchers and receivers are present e.g. like Observability-Metrics-Endpoint-100k-EU01
if plan.GetAlertMatchers() != 0 && plan.GetAlertReceivers() != 0 {
alertConfigResp, err := d.client.GetAlertConfigs(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get alert config: %v", err))
return
}
// Map response body to schema
err = mapAlertConfigField(ctx, alertConfigResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the alert config: %v", err))
return
}
// Set state to fully populated data
diags = setAlertConfig(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
err = mapAlertConfigField(ctx, alertConfigResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the alert config: %v", err))
return
}
// Set state to fully populated data
diags = setAlertConfig(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "Observability instance read")
}

View file

@ -778,6 +778,52 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
}
}
// ModifyPlan will be called in the Plan phase.
// It will check if the plan contains a change that requires replacement. If yes, it will show an error to the user.
// Since there are observabiltiy plans which do not support specific configurations the request needs to be aborted with an error.
func (r *instanceResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
// If the plan is empty we are deleting the resource
if req.Plan.Raw.IsNull() {
return
}
var configModel Model
// skip initial empty configuration to avoid follow-up errors
if req.Config.Raw.IsNull() {
return
}
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
if resp.Diagnostics.HasError() {
return
}
plan, err := loadPlanId(ctx, *r.client, &configModel)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error validating plan", fmt.Sprintf("Loading service plan: %v", err))
return
}
// Plan does not support alert config
if plan.GetAlertMatchers() == 0 && plan.GetAlertReceivers() == 0 {
// If an alert config was set, return an error to the user
if !(utils.IsUndefined(configModel.AlertConfig)) {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error validating plan", fmt.Sprintf("Plan (%s) does not support configuring an alert config. Remove this from your config or use a different plan.", *plan.Name))
}
}
// Plan does not support log storage and trace storage
if plan.GetLogsStorage() == 0 && plan.GetTracesStorage() == 0 {
metricsRetentionDays := conversion.Int64ValueToPointer(configModel.MetricsRetentionDays)
metricsRetentionDays5mDownsampling := conversion.Int64ValueToPointer(configModel.MetricsRetentionDays5mDownsampling)
metricsRetentionDays1hDownsampling := conversion.Int64ValueToPointer(configModel.MetricsRetentionDays1hDownsampling)
// If any of the metrics retention days are set, return an error to the user
if metricsRetentionDays != nil || metricsRetentionDays5mDownsampling != nil || metricsRetentionDays1hDownsampling != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error validating plan", fmt.Sprintf("Plan (%s) does not support configuring metrics retention days. Remove this from your config or use a different plan.", *plan.Name))
}
}
}
// Create creates the resource and sets the initial Terraform state.
func (r *instanceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
// Retrieve values from plan
@ -797,10 +843,6 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
}
}
metricsRetentionDays := conversion.Int64ValueToPointer(model.MetricsRetentionDays)
metricsRetentionDays5mDownsampling := conversion.Int64ValueToPointer(model.MetricsRetentionDays5mDownsampling)
metricsRetentionDays1hDownsampling := conversion.Int64ValueToPointer(model.MetricsRetentionDays1hDownsampling)
alertConfig := alertConfigModel{}
if !(model.AlertConfig.IsNull() || model.AlertConfig.IsUnknown()) {
diags = model.AlertConfig.As(ctx, &alertConfig, basetypes.ObjectAsOptions{})
@ -813,7 +855,7 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
projectId := model.ProjectId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
err := r.loadPlanId(ctx, &model)
plan, err := loadPlanId(ctx, *r.client, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Loading service plan: %v", err))
return
@ -877,92 +919,43 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
return
}
// If any of the metrics retention days are set, set the metrics retention policy
if metricsRetentionDays != nil || metricsRetentionDays5mDownsampling != nil || metricsRetentionDays1hDownsampling != nil {
// Need to get the metrics retention policy because update endpoint is a PUT and we need to send all fields
metricsResp, err := r.client.GetMetricsStorageRetentionExecute(ctx, *instanceId, projectId)
// There are some plans which does not offer storage e.g. like Observability-Metrics-Endpoint-100k-EU01
if plan.GetLogsStorage() != 0 && plan.GetTracesStorage() != 0 {
err := r.getMetricsRetention(ctx, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Getting metrics retention policy: %v", err))
return
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("%v", err))
}
metricsRetentionPayload, err := toUpdateMetricsStorageRetentionPayload(metricsRetentionDays, metricsRetentionDays5mDownsampling, metricsRetentionDays1hDownsampling, metricsResp)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Building metrics retention policy payload: %v", err))
// Set state to fully populated data
diags = setMetricsRetentions(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
_, err = r.client.UpdateMetricsStorageRetention(ctx, *instanceId, projectId).UpdateMetricsStorageRetentionPayload(*metricsRetentionPayload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Setting metrics retention policy: %v", err))
} else {
// Set metric retention days to zero
diags = setMetricsRetentionsZero(ctx, &resp.State)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
// Get metrics retention policy after update
metricsResp, err := r.client.GetMetricsStorageRetentionExecute(ctx, *instanceId, projectId)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Getting metrics retention policy: %v", err))
return
}
// Map response body to schema
err = mapMetricsRetentionField(metricsResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the metrics retention: %v", err))
return
}
// Set state to fully populated data
diags = setMetricsRetentions(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// Alert Config
if model.AlertConfig.IsUnknown() || model.AlertConfig.IsNull() {
alertConfig, err = getMockAlertConfig(ctx)
// There are plans where no alert matchers and receivers are present e.g. like Observability-Metrics-Endpoint-100k-EU01
if plan.GetAlertMatchers() != 0 && plan.GetAlertReceivers() != 0 {
err := r.getAlertConfigs(ctx, &alertConfig, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Getting mock alert config: %v", err))
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("%v", err))
}
// Set state to fully populated data
diags = setAlertConfig(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
alertConfigPayload, err := toUpdateAlertConfigPayload(ctx, &alertConfig)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Building alert config payload: %v", err))
return
}
if alertConfigPayload != nil {
_, err = r.client.UpdateAlertConfigs(ctx, *instanceId, projectId).UpdateAlertConfigsPayload(*alertConfigPayload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Setting alert config: %v", err))
return
}
}
// Get alert config after update
alertConfigResp, err := r.client.GetAlertConfigs(ctx, *instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Getting alert config: %v", err))
return
}
// Map response body to schema
err = mapAlertConfigField(ctx, alertConfigResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the alert config: %v", err))
return
}
// Set state to fully populated data
diags = setAlertConfig(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "Observability instance created")
}
@ -994,24 +987,6 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
return
}
aclListResp, err := r.client.ListACL(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API for ACL data: %v", err))
return
}
metricsRetentionResp, err := r.client.GetMetricsStorageRetention(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get metrics retention: %v", err))
return
}
alertConfigResp, err := r.client.GetAlertConfigs(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get alert config: %v", err))
return
}
// Map response body to schema
err = mapFields(ctx, instanceResp, &model)
if err != nil {
@ -1019,6 +994,18 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
return
}
plan, err := loadPlanId(ctx, *r.client, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Loading service plan: %v", err))
return
}
aclListResp, err := r.client.ListACL(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API for ACL data: %v", err))
return
}
// Set state to fully populated data
diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...)
@ -1040,32 +1027,47 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
return
}
// Map response body to schema
err = mapMetricsRetentionField(metricsRetentionResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API response for the metrics retention: %v", err))
return
// There are some plans which does not offer storage e.g. like Observability-Metrics-Endpoint-100k-EU01
if plan.GetLogsStorage() != 0 && plan.GetTracesStorage() != 0 {
metricsRetentionResp, err := r.client.GetMetricsStorageRetention(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get metrics retention: %v", err))
return
}
// Map response body to schema
err = mapMetricsRetentionField(metricsRetentionResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API response for the metrics retention: %v", err))
return
}
// Set state to fully populated data
diags = setMetricsRetentions(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
// Set state to fully populated data
diags = setMetricsRetentions(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// There are plans where no alert matchers and receivers are present e.g. like Observability-Metrics-Endpoint-100k-EU01
if plan.GetAlertMatchers() != 0 && plan.GetAlertReceivers() != 0 {
alertConfigResp, err := r.client.GetAlertConfigs(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API to get alert config: %v", err))
return
}
// Map response body to schema
err = mapAlertConfigField(ctx, alertConfigResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the alert config: %v", err))
return
}
// Map response body to schema
err = mapAlertConfigField(ctx, alertConfigResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the alert config: %v", err))
return
}
// Set state to fully populated data
diags = setAlertConfig(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
// Set state to fully populated data
diags = setAlertConfig(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
tflog.Info(ctx, "Observability instance read")
@ -1092,10 +1094,6 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
}
}
metricsRetentionDays := conversion.Int64ValueToPointer(model.MetricsRetentionDays)
metricsRetentionDays5mDownsampling := conversion.Int64ValueToPointer(model.MetricsRetentionDays5mDownsampling)
metricsRetentionDays1hDownsampling := conversion.Int64ValueToPointer(model.MetricsRetentionDays1hDownsampling)
alertConfig := alertConfigModel{}
if !(model.AlertConfig.IsNull() || model.AlertConfig.IsUnknown()) {
diags = model.AlertConfig.As(ctx, &alertConfig, basetypes.ObjectAsOptions{})
@ -1105,7 +1103,7 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
}
}
err := r.loadPlanId(ctx, &model)
plan, err := loadPlanId(ctx, *r.client, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Loading service plan: %v", err))
return
@ -1186,91 +1184,43 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
return
}
// If any of the metrics retention days are set, set the metrics retention policy
if metricsRetentionDays != nil || metricsRetentionDays5mDownsampling != nil || metricsRetentionDays1hDownsampling != nil {
// Need to get the metrics retention policy because update endpoint is a PUT and we need to send all fields
metricsResp, err := r.client.GetMetricsStorageRetentionExecute(ctx, instanceId, projectId)
// There are some plans which does not offer storage e.g. like Observability-Metrics-Endpoint-100k-EU01
if plan.GetLogsStorage() != 0 && plan.GetTracesStorage() != 0 {
err := r.getMetricsRetention(ctx, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Getting metrics retention policy: %v", err))
return
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("%v", err))
}
metricsRetentionPayload, err := toUpdateMetricsStorageRetentionPayload(metricsRetentionDays, metricsRetentionDays5mDownsampling, metricsRetentionDays1hDownsampling, metricsResp)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Building metrics retention policy payload: %v", err))
// Set state to fully populated data
diags = setMetricsRetentions(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
_, err = r.client.UpdateMetricsStorageRetention(ctx, instanceId, projectId).UpdateMetricsStorageRetentionPayload(*metricsRetentionPayload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Setting metrics retention policy: %v", err))
} else {
// Set metric retention days to zero
diags = setMetricsRetentionsZero(ctx, &resp.State)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
// Get metrics retention policy after update
metricsResp, err := r.client.GetMetricsStorageRetentionExecute(ctx, instanceId, projectId)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Getting metrics retention policy: %v", err))
return
}
// Map response body to schema
err = mapMetricsRetentionField(metricsResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API response for the metrics retention %v", err))
return
}
// Set state to fully populated data
diags = setMetricsRetentions(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// Alert Config
if model.AlertConfig.IsUnknown() || model.AlertConfig.IsNull() {
alertConfig, err = getMockAlertConfig(ctx)
// There are plans where no alert matchers and receivers are present e.g. like Observability-Metrics-Endpoint-100k-EU01
if plan.GetAlertMatchers() != 0 && plan.GetAlertReceivers() != 0 {
err := r.getAlertConfigs(ctx, &alertConfig, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Getting mock alert config: %v", err))
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("%v", err))
}
// Set state to fully populated data
diags = setAlertConfig(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
alertConfigPayload, err := toUpdateAlertConfigPayload(ctx, &alertConfig)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Building alert config payload: %v", err))
return
}
if alertConfigPayload != nil {
_, err = r.client.UpdateAlertConfigs(ctx, instanceId, projectId).UpdateAlertConfigsPayload(*alertConfigPayload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Setting alert config: %v", err))
return
}
}
// Get updated alert config
alertConfigResp, err := r.client.GetAlertConfigs(ctx, instanceId, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Calling API to get alert config: %v", err))
return
}
// Map response body to schema
err = mapAlertConfigField(ctx, alertConfigResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API response for the alert config: %v", err))
return
}
// Set state to fully populated data
diags = setAlertConfig(ctx, &resp.State, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "Observability instance updated")
}
@ -2194,11 +2144,11 @@ func toGlobalConfigPayload(ctx context.Context, model *alertConfigModel) (*obser
}, nil
}
func (r *instanceResource) loadPlanId(ctx context.Context, model *Model) error {
func loadPlanId(ctx context.Context, client observability.APIClient, model *Model) (observability.Plan, error) {
projectId := model.ProjectId.ValueString()
res, err := r.client.ListPlans(ctx, projectId).Execute()
res, err := client.ListPlans(ctx, projectId).Execute()
if err != nil {
return err
return observability.Plan{}, err
}
planName := model.PlanName.ValueString()
@ -2211,12 +2161,87 @@ func (r *instanceResource) loadPlanId(ctx context.Context, model *Model) error {
}
if strings.EqualFold(*p.Name, planName) && p.PlanId != nil {
model.PlanId = types.StringPointerValue(p.PlanId)
break
return p, nil
}
avl = fmt.Sprintf("%s\n- %s", avl, *p.Name)
}
if model.PlanId.ValueString() == "" {
return fmt.Errorf("couldn't find plan_name '%s', available names are: %s", planName, avl)
return observability.Plan{}, fmt.Errorf("couldn't find plan_name '%s', available names are: %s", planName, avl)
}
return observability.Plan{}, nil
}
func (r *instanceResource) getAlertConfigs(ctx context.Context, alertConfig *alertConfigModel, model *Model) error {
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
var err error
// Alert Config
if utils.IsUndefined(model.AlertConfig) {
*alertConfig, err = getMockAlertConfig(ctx)
if err != nil {
return fmt.Errorf("Getting mock alert config: %w", err)
}
}
alertConfigPayload, err := toUpdateAlertConfigPayload(ctx, alertConfig)
if err != nil {
return fmt.Errorf("Building alert config payload: %w", err)
}
if alertConfigPayload != nil {
_, err = r.client.UpdateAlertConfigs(ctx, instanceId, projectId).UpdateAlertConfigsPayload(*alertConfigPayload).Execute()
if err != nil {
return fmt.Errorf("Setting alert config: %w", err)
}
}
alertConfigResp, err := r.client.GetAlertConfigs(ctx, instanceId, projectId).Execute()
if err != nil {
return fmt.Errorf("Calling API to get alert config: %w", err)
}
// Map response body to schema
err = mapAlertConfigField(ctx, alertConfigResp, model)
if err != nil {
return fmt.Errorf("Processing API response for the alert config: %w", err)
}
return nil
}
func (r *instanceResource) getMetricsRetention(ctx context.Context, model *Model) error {
metricsRetentionDays := conversion.Int64ValueToPointer(model.MetricsRetentionDays)
metricsRetentionDays5mDownsampling := conversion.Int64ValueToPointer(model.MetricsRetentionDays5mDownsampling)
metricsRetentionDays1hDownsampling := conversion.Int64ValueToPointer(model.MetricsRetentionDays1hDownsampling)
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
// If any of the metrics retention days are set, set the metrics retention policy
if metricsRetentionDays != nil || metricsRetentionDays5mDownsampling != nil || metricsRetentionDays1hDownsampling != nil {
// Need to get the metrics retention policy because update endpoint is a PUT and we need to send all fields
metricsResp, err := r.client.GetMetricsStorageRetentionExecute(ctx, instanceId, projectId)
if err != nil {
return fmt.Errorf("Getting metrics retention policy: %w", err)
}
metricsRetentionPayload, err := toUpdateMetricsStorageRetentionPayload(metricsRetentionDays, metricsRetentionDays5mDownsampling, metricsRetentionDays1hDownsampling, metricsResp)
if err != nil {
return fmt.Errorf("Building metrics retention policy payload: %w", err)
}
_, err = r.client.UpdateMetricsStorageRetention(ctx, instanceId, projectId).UpdateMetricsStorageRetentionPayload(*metricsRetentionPayload).Execute()
if err != nil {
return fmt.Errorf("Setting metrics retention policy: %w", err)
}
}
// Get metrics retention policy after update
metricsResp, err := r.client.GetMetricsStorageRetentionExecute(ctx, instanceId, projectId)
if err != nil {
return fmt.Errorf("Getting metrics retention policy: %w", err)
}
// Map response body to schema
err = mapMetricsRetentionField(metricsResp, model)
if err != nil {
return fmt.Errorf("Processing API response for the metrics retention %w", err)
}
return nil
}
@ -2225,6 +2250,15 @@ func setACL(ctx context.Context, state *tfsdk.State, model *Model) diag.Diagnost
return state.SetAttribute(ctx, path.Root("acl"), model.ACL)
}
// Needed since some plans cannot call the metrics API.
// Since the fields are optional but get a default value from the API this needs to be set for the other plans manually.
func setMetricsRetentionsZero(ctx context.Context, state *tfsdk.State) (diags diag.Diagnostics) {
diags = append(diags, state.SetAttribute(ctx, path.Root("metrics_retention_days"), 0)...)
diags = append(diags, state.SetAttribute(ctx, path.Root("metrics_retention_days_5m_downsampling"), 0)...)
diags = append(diags, state.SetAttribute(ctx, path.Root("metrics_retention_days_1h_downsampling"), 0)...)
return diags
}
func setMetricsRetentions(ctx context.Context, state *tfsdk.State, model *Model) (diags diag.Diagnostics) {
diags = append(diags, state.SetAttribute(ctx, path.Root("metrics_retention_days"), model.MetricsRetentionDays)...)
diags = append(diags, state.SetAttribute(ctx, path.Root("metrics_retention_days_5m_downsampling"), model.MetricsRetentionDays5mDownsampling)...)