magodo / terraform-provider-restful

Terraform provider to manage RESTful resources
https://registry.terraform.io/providers/magodo/restful
Mozilla Public License 2.0
15 stars 5 forks source link

Question regarding restful_resource #95

Closed haferch closed 5 months ago

haferch commented 5 months ago

Hello,

first of all you have provided a create provider, but i have some trouble with the restful_resource, because the resource will be not added to the terraform stateful file. My problem is that the REST call returns only the id without any properties. The return value is in JSON format like this "\"hello\"". I am not able to set the read_path which is used as id, iam right ? I have tried like this read-path = $(path)/$(body), but i am not able to get the id in a string format.

Could you help me in this case and maybe can you provide a example ?

Thank you in advance!

best regards

Harald

magodo commented 5 months ago

@haferch Would you please elaborate your scenario with the following information:

Thank you!

haferch commented 5 months ago

Hello,

thank you for your fast response.

Here is an extract of the code:

locals { datacenter = jsonencode( { "folder": var.vsphere_datacenter.folder_name, "name": var.vsphere_datacenter.datacenter_name } ) }

resource "restful_resource" "create_datacenter"{ path = "vcenter/datacenter" read_path ="$(path)/$(body)" create_method = "POST" body = local.datacenter }

Here the response:

module.COMP_WLD_DATACENTERmodule.vsphere_minio.restful_resource.create_datacenter: Creating... ╷ │ Error: Failed to build the path for reading the resource │ │ with module.COMP_WLD_DATACENTER.module.vsphere_minio.restful_resource.create_datacenter, │ on modules\vsphere\datacenter.tf line 10, in resource "restful_resource" "create_datacenter": │ 10: resource "restful_resource" "create_datacenter"{ │ │ Can't build resource id with read_path: "$(path)/$(body)", path: "vcenter/datacenter", body: "\"datacenter-2434\"": invalid match: $(body)

I hope this helps to understand the problem.

Thank you in advance!

magodo commented 5 months ago

After you created this resource, how do you usually read it back via API (not using terraform)?

haferch commented 5 months ago

With this call /vcenter/datacenter/ in my case /vcenter/datacenter/datacenter-2434. So problem is that the response is JSON Format and your read_path must be a string. So, what I need is to convert the JSON response into a string. If I am right understand, the resposne is in the $(body) varaiable (in the read_path), but how can I convert it in a string ?

I will now stay online and wait for your answer. thank you again for your support

magodo commented 5 months ago

@haferch Would you like to test with the main branch locally to verify whether it works?

haferch commented 5 months ago

Could you provide me the source which I have to add to the provider configuration

magodo commented 5 months ago

You can check how to build the provider from source and use it via:

haferch commented 5 months ago

I have used as read_path this "$(path)/$(body)", but still not working. I don´t get an error message, the resource was created, but still not in the statefile.

Here the response:

issing Resource State After Create │ │ with module.COMP_WLD_DATACENTER.module.vsphere_datacenter.restful_resource.create_datacenter, │ on modules\vsphere\Datacenter\datacenter.tf line 10, in resource "restful_resource" "create_datacenter": │ 10: resource "restful_resource" "create_datacenter"{ │ │ The Terraform Provider unexpectedly returned no resource state after having no errors in the resource creation. This is always an issue in the Terraform Provider and should be reported to the provider developers. │ │ The resource may have been successfully created, but Terraform is not tracking it. Applying the configuration again with no other action may result in duplicate resource errors. Import the resource if the resource was actually created and Terraform should be tracking it.

Any idea ?

Thank you in advance!

magodo commented 5 months ago

@haferch I've just put a new commit to main, which enables the client API log. You can now export TF_LOG_PROVIDER=DEBUG to enable the log. From that, you can check which URL the GET right after the creation is targeting to.

haferch commented 5 months ago

Thank you very much for your support. I think the Problem are the Quotes in the respond Body. It is possible to remove it ?

==============================================================================


POST  /api/vcenter/datacenter  HTTP/1.1
HOST   : fra-w01-vc01.mgmt-ad.corp.internal
HEADERS:
        Accept: application/json
        Content-Type: application/json
        User-Agent: go-resty/2.10.0 (https://github.com/go-resty/resty)
        Vmware-Api-Session-Id: 7a4893e1ef86e274c3032d21c5461f5a
BODY   :
{
   "folder": "group-d1",
   "name": "hf-dc-001"
}
------------------------------------------------------------------------------
~~~ RESPONSE ~~~
STATUS       : 201 Created
PROTO        : HTTP/2.0
RECEIVED AT  : 2024-05-18T21:02:58.1406098+02:00
TIME DURATION: 908.9432ms
HEADERS      :
        Content-Type: application/json
        Date: Sat, 18 May 2024 19:02:57 GMT
        X-Envoy-Upstream-Service-Time: 313
BODY         :
"datacenter-2544"

==============================================================================
~~~ REQUEST ~~~
GET  /api/vcenter/datacenter/%22datacenter-2544%22  HTTP/1.1
HOST   : fra-w01-vc01.mgmt-ad.corp.internal
HEADERS:
        Accept: application/json
        Content-Type: application/json
        User-Agent: go-resty/2.10.0 (https://github.com/go-resty/resty)
        Vmware-Api-Session-Id: 7a4893e1ef86e274c3032d21c5461f5a
BODY   :
***** NO CONTENT *****
------------------------------------------------------------------------------
~~~ RESPONSE ~~~
STATUS       : 404 Not Found
PROTO        : HTTP/2.0
RECEIVED AT  : 2024-05-18T21:02:58.1596377+02:00
TIME DURATION: 2.3235ms
HEADERS      :
        Content-Type: application/json
        Date: Sat, 18 May 2024 19:02:58 GMT
        X-Envoy-Upstream-Service-Time: 6
BODY         :
{
   "error_type": "NOT_FOUND",
   "messages": [
      {
         "args": [
            "\"datacenter-2544\":e2065eeb-0f32-4a1d-b065-f180edde15fe"
         ],
         "default_message": "Datacenter with identifier '\"datacenter-2544\":e2065eeb-0f32-4a1d-b065-f180edde15fe' does not exist.",
         "id": "com.vmware.api.vcenter.datacenter.not_found"
      }
   ]
}
==============================================================================
magodo commented 5 months ago

@haferch Thank you for providing this! I've fixed the bug in the main, would you please verify it fixes your issue now?

haferch commented 5 months ago

Hello,

the GET is working now, but now I get this error:

Error: Provider produced inconsistent result after apply │ │ When applying changes to module.COMP_WLD_DATASTORE.module.vsphere_datastore.restful_resource.create_datacenter, provider │ "module.COMP_WLD_DATASTORE.provider[\"registry.terraform.io/magodo/restful\"].vsphere" produced an unexpected new value: .body: was │ cty.StringVal("{\"folder\":\"group-d1\",\"name\":\"dc-001\"}"), but now cty.StringVal("{\"name\":\"dc-001\"}").

Did i something wrong ?

Another short question what meand the "code" in the create_poll ?

magodo commented 5 months ago

@haferch The error indicates that you specified the folder in your request, but it isn't returned from the response. You can put this folder property in the write_only_attrs.

The code represents the http status code.

magodo commented 5 months ago

Fixed by https://github.com/magodo/terraform-provider-restful/commit/0c98b2195ba6903b8d8fb3f266c1c64e6b2abea8

haferch commented 5 months ago

Hello,

now it is working as expected. Thank you again for you great and fast support.

best regards

Harald

haferch commented 5 months ago

Hello,

I have again a short question. I have a complex JSON as input body to create a new resource, but the read_path returns only one value. I get the same rror as befor that the body was and is now .... Is the onliest possibility to add all inputs from the complex JSON to the write_only_attrs sting list or do you have another idea ? Here the Input (body) JSON:

{
    "control_plane": {
        "login_banner": "",
        "network": {
            "backing": {
                "backing": "",
                "network": ""
            },
            "ip_management": {
                "dhcp_enabled": ,
                "gateway_address": "",
                "ip_assignments": [
                    {
                        "assignee": "",
                        "ranges": [
                            {
                                "address": "",
                                "count":                            }
                        ]
                    }
                ]
            },
            "network": "",
            "services": {
                "dns": {
                    "search_domains": [
                        ""
                    ],
                    "servers": [
                        ""
                    ]
                },
                "ntp": {
                    "servers": [
                        ""
                    ]
                }
            }
        },
        "size": "",
        "storage_policy": "3
    },
    "name": "",
    "workloads": {
        "edge": {
            "load_balancer_address_ranges": [
                {
                    "address": "",
                    "count": 
                }
            ],
            "nsx": {
                "edge_cluster_ID": "",
                "egress_IP_ranges": [
                    {
                        "address": "",
                        "count": 
                    }
                ],
                "load_balancer_size": "",
                "routing_mode": ""
                "t0_gateway": ""
            },
            "provider": ""
        },
        "network": {
            "ip_management": {
                "dhcp_enabled": ,
                "ip_assignments": [
                    {
                        "assignee": "",
                        "ranges": [
                            {
                                "address": "",
                                "count": 
                            }
                        ]
                    },
                    {
                        "assignee": "",
                        "ranges": [
                            {
                                "address": "",
                                "count": 
                            }
                        ]
                    }
                ]
            },
            "network_type": "",
            "nsx": {
                "dvs": "",
                "namespace_subnet_prefix": ""
            },
            "services": {
                "dns": {
                    "search_domains": [
                        ""
                    ],
                    "servers": [
                        ""
                    ]
                }
            },
            "storage": {
                "ephemeral_storage_policy": "",
                "image_storage_policy": ""
            }
        },
        "zone": 
    }
}
magodo commented 5 months ago

@haferch Terraform by its nature requires the read returns the same thing that you used for creation, which makes the resource can be managed in a declaration flavor. This provider assumes the API is restful, which in most cases have its create and read (or even update) using the same data model.

So would you please further check your API to see if there exists a GET URL that can return the data model, and construct the read_path accordingly. Alternatively, some APIs might only have colleciton GET (i.e. list) API available, while that can still work well with this provider by using its read_selector.

Note that the write_only_attrs are not meant to contain too much attributes.

haferch commented 5 months ago

Ok, thank you for your support and your time!