Skip to content

Commit

Permalink
Use private state to track traffic filters previously managed by the …
Browse files Browse the repository at this point in the history
…deployment resource
  • Loading branch information
tobio committed Jul 31, 2023
1 parent ae29a40 commit a3d9507
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 20 deletions.
3 changes: 2 additions & 1 deletion ec/ecresource/deploymentresource/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ func (r *Resource) Create(ctx context.Context, req resource.CreateRequest, resp

resp.Diagnostics.Append(v2.HandleRemoteClusters(ctx, r.client, *res.ID, plan.Elasticsearch)...)

deployment, diags := r.read(ctx, *res.ID, nil, &plan, res.Resources)
deployment, diags := r.read(ctx, *res.ID, nil, &plan, res.Resources, request.Settings.TrafficFilterSettings.Rulesets)
updatePrivateStateTrafficFiltersFromCreate(ctx, resp, request.Settings.TrafficFilterSettings.Rulesets)

resp.Diagnostics.Append(diags...)

Expand Down
24 changes: 22 additions & 2 deletions ec/ecresource/deploymentresource/deployment/v2/deployment_read.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,14 +230,19 @@ func (dep *Deployment) ProcessSelfInObservability() {
}
}

func (dep *Deployment) HandleEmptyTrafficFilters(ctx context.Context, base DeploymentTF) diag.Diagnostics {
func (dep *Deployment) HandleEmptyTrafficFilters(ctx context.Context, base DeploymentTF, privateFilters []string) diag.Diagnostics {
var baseFilters []string
diags := base.TrafficFilter.ElementsAs(ctx, &baseFilters, true)
if diags.HasError() {
return diags
}

// Only include traffic filters which are part of the TF plan.
for _, filter := range privateFilters {
if !slices.Contains[string](baseFilters, filter) {
baseFilters = append(baseFilters, filter)
}
}

if len(baseFilters) == 0 {
dep.TrafficFilter = baseFilters
}
Expand All @@ -251,6 +256,21 @@ func (dep *Deployment) HandleEmptyTrafficFilters(ctx context.Context, base Deplo

dep.TrafficFilter = intersectionFilters

// // Ensure consistency between null, and empty configured traffic filter values.
// // The Cloud API represents an empty set of traffic filters as a null/missing value. Terraform does distinguish between those two cases.
// // If the Cloud response does not include traffic filters, then set the read value as the planned value, but only if the planned value is empty.
// if dep.TrafficFilter == nil {
// var baseFilters []string
// diags := base.TrafficFilter.ElementsAs(ctx, &baseFilters, true)
// if diags.HasError() {
// return diags
// }

// if len(baseFilters) == 0 {
// dep.TrafficFilter = baseFilters
// }
// }

return diags
}

Expand Down
73 changes: 73 additions & 0 deletions ec/ecresource/deploymentresource/private_state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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 deploymentresource

import (
"context"
"encoding/json"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource"
)

const trafficFilterStateKey = "traffic_filters"

func readPrivateStateTrafficFiltersFromRead(ctx context.Context, req resource.ReadRequest) ([]string, diag.Diagnostics) {
return readPrivateStateTrafficFilters(req.Private.GetKey(ctx, trafficFilterStateKey))
}

func readPrivateStateTrafficFiltersFromUpdate(ctx context.Context, req resource.UpdateRequest) ([]string, diag.Diagnostics) {
return readPrivateStateTrafficFilters(req.Private.GetKey(ctx, trafficFilterStateKey))
}

func readPrivateStateTrafficFilters(privateFilterBytes []byte, diags diag.Diagnostics) ([]string, diag.Diagnostics) {
if privateFilterBytes == nil || diags.HasError() {
return []string{}, diags
}

var privateFilters []string
err := json.Unmarshal(privateFilterBytes, &privateFilters)
if err != nil {
diags.AddError("failed to parse private state", err.Error())
return []string{}, diags
}

return privateFilters, diags
}

func updatePrivateStateTrafficFiltersFromUpdate(ctx context.Context, resp *resource.UpdateResponse, filters []string) diag.Diagnostics {
var diags diag.Diagnostics
filterBytes, err := json.Marshal(filters)
if err != nil {
diags.AddError("failed to update private state", err.Error())
return diags
}

return resp.Private.SetKey(ctx, trafficFilterStateKey, filterBytes)
}

func updatePrivateStateTrafficFiltersFromCreate(ctx context.Context, resp *resource.CreateResponse, filters []string) diag.Diagnostics {
var diags diag.Diagnostics
filterBytes, err := json.Marshal(filters)
if err != nil {
diags.AddError("failed to update private state", err.Error())
return diags
}

return resp.Private.SetKey(ctx, trafficFilterStateKey, filterBytes)
}
12 changes: 9 additions & 3 deletions ec/ecresource/deploymentresource/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,14 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo

var newState *deploymentv2.Deployment

privateFilters, d := readPrivateStateTrafficFiltersFromRead(ctx, request)
response.Diagnostics.Append(d...)
if response.Diagnostics.HasError() {
return
}

// use state for the plan (there is no plan and config during Read) - otherwise we can get unempty plan output
newState, diags = r.read(ctx, curState.Id.ValueString(), &curState, nil, nil)
newState, diags = r.read(ctx, curState.Id.ValueString(), &curState, nil, nil, privateFilters)

response.Diagnostics.Append(diags...)

Expand All @@ -74,7 +80,7 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo
}

// at least one of state and plan should not be nil
func (r *Resource) read(ctx context.Context, id string, state *deploymentv2.DeploymentTF, plan *deploymentv2.DeploymentTF, deploymentResources []*models.DeploymentResource) (*deploymentv2.Deployment, diag.Diagnostics) {
func (r *Resource) read(ctx context.Context, id string, state *deploymentv2.DeploymentTF, plan *deploymentv2.DeploymentTF, deploymentResources []*models.DeploymentResource, privateFilters []string) (*deploymentv2.Deployment, diag.Diagnostics) {
var diags diag.Diagnostics

var base deploymentv2.DeploymentTF
Expand Down Expand Up @@ -158,7 +164,7 @@ func (r *Resource) read(ctx context.Context, id string, state *deploymentv2.Depl
deployment.ResetElasticsearchPassword = base.ResetElasticsearchPassword.ValueBoolPointer()
}

diags.Append(deployment.HandleEmptyTrafficFilters(ctx, base)...)
diags.Append(deployment.HandleEmptyTrafficFilters(ctx, base, privateFilters)...)

deployment.SetCredentialsIfEmpty(state)

Expand Down
30 changes: 16 additions & 14 deletions ec/ecresource/deploymentresource/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,17 @@ func (r *Resource) Update(ctx context.Context, req resource.UpdateRequest, resp
return
}

resp.Diagnostics.Append(HandleTrafficFilterChange(ctx, r.client, plan, state)...)

privateFilters, d := readPrivateStateTrafficFiltersFromUpdate(ctx, req)
resp.Diagnostics.Append(d...)
if resp.Diagnostics.HasError() {
return
}
planRules, diags := HandleTrafficFilterChange(ctx, r.client, plan, privateFilters)
resp.Diagnostics.Append(diags...)
updatePrivateStateTrafficFiltersFromUpdate(ctx, resp, planRules)
resp.Diagnostics.Append(v2.HandleRemoteClusters(ctx, r.client, plan.Id.ValueString(), plan.Elasticsearch)...)

deployment, diags := r.read(ctx, plan.Id.ValueString(), &state, &plan, res.Resources)
deployment, diags := r.read(ctx, plan.Id.ValueString(), &state, &plan, res.Resources, planRules)

resp.Diagnostics.Append(diags...)

Expand Down Expand Up @@ -116,18 +122,14 @@ func (r *Resource) ResetElasticsearchPassword(deploymentID string, refID string)
return *resetResp.Password, diags
}

func HandleTrafficFilterChange(ctx context.Context, client *api.API, plan, state v2.DeploymentTF) diag.Diagnostics {
if plan.TrafficFilter.Equal(state.TrafficFilter) {
return nil
}
func HandleTrafficFilterChange(ctx context.Context, client *api.API, plan v2.DeploymentTF, stateRules ruleSet) ([]string, diag.Diagnostics) {
// if plan.TrafficFilter.Equal(state.TrafficFilter) {
// return nil
// }

var planRules, stateRules ruleSet
var planRules ruleSet
if diags := plan.TrafficFilter.ElementsAs(ctx, &planRules, true); diags.HasError() {
return diags
}

if diags := state.TrafficFilter.ElementsAs(ctx, &stateRules, true); diags.HasError() {
return diags
return []string{}, diags
}

var rulesToAdd, rulesToDelete []string
Expand Down Expand Up @@ -157,7 +159,7 @@ func HandleTrafficFilterChange(ctx context.Context, client *api.API, plan, state
}
}

return diags
return planRules, diags
}

type ruleSet []string
Expand Down

0 comments on commit a3d9507

Please sign in to comment.