sullivtr / terraform-provider-graphql

Terraform GraphQL plugin to automate the full life-cycle of graphql api resources.
https://sullivtr.github.io/terraform-provider-graphql
Mozilla Public License 2.0
53 stars 13 forks source link

Plugin crashes when read_query response contains null value #94

Open mootari opened 1 year ago

mootari commented 1 year ago

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) ~~~hcl 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/v2@v2.7.0/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/v2@v2.7.0/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/v2@v2.7.0/helper/schema/grpc_provider.go:575 +0x5a8
github.com/hashicorp/terraform-plugin-go/tfprotov5/server.(*server).ReadResource(0x14000083640, {0x1030c54a0, 0x1400052b8f0}, 0x140004811a0)
    github.com/hashicorp/terraform-plugin-go@v0.3.0/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/terraform-plugin-go@v0.3.0/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/grpc@v1.32.0/server.go:1194 +0xc38
google.golang.org/grpc.(*Server).handleStream(0x14000211180, {0x1030d0990, 0x140000fa780}, 0x140004bc700, 0x0)
    google.golang.org/grpc@v1.32.0/server.go:1517 +0xa34
google.golang.org/grpc.(*Server).serveStreams.func1.2(0x1400037e870, 0x14000211180, {0x1030d0990, 0x140000fa780}, 0x140004bc700)
    google.golang.org/grpc@v1.32.0/server.go:859 +0x94
created by google.golang.org/grpc.(*Server).serveStreams.func1
    google.golang.org/grpc@v1.32.0/server.go:857 +0x1f0

Error: The terraform-provider-graphql_2.5.4 plugin crashed!
sullivtr commented 1 year ago

@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?

mootari commented 1 year ago

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

sullivtr commented 1 year ago

@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.

fsdrw08 commented 6 months ago

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

panic: interface conversion: interface {} is float64, not string
devinnasar commented 5 months ago

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

sullivtr commented 1 month ago

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