Hello, I have the follow plan and state data in my Update
function using the new provider framework…
plan:
&{Activate:true Comment:"Managed by Terraform" Domain:[{"comment":<null>,"name":"tf-test-pxhjl6z0px-terraform-provider-fastly-framework-2.integralist.co.uk"},{"comment":<null>,"name":"tf-test-pxhjl6z0px-updated-terraform-provider-fastly-framework-1.integralist.co.uk"}] Force:true ID:"123" LastActive:<unknown> Name:"tf-test-pxhjl6z0px-updated" Reuse:<null> Version:<unknown>}
state:
&{Activate:true Comment:"Managed by Terraform" Domain:[{"comment":<null>,"name":"tf-test-pxhjl6z0px-terraform-provider-fastly-framework-1.integralist.co.uk"},{"comment":<null>,"name":"tf-test-pxhjl6z0px-terraform-provider-fastly-framework-2.integralist.co.uk"}] Force:false ID:"123" LastActive:1 Name:"tf-test-pxhjl6z0px" Reuse:<null> Version:1}
The schema for the domains is:
Blocks: map[string]schema.Block{
"domain": schema.SetNestedBlock{
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"comment": schema.StringAttribute{
MarkdownDescription: "An optional comment about the domain",
Optional: true,
},
"name": schema.StringAttribute{
MarkdownDescription: "The domain that this service will respond to. It is important to note that changing this attribute will delete and recreate the resource",
Required: true,
},
},
},
},
},
In my provider tests I create two domains and then update one of them (I change tf-test-pxhjl6z0px-terraform-provider-fastly-framework-1.integralist.co.uk
to include -updated
like so tf-test-pxhjl6z0px-updated-terraform-provider-fastly-framework-1.integralist.co.uk
.
My API needs to know which specific domain needs updating, so how can I check which domain has been updated?
If the order of the domains were consistent then I could loop over the plan ‘domains’ and for each plan domain I could use the same index to find the domain in the state, but from what I’m seeing the order is not consistent.
My ‘Update’ code is below:
func (r *ServiceVCLResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan *ServiceVCLResourceModel
var state *ServiceVCLResourceModel
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}
// Read Terraform state data into the model so it can be compared against plan
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
// NOTE: The plan data doesn't contain computed attributes.
// So we need to read it from the current state.
plan.Version = state.Version
plan.LastActive = state.LastActive
if !plan.Domain.Equal(state.Domain) {
for _, domain := range plan.Domain.Elements() {
v, err := domain.ToTerraformValue(ctx)
if err != nil {
tflog.Trace(ctx, "ToTerraformValue error", map[string]any{"err": err})
resp.Diagnostics.AddError("ToTerraformValue error", fmt.Sprintf("Unable to convert type to Terraform value: %s", err))
return
}
var planDomains map[string]tftypes.Value
err = v.As(&planDomains)
if err != nil {
tflog.Trace(ctx, "As error", map[string]any{"err": err})
resp.Diagnostics.AddError("As error", fmt.Sprintf("Unable to convert type to Go value: %s", err))
return
}
for _, domain := range state.Domain.Elements() {
vState, err := domain.ToTerraformValue(ctx)
if err != nil {
tflog.Trace(ctx, "ToTerraformValue error", map[string]any{"err": err})
resp.Diagnostics.AddError("ToTerraformValue error", fmt.Sprintf("Unable to convert type to Terraform value: %s", err))
return
}
var stateDomains map[string]tftypes.Value
err = vState.As(&stateDomains)
if err != nil {
tflog.Trace(ctx, "As error", map[string]any{"err": err})
resp.Diagnostics.AddError("As error", fmt.Sprintf("Unable to convert type to Go value: %s", err))
return
}
}
// FIXME: What domain to update? How can I tell which domain has changed?
var domainToUpdate string
planDomains["name"].As(&domainToUpdate)
clientReq := r.client.DomainAPI.UpdateDomain(r.clientCtx, plan.ID.ValueString(), int32(plan.Version.ValueInt64()), domainToUpdate)
// Update specific aspects of the domain like a 'comment' or 'name'...
if v, ok := planDomains["comment"]; ok && !v.IsNull() {
var dst string
err := v.As(&dst)
if err != nil {
tflog.Trace(ctx, "As error", map[string]any{"err": err})
resp.Diagnostics.AddError("As error", fmt.Sprintf("Unable to convert type to Go value: %s", err))
return
}
clientReq.Comment(dst)
}
if v, ok := planDomains["name"]; ok && !v.IsNull() {
var dst string
err := v.As(&dst)
if err != nil {
tflog.Trace(ctx, "As error", map[string]any{"err": err})
resp.Diagnostics.AddError("As error", fmt.Sprintf("Unable to convert type to Go value: %s", err))
return
}
clientReq.Name(dst)
}
_, httpResp, err := clientReq.Execute()
if err != nil {
tflog.Trace(ctx, "Fastly DomainAPI.UpdateDomain error", map[string]any{"http_resp": httpResp})
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update domain, got error: %s", err))
return
}
if httpResp.StatusCode != http.StatusOK {
tflog.Trace(ctx, "Fastly API error", map[string]any{"http_resp": httpResp})
resp.Diagnostics.AddError("API Error", fmt.Sprintf("Unsuccessful status code: %s", httpResp.Status))
return
}
}
}
clientReq := r.client.ServiceAPI.UpdateService(r.clientCtx, plan.ID.ValueString())
if !plan.Comment.Equal(state.Comment) {
clientReq.Comment(plan.Comment.ValueString())
}
if !plan.Name.Equal(state.Name) {
clientReq.Name(plan.Name.ValueString())
}
_, httpResp, err := clientReq.Execute()
if err != nil {
tflog.Trace(ctx, "Fastly ServiceAPI.UpdateService error", map[string]any{"http_resp": httpResp})
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update service, got error: %s", err))
return
}
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
}