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

Issue:
docker.stack.ps: ValidationError
Version: 0.14.0

Hello! One more bug found. 

So I have a stack and one of services is broken:

```
 ➜  0.3.0118 git:(master) ✗ docker stack ps test1
ID             NAME                    IMAGE                                              NODE             DESIRED STATE   CURRENT STATE             ERROR                              PORTS
jmzs1ntvjxd6   test1_name1.1   name1:2.3.0131                    Running         Pending 43 seconds ago    "no suitable node (scheduling …"
...
```

I've tried to do next

```python
for l in docker.stack.ps("test1"):
    print(l.id). # jmzs1ntvjxd6...
    print(' l', l.labels). # ERROR!
```

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
NodeID
  field required (type=value_error.missing)

```



Discussion / hints:
The same issue with 

```
docker.stack.ps("test1")[0].status
docker.stack.ps("test1")[0].spec
etc
```
Thanks, it would help a lot if you could give me the result of  `docker inspect <task-id>`. I could then put that in the unit tests and make sure it works correctly

Just make sure to remove any secrets/passwords
Sure 

```
[
    {
        "ID": "f7tpoi3my38p0vk1j96sbhalf",
        "Version": {
            "Index": 1058705
        },
        "CreatedAt": "2021-02-19T14:07:51.1034515Z",
        "UpdatedAt": "2021-02-19T14:07:51.2120791Z",
        "Labels": {},
        "Spec": {
            "ContainerSpec": {
                "Image": "test:2.3.0131",
                "Labels": {
                    "com.docker.stack.namespace": "test1"
                },
                "Env": [
                    "DOMAIN=localhost",
                    "STACK_NAME=test1",
                    "discovery.type=single-node"
                ],
                "Privileges": {
                    "CredentialSpec": null,
                    "SELinuxContext": null
                },
                "Mounts": [
                    {
                        "Type": "volume",
                        "Source": "test1_app-es-data",
                        "Target": "/usr/share/elasticsearch/data",
                        "VolumeOptions": {
                            "Labels": {
                                "com.docker.stack.namespace": "test1"
                            }
                        }
                    }
                ],
                "Isolation": "default",
                "Ulimits": [
                    {
                        "Name": "memlock",
                        "Hard": -1,
                        "Soft": -1
                    }
                ]
            },
            "Resources": {},
            "RestartPolicy": {
                "Condition": "on-failure",
                "Delay": 5000000000,
                "MaxAttempts": 0
            },
            "Placement": {
                "Constraints": [
                    "node.labels.test1.app-es-data == true"
                ]
            },
            "Networks": [
                {
                    "Target": "orlu6efwwazflqzaolup1v960",
                    "Aliases": [
                        "elasticsearch"
                    ]
                }
            ],
            "ForceUpdate": 0
        },
        "ServiceID": "rkys3vcykrycd0cwb9myt7wp7",
        "Slot": 1,
        "Status": {
            "Timestamp": "2021-02-19T14:07:51.2118683Z",
            "State": "pending",
            "Message": "pending task scheduling",
            "Err": "no suitable node (scheduling constraints not satisfied on 1 node)",
            "PortStatus": {}
        },
        "DesiredState": "running",
        "NetworksAttachments": [
            {
                "Network": {
                    "ID": "orlu6efwwazflqzaolup1v960",
                    "Version": {
                        "Index": 1058665
                    },
                    "CreatedAt": "2021-02-19T14:07:26.4206155Z",
                    "UpdatedAt": "2021-02-19T14:07:26.4264181Z",
                    "Spec": {
                        "Name": "test1_default",
                        "Labels": {
                            "com.docker.stack.namespace": "test1"
                        },
                        "DriverConfiguration": {
                            "Name": "overlay"
                        },
                        "Scope": "swarm"
                    },
                    "DriverState": {
                        "Name": "overlay",
                        "Options": {
                            "com.docker.network.driver.overlay.vxlanid_list": "4113"
                        }
                    },
                    "IPAMOptions": {
                        "Driver": {
                            "Name": "default"
                        },
                        "Configs": [
                            {
                                "Subnet": "10.0.17.0/24",
                                "Gateway": "10.0.17.1"
                            }
                        ]
                    }
                },
                "Addresses": [
                    "10.0.17.12/24"
                ]
            }
        ]
    }
]
```
Great! Thanks! I'll work on a fix. This helps a lot. This will go right to the unit tests.

Fix patch:
diff --git a/python_on_whales/components/task.py b/python_on_whales/components/task.py
index f78c4454..df101b41 100644
--- a/python_on_whales/components/task.py
+++ b/python_on_whales/components/task.py
@@ -160,7 +160,7 @@ class TaskInspectResult(DockerCamelModel):
     spec: TaskSpec
     service_id: str = pydantic.Field(alias="ServiceID")
     slot: Optional[int]
-    node_id: str = pydantic.Field(alias="NodeID")
+    node_id: Optional[str] = pydantic.Field(alias="NodeID")
     assigned_generic_resources: Optional[List[AssignedGenericResources]]
     status: TaskStatus
     desired_state: str


Test patch:
diff --git a/tests/python_on_whales/components/jsons/tasks/5.json b/tests/python_on_whales/components/jsons/tasks/5.json
new file mode 100644
index 00000000..4e6b9aba
--- /dev/null
+++ b/tests/python_on_whales/components/jsons/tasks/5.json
@@ -0,0 +1,118 @@
+{
+    "ID": "f7tpoi3my38p0vk1j96sbhalf",
+    "Version": {
+        "Index": 1058705
+    },
+    "CreatedAt": "2021-02-19T14:07:51.1034515Z",
+    "UpdatedAt": "2021-02-19T14:07:51.2120791Z",
+    "Labels": {},
+    "Spec": {
+        "ContainerSpec": {
+            "Image": "test:2.3.0131",
+            "Labels": {
+                "com.docker.stack.namespace": "test1"
+            },
+            "Env": [
+                "DOMAIN=localhost",
+                "STACK_NAME=test1",
+                "discovery.type=single-node"
+            ],
+            "Privileges": {
+                "CredentialSpec": null,
+                "SELinuxContext": null
+            },
+            "Mounts": [
+                {
+                    "Type": "volume",
+                    "Source": "test1_app-es-data",
+                    "Target": "/usr/share/elasticsearch/data",
+                    "VolumeOptions": {
+                        "Labels": {
+                            "com.docker.stack.namespace": "test1"
+                        }
+                    }
+                }
+            ],
+            "Isolation": "default",
+            "Ulimits": [
+                {
+                    "Name": "memlock",
+                    "Hard": -1,
+                    "Soft": -1
+                }
+            ]
+        },
+        "Resources": {},
+        "RestartPolicy": {
+            "Condition": "on-failure",
+            "Delay": 5000000000,
+            "MaxAttempts": 0
+        },
+        "Placement": {
+            "Constraints": [
+                "node.labels.test1.app-es-data == true"
+            ]
+        },
+        "Networks": [
+            {
+                "Target": "orlu6efwwazflqzaolup1v960",
+                "Aliases": [
+                    "elasticsearch"
+                ]
+            }
+        ],
+        "ForceUpdate": 0
+    },
+    "ServiceID": "rkys3vcykrycd0cwb9myt7wp7",
+    "Slot": 1,
+    "Status": {
+        "Timestamp": "2021-02-19T14:07:51.2118683Z",
+        "State": "pending",
+        "Message": "pending task scheduling",
+        "Err": "no suitable node (scheduling constraints not satisfied on 1 node)",
+        "PortStatus": {}
+    },
+    "DesiredState": "running",
+    "NetworksAttachments": [
+        {
+            "Network": {
+                "ID": "orlu6efwwazflqzaolup1v960",
+                "Version": {
+                    "Index": 1058665
+                },
+                "CreatedAt": "2021-02-19T14:07:26.4206155Z",
+                "UpdatedAt": "2021-02-19T14:07:26.4264181Z",
+                "Spec": {
+                    "Name": "test1_default",
+                    "Labels": {
+                        "com.docker.stack.namespace": "test1"
+                    },
+                    "DriverConfiguration": {
+                        "Name": "overlay"
+                    },
+                    "Scope": "swarm"
+                },
+                "DriverState": {
+                    "Name": "overlay",
+                    "Options": {
+                        "com.docker.network.driver.overlay.vxlanid_list": "4113"
+                    }
+                },
+                "IPAMOptions": {
+                    "Driver": {
+                        "Name": "default"
+                    },
+                    "Configs": [
+                        {
+                            "Subnet": "10.0.17.0/24",
+                            "Gateway": "10.0.17.1"
+                        }
+                    ]
+                }
+            },
+            "Addresses": [
+                "10.0.17.12/24"
+            ]
+        }
+    ]
+}
\ No newline at end of file