terraform-provider-graphql icon indicating copy to clipboard operation
terraform-provider-graphql copied to clipboard

Plugin crashes when read_query response contains null value

Open mootari opened this issue 1 year ago • 6 comments

I was testing the resilience of my graphql_mutation resource by deallocating a resource externally and then running terraform apply again. Unfortunately the plugin crashes when one of the to-be-mapped values in the read_query response is null.

Given the following provider resource

resource "graphql_mutation" "fly_ipv4_shared" {
  for_each = toset([ fly_app.MY_APP.name ])

  mutation_variables    = { appId = each.key }
  read_query_variables  = { appId = each.key }
  compute_mutation_keys = {
    appId = "app.id"
    ip    = "app.sharedIpAddress"
  }
  create_mutation = "mutation ($appId: ID!) { allocateIpAddress(input: {appId: $appId, type: shared_v4}) { app { sharedIpAddress } } }"
  update_mutation = "mutation ($appId: ID!) { allocateIpAddress(input: {appId: $appId, type: shared_v4}) { app { sharedIpAddress } } }"
  delete_mutation = "mutation ($appId: ID!, $ip: String!) { releaseIpAddress(input: {appId: $appId, ip: $ip}) { clientMutationId } }"
  read_query      = "query ($appId: String!) { app(name: $appId) { id, sharedIpAddress } }"
}

and the state

(show state)
resource "graphql_mutation" "fly_ipv4_shared" {
    compute_mutation_keys               = {
        "appId" = "app.id"
        "ip"    = "app.sharedIpAddress"
    }
    computed_delete_operation_variables = {
        "appId" = "my-app-id"
        "ip"    = "66.241.124.84"
    }
    computed_read_operation_variables   = {
        "appId" = "my-app-id"
        "ip"    = "66.241.124.84"
    }
    computed_update_operation_variables = {
        "appId" = "my-app-id"
        "ip"    = "66.241.124.84"
    }
    create_mutation                     = "mutation ($appId: ID!) { allocateIpAddress(input: {appId: $appId, type: shared_v4}) { app { sharedIpAddress } } }"
    delete_mutation                     = "mutation ($appId: ID!, $ip: String!) { releaseIpAddress(input: {appId: $appId, ip: $ip}) { clientMutationId } }"
    enable_remote_state_verification    = true
    existing_hash                       = "1025037459"
    force_replace                       = false
    id                                  = "1025037459"
    mutation_variables                  = {
        "appId" = "my-app-id"
    }
    query_response                      = jsonencode(
        {
            data = {
                app = {
                    id              = "my-app-id"
                    sharedIpAddress = "66.241.124.84"
                }
            }
        }
    )
    query_response_input_key_map        = {
        "appId" = "app.id"
    }
    read_query                          = "query ($appId: String!) { app(name: $appId) { id, sharedIpAddress } }"
    read_query_variables                = {
        "appId" = "my-app-id"
    }
    update_mutation                     = "mutation ($appId: ID!) { allocateIpAddress(input: {appId: $appId, type: shared_v4}) { app { sharedIpAddress } } }"
}

if on the next run the read_query response has the shape

{
  "data": {
    "app": {
      "id": "my-app-id",
      "sharedIpAddress": null
    }
  }
}

then the plugin crashes in computeMutationVariableKeys with the following error and trace:

Planning failed. Terraform encountered an error while generating this plan.

