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

Error when using provider with phpipam Version 1.6.0

Open steffencircle opened this issue 1 year ago • 9 comments

Hi,

i have upgraded our phpipam installation to the latest v1.6.0 , which was released mid december 2023 and now i am facing the following error, when using the latest phpipam Terraform provider (currently 1.5.2) :

JSON parsing error: json: cannot unmarshal number into Go struct field Section.showVLAN of type string

I have since rolled back to v1.5.2 and everything is working as expected again. I had a look on what might have change with regards to "sections" and therefore did some investigations via the phpipam API.

Below is a section output of the error, on what the returned response for Section now with 1.6.0 looks like:

  {
    "id": 1,
    "name": "Customers",
    "description": "Section for customers",
    "masterSection": 0,
    "permissions": "{\"3\":\"1\",\"2\":\"2\"}",
    "strictMode": "1",
    "subnetOrdering": null,
    "order": 3,
    "editDate": "2022-12-22 10:48:16",
    "showSubnet": 1,
    "showVLAN": 0,
    "showVRF": 0,
    "showSupernetOnly": 0,
    "DNS": null
  },

Running the following API call against the 1.5.2 instance reveals that the response looks different there:

curl -X GET https://my-phpipam.net/api/apiclient/sections/ --header "token: MY_TOKEN" -i

...
    {
      "id": "1",
      "name": "Customers",
      "description": "Section for customers",
      "masterSection": "0",
      "permissions": "{\"3\":\"1\",\"2\":\"2\"}",
      "strictMode": "1",
      "subnetOrdering": null,
      "order": "3",
      "editDate": "2022-12-22 10:48:16",
      "showSubnet": "1",
      "showVLAN": "0",
      "showVRF": "0",
      "showSupernetOnly": "0",
      "DNS": null
    },

...

steffencircle avatar Feb 04 '24 18:02 steffencircle

Same issue being experienced:

phpipam version = 1.6.0 Terraform version = >1.6 Terraform provider version = 1.5.2

Error: JSON parsing error: json: cannot unmarshal number into Go struct field Section.showVLAN of type string - Response data:

[
    {
        "id": 3,
        "name": "Private IPv4",
        "description": null,
        "masterSection": 0,
        "permissions": "{\"2\":\"2\",\"3\":\"1\",\"6\":\"3\",\"7\":\"2\"}",
        "strictMode": "1",
        "subnetOrdering": "default",
        "order": null,
        "editDate": "2024-02-13 12:26:08",
        "showSubnet": 1,
        "showVLAN": 1,
        "showVRF": 1,
        "showSupernetOnly": 1,
        "DNS": null
    }
]

Swaeltjie avatar Feb 07 '24 09:02 Swaeltjie

I have identified this as a problem with phpipam-sdk-go, specifically: https://github.com/pavel-z1/phpipam-sdk-go/blob/dfc39985162245b0e2c27b7fb61a6be446cdbe42/phpipam/phpipam.go#L91

It is actually a little bit tricky to solve, because the code needs to be able to convert the following JSON values "0", "", 0 to a Go boolean false and vice versa. I cannot figure out how to determine if this should Marshal the Go false back to JSON value 0 or "0" without breaking backwards compatibility.

poulpreben avatar Feb 16 '24 14:02 poulpreben

