terraform-provider-phpipam
terraform-provider-phpipam copied to clipboard
Error when using provider with phpipam Version 1.6.0
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
},
...
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
}
]
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.
I think the issue stems from PHP8 (https://www.php.net/manual/en/migration81.incompatible.php#migration81.incompatible.pdo.mysql):
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
}
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.
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 !
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());
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
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)?
I just think that phpipam should keep its API stable.
hello,
is there any update on this issue? or is there any workaround to this problem?
Added backward compatibility as phpipam tram described https://github.com/phpipam/phpipam/issues/4043#issuecomment-2093611911
@pavel-z1 when do you plan to do a new release?
@scholdan new release will be compiled tonight.
@lord-kyron any updates on the new release?