Repository: gabrieldemarmiesse/python-on-whales (language: python)

Issue:
ParsingError: There was an error parsing the json response from the Docker daemon
I've created stack with `docker.stack.deploy` - in this stack all services are running.
When I run `docker.stack.services("my_stack")` everything is working as expected. 

Then I've deployed a new stack with one broken service! 
And run `docker.stack.services("my_stack")` again. But this time I've got an error

```
--------------------------------------------------------------------
ValidationError                    Traceback (most recent call last)
~/.../updater/venv/lib/python3.7/site-packages/python_on_whales/client_config.py in _fetch_and_parse_inspect_result(self, reference)
    211         try:
--> 212             return self._parse_json_object(json_object)
    213         except pydantic.error_wrappers.ValidationError as err:

~/.../updater/venv/lib/python3.7/site-packages/python_on_whales/components/service.py in _parse_json_object(self, json_object)
    120     def _parse_json_object(self, json_object: Dict[str, Any]) -> ServiceInspectResult:
--> 121         return ServiceInspectResult.parse_obj(json_object)
    122 

~/.../updater/venv/lib/python3.7/site-packages/pydantic/main.cpython-37m-darwin.so in pydantic.main.BaseModel.parse_obj()

~/.../updater/venv/lib/python3.7/site-packages/pydantic/main.cpython-37m-darwin.so in pydantic.main.BaseModel.__init__()

ValidationError: 1 validation error for ServiceInspectResult
PreviousSpec -> UpdateConfig -> Monitor
  field required (type=value_error.missing)
```




Discussion / hints:
Another case: 

```python
lst = docker.stack.ps("my_stack")

print(lst)
"""
[<python_on_whales.components.task.Task object at 0x10df09be0>, <python_on_whales.components.task.Task object at 0x10df09f98>, <python_on_whales.components.task.Task object at 0x10df09d68>, <python_on_whales.components.task.Task object at 0x10df09438>, <python_on_whales.components.task.Task object at 0x10df09358>, <python_on_whales.components.task.Task object at 0x10df09278>, <python_on_whales.components.task.Task object at 0x10df09710>, <python_on_whales.components.task.Task object at 0x10df09ba8>, <python_on_whales.components.task.Task object at 0x10df09ef0>]
"""

for l in lst:
    print(l.name)
    # ERROR
```

```
ValidationError                           Traceback (most recent call last)
~/.../updater/venv/lib/python3.7/site-packages/python_on_whales/client_config.py in _fetch_and_parse_inspect_result(self, reference)
    211         try:
--> 212             return self._parse_json_object(json_object)
    213         except pydantic.error_wrappers.ValidationError as err:

~/.../updater/venv/lib/python3.7/site-packages/python_on_whales/components/task.py in _parse_json_object(self, json_object)
    178     def _parse_json_object(self, json_object: Dict[str, Any]) -> TaskInspectResult:
--> 179         return TaskInspectResult.parse_obj(json_object)
    180 

~/.../updater/venv/lib/python3.7/site-packages/pydantic/main.cpython-37m-darwin.so in pydantic.main.BaseModel.parse_obj()

~/.../updater/venv/lib/python3.7/site-packages/pydantic/main.cpython-37m-darwin.so in pydantic.main.BaseModel.__init__()

ValidationError: 1 validation error for TaskInspectResult
Spec -> Placement -> Platforms
  field required (type=value_error.missing)

```




Thank you very much for the bug report. This helps a lot to improve the package! This seems easy to fix. I'll work on it you can expect a bugfix release tomorrow or the day after :) 

Fix patch:
diff --git a/python_on_whales/components/image.py b/python_on_whales/components/image.py
index ca3fa94c..9252df6e 100644
--- a/python_on_whales/components/image.py
+++ b/python_on_whales/components/image.py
@@ -61,7 +61,7 @@ class ImageInspectResult(DockerCamelModel):
     virtual_size: int
     graph_driver: ImageGraphDriver
     root_fs: ImageRootFS
-    metadata: Dict[str, str]
+    metadata: Optional[Dict[str, str]]
 
 
 class Image(ReloadableObjectFromJson):
@@ -159,7 +159,7 @@ def root_fs(self) -> ImageRootFS:
         return self._get_inspect_result().root_fs
 
     @property