I think the issue stems from PHP8 (https://www.php.net/manual/en/migration81.incompatible.php#migration81.incompatible.pdo.mysql):

image

phpIPAM PHP MySQL
1.7.0 7.3.34 10.11.6-MariaDB-1:10.11.6+maria~ubu2204
1.7.0 8.3.2 10.11.6-MariaDB-1:10.11.6+maria~ubu2204

PHP 7.3.34

{
  "code": 200,
  "success": true,
  "data": {
    "id": "1",
    "name": "Customers",
    "description": "Section for customers",
    "masterSection": "0",
    "permissions": "{\"3\":\"1\",\"2\":\"2\"}",
    "strictMode": "1",
    "subnetOrdering": null,
    "order": null,
    "editDate": null,
    "showSubnet": "1",
    "showVLAN": "0",
    "showVRF": "0",
    "showSupernetOnly": "0",
    "DNS": null,
    "custom_fields": null
  },
  "time": 0.012
}

PHP 8.3.2

{
  "code": 200,
  "success": true,
  "data": {
    "id": 1,
    "name": "Customers",
    "description": "Section for customers",
    "masterSection": 0,
    "permissions": "{\"3\":\"1\",\"2\":\"2\"}",
    "strictMode": "1",
    "subnetOrdering": null,
    "order": null,
    "editDate": null,
    "showSubnet": 1,
    "showVLAN": 0,
    "showVRF": 0,
    "showSupernetOnly": 0,
    "DNS": null,
    "custom_fields": null
  },
  "time": 0.006
}

d-costa avatar Feb 19 '24 17:02 d-costa

GPT-4 suggests the following: The error message we are seeing suggests that the Terraform provider is trying to unmarshal a JSON number into a Go string field. This is likely due to the changes in PHP8 that @d-costa mentioned, where integers and floats are now returned as native PHP types instead of strings.

The code @poulpreben posted is indeed the part of the SDK that handles the unmarshalling of JSON values into Go types. The BoolIntString and JSONIntString types are used to handle JSON values that can be either a boolean, an integer, or a string.

The UnmarshalJSON method for the BoolIntString type tries to unmarshal the JSON value into a string, and then converts this string to a boolean. If the JSON value is a number, this will fail because a number cannot be unmarshalled into a string.

Similarly, the UnmarshalJSON method for the JSONIntString type tries to unmarshal the JSON value into a string, and then converts this string to an integer. Again, if the JSON value is a number, this will fail because a number cannot be unmarshalled into a string.

To fix this issue, one could modify these methods to first try to unmarshal the JSON value into a number, and if that fails, then try to unmarshal it into a string. Here's how one could modify the UnmarshalJSON method for the BoolIntString type:

func (bis *BoolIntString) UnmarshalJSON(b []byte) error {
    var i int
    if err := json.Unmarshal(b, &i); err == nil {
        *bis = i != 0
        return nil
    }

    var s string
    if err := json.Unmarshal(b, &s); err != nil {
        return err
    }
    switch s {
    case "0", "":
        *bis = false
    case "1":
        *bis = true
    default:
        return &json.UnmarshalTypeError{
            Value: "bool",
            Type:  reflect.ValueOf(s).Type(),
        }
    }

    return nil
}

And here's how you could modify the UnmarshalJSON method for the JSONIntString type:

func (jis *JSONIntString) UnmarshalJSON(b []byte) error {
    var i int
    if err := json.Unmarshal(b, &i); err == nil {
        *jis = JSONIntString(i)
        return nil
    }

    var s string
    if err := json.Unmarshal(b, &s); err != nil {
        return err
    }
    if s == "" {
        *jis = 0
    } else {
        i, err := strconv.Atoi(s)
        if err != nil {
            return &json.UnmarshalTypeError{
                Value: "int",
                Type:  reflect.ValueOf(s).Type(),
            }
        }
        *jis = JSONIntString(i)
    }

    return nil
}

This should allow the SDK to handle JSON values that are either numbers or strings.

Swaeltjie avatar Feb 19 '24 18:02 Swaeltjie

Oh, good catch @d-costa !

This might actually help here

The previous behaviour can be restored by enabling the PDO::ATTR_STRINGIFY_FETCHES option.

I will give this a try and report back !

steffencircle avatar Feb 19 '24 19:02 steffencircle

Although not a long-term solution, updating PHPIPAMs class.PDO.php seems to work as a temporary workaround.

diff --git a/functions/classes/class.PDO.php b/functions/classes/class.PDO.php
index ba2d379e..33f28418 100644
--- a/functions/classes/class.PDO.php
+++ b/functions/classes/class.PDO.php
@@ -189,6 +189,7 @@ abstract class DB {
                        }
 
                        $this->setErrMode(\PDO::ERRMODE_EXCEPTION);
+                       $this->pdo->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES, true);
 
                } catch (\PDOException $e) {
                        throw new Exception ("Could not connect to database! ".$e->getMessage());

dejongm avatar Feb 22 '24 17:02 dejongm

Hey,

i've raised an issue with phpipam itself as i am still wondering if the change in the API responses is intended.

https://github.com/phpipam/phpipam/issues/4043

steffencircle avatar Feb 23 '24 07:02 steffencircle

Hey @steffencircle, do you think the responsibility of managing/masking the different types lies in the phpIPAM app, or in the SDK (phpipam-sdk-go)?

d-costa avatar Feb 23 '24 09:02 d-costa

I just think that phpipam should keep its API stable.

steffencircle avatar Feb 23 '24 10:02 steffencircle

hello,

is there any update on this issue? or is there any workaround to this problem?

ta-yuhyeon avatar May 16 '24 14:05 ta-yuhyeon

Added backward compatibility as phpipam tram described https://github.com/phpipam/phpipam/issues/4043#issuecomment-2093611911

pavel-z1 avatar May 20 '24 05:05 pavel-z1

@pavel-z1 when do you plan to do a new release?

Scholdan avatar May 22 '24 06:05 Scholdan

@scholdan new release will be compiled tonight.

lord-kyron avatar May 22 '24 07:05 lord-kyron

@lord-kyron any updates on the new release?

Scholdan avatar May 31 '24 06:05 Scholdan