╷
│ Error: Plugin did not respond
│ 
│   with graphql_mutation.fly_ipv4_shared["obs-test-5--api"],
│   on deploy.tf line 82, in resource "graphql_mutation" "fly_ipv4_shared":
│   82: resource "graphql_mutation" "fly_ipv4_shared" {
│ 
│ The plugin encountered an error, and failed to respond to the plugin.(*GRPCProvider).ReadResource call. The plugin logs may contain more details.
╵
panic: interface conversion: interface {} is nil, not string

goroutine 38 [running]:
github.com/sullivtr/terraform-provider-graphql/graphql.computeMutationVariableKeys(0x140002516e0, 0x14000251aa0)
	github.com/sullivtr/terraform-provider-graphql/graphql/keys.go:36 +0x1b4
github.com/sullivtr/terraform-provider-graphql/graphql.computeMutationVariables({0x140001a1800, 0x40, 0x200}, 0x140000fc300)
	github.com/sullivtr/terraform-provider-graphql/graphql/resource_graphql_mutation.go:327 +0x1a8
github.com/sullivtr/terraform-provider-graphql/graphql.resourceGraphqlRead({0x1030c5468, 0x1400010c480}, 0x140000fc300, {0x102fa18c0, 0x1400028ace0})
	github.com/sullivtr/terraform-provider-graphql/graphql/resource_graphql_mutation.go:240 +0x74c
github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema.(*Resource).read(0x1400025c540, {0x1030c53f8, 0x14000493280}, 0x140000fc300, {0x102fa18c0, 0x1400028ace0})
	github.com/hashicorp/terraform-plugin-sdk/[email protected]/helper/schema/resource.go:347 +0x118
github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema.(*Resource).RefreshWithoutUpgrade(0x1400025c540, {0x1030c53f8, 0x14000493280}, 0x140004c32d0, {0x102fa18c0, 0x1400028ace0})
	github.com/hashicorp/terraform-plugin-sdk/[email protected]/helper/schema/resource.go:624 +0x388
github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema.(*GRPCProviderServer).ReadResource(0x1400000d488, {0x1030c53f8, 0x14000493280}, 0x140004932c0)
	github.com/hashicorp/terraform-plugin-sdk/[email protected]/helper/schema/grpc_provider.go:575 +0x5a8
github.com/hashicorp/terraform-plugin-go/tfprotov5/server.(*server).ReadResource(0x14000083640, {0x1030c54a0, 0x1400052b8f0}, 0x140004811a0)
	github.com/hashicorp/[email protected]/tfprotov5/server/server.go:298 +0x240
github.com/hashicorp/terraform-plugin-go/tfprotov5/internal/tfplugin5._Provider_ReadResource_Handler({0x10307f4e0, 0x14000083640}, {0x1030c54a0, 0x1400052b8f0}, 0x14000481140, 0x0)
	github.com/hashicorp/[email protected]/tfprotov5/internal/tfplugin5/tfplugin5_grpc.pb.go:344 +0x1c0
google.golang.org/grpc.(*Server).processUnaryRPC(0x14000211180, {0x1030d0990, 0x140000fa780}, 0x140004bc700, 0x14000384030, 0x103530b30, 0x0)
	google.golang.org/[email protected]/server.go:1194 +0xc38
google.golang.org/grpc.(*Server).handleStream(0x14000211180, {0x1030d0990, 0x140000fa780}, 0x140004bc700, 0x0)
	google.golang.org/[email protected]/server.go:1517 +0xa34
google.golang.org/grpc.(*Server).serveStreams.func1.2(0x1400037e870, 0x14000211180, {0x1030d0990, 0x140000fa780}, 0x140004bc700)
	google.golang.org/[email protected]/server.go:859 +0x94
created by google.golang.org/grpc.(*Server).serveStreams.func1
	google.golang.org/[email protected]/server.go:857 +0x1f0

Error: The terraform-provider-graphql_2.5.4 plugin crashed!

mootari avatar May 22 '23 13:05 mootari

@mootari Thanks for reporting this. I will look into this. Am I correct to assume the expected behavior here is for the read query to not flop on a null value, and proceed to execute the mutation to re-populate the value?

sullivtr avatar May 24 '23 22:05 sullivtr

@sullivtr Yes, I think that's accurate. Thank you!

mootari avatar May 25 '23 00:05 mootari

@mootari In the meantime, it seems like setting compute_from_create to true would benefit you here (assuming the graphql response from your create mutation would return the most accurate representation of this data). This is still a bug I need to fix, but this could be a workaround for you in the meantime.

sullivtr avatar May 25 '23 17:05 sullivtr

I have the same issue, even if I use compute_from_create, error shows

panic: interface conversion: interface {} is float64, not string

fsdrw08 avatar May 01 '24 01:05 fsdrw08

@sullivtr, I am also running into the computer_from_create error. Is this something you have time to look into?

devinnasar avatar May 16 '24 16:05 devinnasar

Hi folks, I believe that #96 by @dlecocq should be a fix for this issue, at least for non-string values such as float64 and integers. Released in v2.5.5

sullivtr avatar Sep 02 '24 20:09 sullivtr