-    def metadata(self) -> Dict[str, str]:
+    def metadata(self) -> Optional[Dict[str, str]]:
         return self._get_inspect_result().metadata
 
     def remove(self, force: bool = False, prune: bool = True):
diff --git a/python_on_whales/components/network.py b/python_on_whales/components/network.py
index 156a132a..eb277027 100644
--- a/python_on_whales/components/network.py
+++ b/python_on_whales/components/network.py
@@ -40,8 +40,8 @@ class NetworkInspectResult(DockerCamelModel):
     containers: Dict[str, NetworkContainer]
     options: Dict[str, Any]
     labels: Dict[str, str]
-    config_from: dict
-    config_only: bool
+    config_from: Optional[dict]
+    config_only: Optional[bool]
 
 
 class Network(ReloadableObjectFromJson):
diff --git a/python_on_whales/components/service.py b/python_on_whales/components/service.py
index 0ad550a8..7d503b90 100644
--- a/python_on_whales/components/service.py
+++ b/python_on_whales/components/service.py
@@ -30,7 +30,7 @@ class ContainerSpec(DockerCamelModel):
     labels: Optional[Dict[str, str]]
     privileges: Optional[Dict[str, Optional[str]]]
     stop_grace_period: Optional[int]
-    isolation: str
+    isolation: Optional[str]
     env: Optional[List[str]]
 
 
@@ -42,14 +42,14 @@ class TaskTemplate(DockerCamelModel):
 class ChangeConfig(DockerCamelModel):
     parallelism: int
     failure_action: str
-    monitor: int
-    max_failure_ratio: int
-    order: str
+    monitor: Optional[int]
+    max_failure_ratio: Optional[int]
+    order: Optional[str]
 
 
 class ServiceSpec(DockerCamelModel):
     name: str
-    labels: Dict[str, str]
+    labels: Optional[Dict[str, str]]
     mode: Optional[Dict[str, Any]]
     update_config: Optional[ChangeConfig]
     rollback_config: Optional[ChangeConfig]
@@ -65,7 +65,7 @@ class EndpointPortConfig(DockerCamelModel):
     protocol: str
     target_port: int
     published_port: Optional[int]
-    publish_mode: str
+    publish_mode: Optional[str]
 
 
 class ServiceEndpointSpec(DockerCamelModel):
diff --git a/python_on_whales/components/task.py b/python_on_whales/components/task.py
index d031e683..f78c4454 100644
--- a/python_on_whales/components/task.py
+++ b/python_on_whales/components/task.py
@@ -29,14 +29,14 @@ class DiscreteResourceSpec(DockerCamelModel):
 
 
 class AssignedGenericResources(DockerCamelModel):
-    named_resource_spec: NamedResourceSpec
-    discrete_resource_spec: DiscreteResourceSpec
+    named_resource_spec: Optional[NamedResourceSpec]
+    discrete_resource_spec: Optional[DiscreteResourceSpec]
 
 
 class ContainerStatus(DockerCamelModel):
     container_id: str = pydantic.Field(alias="ContainerID")
     pid: int = pydantic.Field(alias="PID")
-    exit_code: int
+    exit_code: Optional[int]
 
 
 class TaskStatus(DockerCamelModel):
@@ -71,7 +71,7 @@ class Placement(DockerCamelModel):
     constraints: Optional[List[str]]
     preferences: Optional[List[Spread]]
     max_replicas: Optional[int]
-    platforms: List[Platform]
+    platforms: Optional[List[Platform]]
 
 
 class RestartPolicy(DockerCamelModel):
@@ -144,7 +144,7 @@ class TaskSpec(DockerCamelModel):
     resources: Resources
     restart_policy: Any
     placement: Placement
-    force_update: int
+    force_update: Optional[int]
     runtime: Optional[str]
     networks: Optional[List[NetworkAttachmentConfig]]
     log_driver: Optional[LogDriver]
@@ -156,7 +156,7 @@ class TaskInspectResult(DockerCamelModel):
     created_at: datetime
     updated_at: datetime
     name: Optional[str]
-    labels: Dict[str, str]
+    labels: Optional[Dict[str, str]]
     spec: TaskSpec
     service_id: str = pydantic.Field(alias="ServiceID")
     slot: Optional[int]


Test patch:
diff --git a/tests/python_on_whales/components/jsons/images/8.json b/tests/python_on_whales/components/jsons/images/8.json
new file mode 100644
index 00000000..dc4022d3
--- /dev/null
+++ b/tests/python_on_whales/components/jsons/images/8.json
@@ -0,0 +1,90 @@
+{
+  "Id": "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c",
+  "Container": "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a",
+  "Comment": "",
+  "Os": "linux",
+  "Architecture": "amd64",
+  "Parent": "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c",
+  "ContainerConfig": {
+    "Tty": false,
+    "Hostname": "e611e15f9c9d",
+    "Domainname": "",
+    "AttachStdout": false,
+    "PublishService": "",
+    "AttachStdin": false,
+    "OpenStdin": false,
+    "StdinOnce": false,
+    "NetworkDisabled": false,
+    "OnBuild": [],
+    "Image": "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c",
+    "User": "",
+    "WorkingDir": "",
+    "MacAddress": "",
+    "AttachStderr": false,
+    "Labels": {
+      "com.example.license": "GPL",
+      "com.example.version": "1.0",
+      "com.example.vendor": "Acme"
+    },
+    "Env": [
+      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+    ],
+    "Cmd": [
+      "/bin/sh",
+      "-c",
+      "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0"
+    ]
+  },
+  "DockerVersion": "1.9.0-dev",
+  "VirtualSize": 188359297,
+  "Size": 0,
+  "Author": "",
+  "Created": "2015-09-10T08:30:53.26995814Z",
+  "GraphDriver": {
+    "Name": "aufs",
+    "Data": {}
+  },
+  "RepoDigests": [
+    "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf"
+  ],
+  "RepoTags": [
+    "example:1.0",
+    "example:latest",
+    "example:stable"
+  ],
+  "Config": {
+    "Image": "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c",
+    "NetworkDisabled": false,
+    "OnBuild": [],
+    "StdinOnce": false,
+    "PublishService": "",
+    "AttachStdin": false,
+    "OpenStdin": false,
+    "Domainname": "",
+    "AttachStdout": false,
+    "Tty": false,
+    "Hostname": "e611e15f9c9d",
+    "Cmd": [
+      "/bin/bash"
+    ],
+    "Env": [
+      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+    ],
+    "Labels": {
+      "com.example.vendor": "Acme",
+      "com.example.version": "1.0",
+      "com.example.license": "GPL"
+    },
+    "MacAddress": "",
+    "AttachStderr": false,
+    "WorkingDir": "",
+    "User": ""
+  },
+  "RootFS": {
+    "Type": "layers",
+    "Layers": [
+      "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6",
+      "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/tests/python_on_whales/components/jsons/networks/7.json b/tests/python_on_whales/components/jsons/networks/7.json
new file mode 100644
index 00000000..31cb1238
--- /dev/null
+++ b/tests/python_on_whales/components/jsons/networks/7.json
@@ -0,0 +1,44 @@
+{
+  "Name": "net01",
+  "Id": "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99",
+  "Created": "2016-10-19T04:33:30.360899459Z",
+  "Scope": "local",
+  "Driver": "bridge",
+  "EnableIPv6": false,
+  "IPAM": {
+    "Driver": "default",
+    "Config": [
+      {
+        "Subnet": "172.19.0.0/16",
+        "Gateway": "172.19.0.1"
+      }
+    ],
+    "Options": {
+      "foo": "bar"
+    }
+  },
+  "Internal": false,
+  "Attachable": false,
+  "Ingress": false,
+  "Containers": {
+    "19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c": {
+      "Name": "test",
+      "EndpointID": "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a",
+      "MacAddress": "02:42:ac:13:00:02",
+      "IPv4Address": "172.19.0.2/16",
+      "IPv6Address": ""
+    }
+  },
+  "Options": {
+    "com.docker.network.bridge.default_bridge": "true",
+    "com.docker.network.bridge.enable_icc": "true",
+    "com.docker.network.bridge.enable_ip_masquerade": "true",
+    "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
+    "com.docker.network.bridge.name": "docker0",
+    "com.docker.network.driver.mtu": "1500"
+  },
+  "Labels": {
+    "com.example.some-label": "some-value",
+    "com.example.some-other-label": "some-other-value"
+  }
+}
\ No newline at end of file
diff --git a/tests/python_on_whales/components/jsons/services/10.json b/tests/python_on_whales/components/jsons/services/10.json
new file mode 100644
index 00000000..56e8ce65
--- /dev/null
+++ b/tests/python_on_whales/components/jsons/services/10.json
@@ -0,0 +1,185 @@
+{
+    "ID": "uiwnxpdl7z6dui1yl003lxz27",
+    "Version": {
+        "Index": 5859782
+    },
+    "CreatedAt": "2020-11-23T18:30:58.478269584Z",
+    "UpdatedAt": "2020-12-18T15:58:35.741139242Z",
+    "Spec": {
+        "Name": "swarmpit_db",
+        "Labels": {
+            "com.docker.stack.image": "766286138643.dkr.ecr.eu-west-1.amazonaws.com/couchdb:2.3.0",
+            "com.docker.stack.namespace": "swarmpit"
+        },
+        "TaskTemplate": {
+            "ContainerSpec": {
+                "Image": "766286138643.dkr.ecr.eu-west-1.amazonaws.com/couchdb:2.3.0@sha256:4b63800b20f5457673fef2fb2c95f45aea8a7edc1f79638ac0c2c9cab2094b06",
+                "Labels": {
+                    "com.docker.stack.namespace": "swarmpit"
+                },
+                "Privileges": {
+                    "CredentialSpec": null,
+                    "SELinuxContext": null
+                },
+                "Mounts": [
+                    {
+                        "Type": "volume",
+                        "Source": "swarmpit_db-data",
+                        "Target": "/opt/couchdb/data",
+                        "VolumeOptions": {
+                            "Labels": {
+                                "com.docker.stack.namespace": "swarmpit"
+                            },
+                            "DriverConfig": {
+                                "Name": "local"
+                            }
+                        }
+                    }
+                ],
+                "StopGracePeriod": 10000000000,
+                "DNSConfig": {},
+                "Isolation": "default"
+            },
+            "Resources": {
+                "Limits": {
+                    "NanoCPUs": 1000000000,
+                    "MemoryBytes": 1073741824
+                },
+                "Reservations": {
+                    "NanoCPUs": 400000000,
+                    "MemoryBytes": 536870912
+                }
+            },
+            "RestartPolicy": {
+                "Condition": "any",
+                "Delay": 5000000000,
+                "MaxAttempts": 0
+            },
+            "Placement": {
+                "Constraints": [
+                    "node.labels.has-swarmpit-db-data==1"
+                ],
+                "Platforms": [
+                    {
+                        "Architecture": "amd64",
+                        "OS": "linux"
+                    }
+                ]
+            },
+            "Networks": [
+                {
+                    "Target": "x4ymj7n0ul3xd8m1naztp3kqh",
+                    "Aliases": [
+                        "db"
+                    ]
+                }
+            ],
+            "ForceUpdate": 0,
+            "Runtime": "container"
+        },
+        "Mode": {
+            "Replicated": {
+                "Replicas": 1
+            }
+        },
+        "UpdateConfig": {
+            "Parallelism": 1,
+            "FailureAction": "pause"
+        },
+        "RollbackConfig": {
+            "Parallelism": 1,
+            "FailureAction": "pause",
+            "Monitor": 5000000000,
+            "MaxFailureRatio": 0,
+            "Order": "stop-first"
+        },
+        "EndpointSpec": {
+            "Mode": "vip"
+        }
+    },
+    "PreviousSpec": {
+        "Name": "swarmpit_db",
+        "Labels": {
+            "com.docker.stack.image": "766286138643.dkr.ecr.eu-west-1.amazonaws.com/couchdb:2.3.0",
+            "com.docker.stack.namespace": "swarmpit"
+        },
+        "TaskTemplate": {
+            "ContainerSpec": {
+                "Image": "766286138643.dkr.ecr.eu-west-1.amazonaws.com/couchdb:2.3.0@sha256:4b63800b20f5457673fef2fb2c95f45aea8a7edc1f79638ac0c2c9cab2094b06",
+                "Labels": {
+                    "com.docker.stack.namespace": "swarmpit"
+                },
+                "Privileges": {
+                    "CredentialSpec": null,
+                    "SELinuxContext": null
+                },
+                "Mounts": [
+                    {
+                        "Type": "volume",
+                        "Source": "swarmpit_db-data",
+                        "Target": "/opt/couchdb/data",
+                        "VolumeOptions": {
+                            "Labels": {
+                                "com.docker.stack.namespace": "swarmpit"
+                            },
+                            "DriverConfig": {
+                                "Name": "local"
+                            }
+                        }
+                    }
+                ],
+                "Isolation": "default"
+            },
+            "Resources": {
+                "Limits": {
+                    "NanoCPUs": 1000000000,
+                    "MemoryBytes": 1073741824
+                },
+                "Reservations": {
+                    "NanoCPUs": 400000000,
+                    "MemoryBytes": 536870912
+                }
+            },
+            "Placement": {
+                "Constraints": [
+                    "node.labels.has-swarmpit-db-data==1"
+                ],
+                "Platforms": [
+                    {
+                        "Architecture": "amd64",
+                        "OS": "linux"
+                    }
+                ]
+            },
+            "Networks": [
+                {
+                    "Target": "x4ymj7n0ul3xd8m1naztp3kqh",
+                    "Aliases": [
+                        "db"
+                    ]
+                }
+            ],
+            "ForceUpdate": 0,
+            "Runtime": "container"
+        },
+        "Mode": {
+            "Replicated": {
+                "Replicas": 1
+            }
+        },
+        "EndpointSpec": {
+            "Mode": "vip"
+        }
+    },
+    "Endpoint": {
+        "Spec": {
+            "Mode": "vip"
+        },
+        "VirtualIPs": [
+            {
+                "NetworkID": "x4ymj7n0ul3xd8m1naztp3kqh",
+                "Addr": "10.0.1.112/24"
+            }
+        ]
+    }
+}
diff --git a/tests/python_on_whales/components/jsons/services/9.json b/tests/python_on_whales/components/jsons/services/9.json
new file mode 100644
index 00000000..12eaf92e
--- /dev/null
+++ b/tests/python_on_whales/components/jsons/services/9.json
@@ -0,0 +1,84 @@
+{
+  "ID": "9mnpnzenvg8p8tdbtq4wvbkcz",
+  "Version": {
+    "Index": 19
+  },
+  "CreatedAt": "2016-06-07T21:05:51.880065305Z",
+  "UpdatedAt": "2016-06-07T21:07:29.962229872Z",
+  "Spec": {
+    "Name": "hopeful_cori",
+    "TaskTemplate": {
+      "ContainerSpec": {
+        "Image": "redis"
+      },
+      "Resources": {
+        "Limits": {},
+        "Reservations": {}
+      },
+      "RestartPolicy": {
+        "Condition": "any",
+        "MaxAttempts": 0
+      },
+      "Placement": {},
+      "ForceUpdate": 0
+    },
+    "Mode": {
+      "Replicated": {
+        "Replicas": 1
+      }
+    },
+    "UpdateConfig": {
+      "Parallelism": 1,
+      "Delay": 1000000000,
+      "FailureAction": "pause",
+      "Monitor": 15000000000,
+      "MaxFailureRatio": 0.15
+    },
+    "RollbackConfig": {
+      "Parallelism": 1,
+      "Delay": 1000000000,
+      "FailureAction": "pause",
+      "Monitor": 15000000000,
+      "MaxFailureRatio": 0.15
+    },
+    "EndpointSpec": {
+      "Mode": "vip",
+      "Ports": [
+        {
+          "Protocol": "tcp",
+          "TargetPort": 6379,
+          "PublishedPort": 30001
+        }
+      ]
+    }
+  },
+  "Endpoint": {
+    "Spec": {
+      "Mode": "vip",
+      "Ports": [
+        {
+          "Protocol": "tcp",
+          "TargetPort": 6379,
+          "PublishedPort": 30001
+        }
+      ]
+    },
+    "Ports": [
+      {
+        "Protocol": "tcp",
+        "TargetPort": 6379,
+        "PublishedPort": 30001
+      }
+    ],
+    "VirtualIPs": [
+      {
+        "NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
+        "Addr": "10.255.0.2/16"
+      },
+      {
+        "NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
+        "Addr": "10.255.0.3/16"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/tests/python_on_whales/components/jsons/tasks/3.json b/tests/python_on_whales/components/jsons/tasks/3.json
new file mode 100644
index 00000000..b8d68997
--- /dev/null
+++ b/tests/python_on_whales/components/jsons/tasks/3.json
@@ -0,0 +1,103 @@
+{
+  "ID": "0kzzo1i0y4jz6027t0k7aezc7",
+  "Version": {
+    "Index": 71
+  },
+  "CreatedAt": "2016-06-07T21:07:31.171892745Z",
+  "UpdatedAt": "2016-06-07T21:07:31.376370513Z",
+  "Spec": {
+    "ContainerSpec": {
+      "Image": "redis"
+    },
+    "Resources": {
+      "Limits": {},
+      "Reservations": {}
+    },
+    "RestartPolicy": {
+      "Condition": "any",
+      "MaxAttempts": 0
+    },
+    "Placement": {}
+  },
+  "ServiceID": "9mnpnzenvg8p8tdbtq4wvbkcz",
+  "Slot": 1,
+  "NodeID": "60gvrl6tm78dmak4yl7srz94v",
+  "Status": {
+    "Timestamp": "2016-06-07T21:07:31.290032978Z",
+    "State": "running",
+    "Message": "started",
+    "ContainerStatus": {
+      "ContainerID": "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035",
+      "PID": 677
+    }
+  },
+  "DesiredState": "running",
+  "NetworksAttachments": [
+    {
+      "Network": {
+        "ID": "4qvuz4ko70xaltuqbt8956gd1",
+        "Version": {
+          "Index": 18
+        },
+        "CreatedAt": "2016-06-07T20:31:11.912919752Z",
+        "UpdatedAt": "2016-06-07T21:07:29.955277358Z",
+        "Spec": {
+          "Name": "ingress",
+          "Labels": {
+            "com.docker.swarm.internal": "true"
+          },
+          "DriverConfiguration": {},
+          "IPAMOptions": {
+            "Driver": {},
+            "Configs": [
+              {
+                "Subnet": "10.255.0.0/16",
+                "Gateway": "10.255.0.1"
+              }
+            ]
+          }
+        },
+        "DriverState": {
+          "Name": "overlay",
+          "Options": {
+            "com.docker.network.driver.overlay.vxlanid_list": "256"
+          }
+        },
+        "IPAMOptions": {
+          "Driver": {
+            "Name": "default"
+          },
+          "Configs": [
+            {
+              "Subnet": "10.255.0.0/16",
+              "Gateway": "10.255.0.1"
+            }
+          ]
+        }
+      },
+      "Addresses": [
+        "10.255.0.10/16"
+      ]
+    }
+  ],
+  "AssignedGenericResources": [
+    {
+      "DiscreteResourceSpec": {
+        "Kind": "SSD",
+        "Value": 3
+      }
+    },
+    {
+      "NamedResourceSpec": {
+        "Kind": "GPU",
+        "Value": "UUID1"
+      }
+    },
+    {
+      "NamedResourceSpec": {
+        "Kind": "GPU",
+        "Value": "UUID2"
+      }
+    }
+  ]
+}
diff --git a/tests/python_on_whales/components/jsons/tasks/4.json b/tests/python_on_whales/components/jsons/tasks/4.json
new file mode 100644
index 00000000..93d68b1b
--- /dev/null
+++ b/tests/python_on_whales/components/jsons/tasks/4.json
@@ -0,0 +1,166 @@
+{
+    "ID": "jryqsdcv9fjnzu77nizl3bqdl",
+    "Version": {
+        "Index": 86
+    },
+    "CreatedAt": "2021-01-09T08:52:33.0630141Z",
+    "UpdatedAt": "2021-01-09T08:54:01.2050251Z",
+    "Labels": {},
+    "Spec": {
+        "ContainerSpec": {
+            "Image": "swarmpit/swarmpit:latest@sha256:5cb19168dbe10fc10672e760ff0bb002b3302959eae308968551f7446c40e16d",
+            "Labels": {
+                "com.docker.stack.namespace": "dodo"
+            },
+            "Env": [
+                "SWARMPIT_DB=http://db:5984",
+                "SWARMPIT_INFLUXDB=http://influxdb:8086"
+            ],
+            "Privileges": {
+                "CredentialSpec": null,
+                "SELinuxContext": null
+            },
+            "Mounts": [
+                {
+                    "Type": "bind",
+                    "Source": "/var/run/docker.sock",
+                    "Target": "/var/run/docker.sock",
+                    "ReadOnly": true
+                }
+            ],
+            "Healthcheck": {
+                "Test": [
+                    "CMD",
+                    "curl",
+                    "-f",
+                    "http://localhost:8080"
+                ],
+                "Interval": 60000000000,
+                "Timeout": 10000000000,
+                "Retries": 3
+            },
+            "Isolation": "default"
+        },
+        "Resources": {
+            "Limits": {
+                "NanoCPUs": 500000000,
+                "MemoryBytes": 1073741824
+            },
+            "Reservations": {
+                "NanoCPUs": 250000000,
+                "MemoryBytes": 536870912
+            }
+        },
+        "Placement": {
+            "Constraints": [
+                "node.role == manager"
+            ]
+        },
+        "Networks": [
+            {
+                "Target": "afg48vk96tgv2k947sybsoqhj",
+                "Aliases": [
+                    "app"
+                ]
+            }
+        ],
+        "ForceUpdate": 0
+    },
+    "ServiceID": "hadeznbtwt4xf8z3m0fzcubak",
+    "Slot": 1,
+    "NodeID": "n5xpfqe9gmktcyirgx1dvkwtu",
+    "Status": {
+        "Timestamp": "2021-01-09T08:54:01.1664046Z",
+        "State": "running",
+        "Message": "started",
+        "ContainerStatus": {
+            "ContainerID": "a0fc27c1c2bbe2ad5312d7bbc4e62b6d51eb0e1ebb6b8a4a6c52c2ed790ea0f3",
+            "PID": 17633,
+            "ExitCode": 0
+        },
+        "PortStatus": {}
+    },
+    "DesiredState": "running",
+    "NetworksAttachments": [
+        {
+            "Network": {
+                "ID": "l56f582r49vn4vp6e1cvs6gm9",
+                "Version": {
+                    "Index": 40
+                },
+                "CreatedAt": "2021-01-07T07:24:28.1225588Z",
+                "UpdatedAt": "2021-01-09T07:37:36.0983587Z",
+                "Spec": {
+                    "Name": "ingress",
+                    "Labels": {},
+                    "DriverConfiguration": {},
+                    "Ingress": true,
+                    "IPAMOptions": {
+                        "Driver": {}
+                    },
+                    "Scope": "swarm"
+                },
+                "DriverState": {
+                    "Name": "overlay",
+                    "Options": {
+                        "com.docker.network.driver.overlay.vxlanid_list": "4096"
+                    }
+                },
+                "IPAMOptions": {
+                    "Driver": {
+                        "Name": "default"
+                    },
+                    "Configs": [
+                        {
+                            "Subnet": "10.0.0.0/24",
+                            "Gateway": "10.0.0.1"
+                        }
+                    ]
+                }
+            },
+            "Addresses": [
+                "10.0.0.4/24"
+            ]
+        },
+        {
+            "Network": {
+                "ID": "afg48vk96tgv2k947sybsoqhj",
+                "Version": {
+                    "Index": 53
+                },
+                "CreatedAt": "2021-01-09T08:52:25.0892136Z",
+                "UpdatedAt": "2021-01-09T08:52:25.0908479Z",
+                "Spec": {
+                    "Name": "dodo_net",
+                    "Labels": {
+                        "com.docker.stack.namespace": "dodo"
+                    },
+                    "DriverConfiguration": {
+                        "Name": "overlay"
+                    },
+                    "Scope": "swarm"
+                },
+                "DriverState": {
+                    "Name": "overlay",
+                    "Options": {
+                        "com.docker.network.driver.overlay.vxlanid_list": "4097"
+                    }
+                },
+                "IPAMOptions": {
+                    "Driver": {
+                        "Name": "default"
+                    },
+                    "Configs": [
+                        {
+                            "Subnet": "10.0.1.0/24",
+                            "Gateway": "10.0.1.1"
+                        }
+                    ]
+                }
+            },
+            "Addresses": [
+                "10.0.1.10/24"
+            ]
+        }
+    ]
+}