Repository: SaaShup/netbox-docker-plugin (language: python)

Issue:
Anonymous Volume breaks webhook update
| Netbox version | Plugin Version | Agent version |  Docker API Version |
| 3.7.0 | 1.5.2 | 0.17.2 | 1.42 |

## Actual Behavior
When I create/recreate/restart/stop/start a container there is PATCH request send back to from the agent to the plugin to update status and any other information (new Mounts, Labels )

The webhook update will not work if there is an anonymous volume being created by the container.
Adding a mount will not work as it would required the creation of the volume before.

## Desired Behavior
The best option would be the plugin to manage it. It would be possible in the agent to implement the logic but that would mean managing the complete entity management of a container. 

Objectives is to create a container with anonymous volume.
To reproduce the issue you can simple create a hachicorp/vault:latest container and recreate it.

Cheers



Discussion / hints:

Fix patch:
diff --git a/netbox_docker_plugin/__init__.py b/netbox_docker_plugin/__init__.py
index 7c2bd19..77749f5 100644
--- a/netbox_docker_plugin/__init__.py
+++ b/netbox_docker_plugin/__init__.py
@@ -10,7 +10,7 @@ class NetBoxDockerConfig(PluginConfig):
     name = "netbox_docker_plugin"
     verbose_name = " NetBox Docker Plugin"
     description = "Manage Docker"
-    version = "1.6.0"
+    version = "1.6.1"
     base_url = "docker"
     author= "Vincent Simonin"
     author_email= "vincent@saashup.com"
diff --git a/netbox_docker_plugin/api/serializers.py b/netbox_docker_plugin/api/serializers.py
index 0dd43d5..dfaa7fa 100644
--- a/netbox_docker_plugin/api/serializers.py
+++ b/netbox_docker_plugin/api/serializers.py
@@ -3,6 +3,7 @@
 # pylint: disable=E1101
 
 from rest_framework import serializers
+from utilities.utils import dict_to_filter_params
 from users.api.nested_serializers import NestedTokenSerializer
 from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer
 from ..models.host import Host
@@ -86,6 +87,20 @@ class Meta:
             "driver",
         )
 
+    def to_internal_value(self, data):
+        if data is None:
+            return None
+
+        if isinstance(data, dict):
+            params = dict_to_filter_params(data)
+            if Volume.objects.filter(**params).count() == 0:
+                host = Host.objects.get(pk=params["host"])
+                volume = Volume(host= host, name= params["name"])
+                volume.save()
+                return volume
+
+        return super().to_internal_value(data)
+
 
 class NestedNetworkSerializer(WritableNestedSerializer):
     """Nested Network Serializer class"""
diff --git a/pyproject.toml b/pyproject.toml
index 556e92f..23dcbcd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "netbox-docker-plugin"
-version = "1.6.0"
+version = "1.6.1"
 authors = [
   { name="Vincent Simonin", email="vincent@saashup.com" },
 ]


Test patch:
diff --git a/netbox_docker_plugin/tests/volume/test_volume_api.py b/netbox_docker_plugin/tests/volume/test_volume_api.py
index 7a27270..ae867de 100644
--- a/netbox_docker_plugin/tests/volume/test_volume_api.py
+++ b/netbox_docker_plugin/tests/volume/test_volume_api.py
@@ -1,8 +1,15 @@
 """Volume Test Case"""
 
+from django.urls import reverse
+from django.contrib.contenttypes.models import ContentType
+from rest_framework import status
+from users.models import ObjectPermission
 from utilities.testing import APIViewTestCases
 from netbox_docker_plugin.models.host import Host
 from netbox_docker_plugin.models.volume import Volume
+from netbox_docker_plugin.models.image import Image
+from netbox_docker_plugin.models.registry import Registry
+from netbox_docker_plugin.models.container import Container, Mount
 from netbox_docker_plugin.tests.base import BaseAPITestCase
 
 
@@ -42,3 +49,74 @@ def setUpTestData(cls) -> None:
                 "name": "volume6",
             },
         ]
+
+    def test_that_embedded_anonymous_volume_is_created(self):
+        """Test that embedded anonymous Volume is created on Container update"""
+
+        # Assign model-level permission
+        obj_perm = ObjectPermission(
+            name="Test permission", actions=["add", "change", "view"]
+        )
+        obj_perm.save()
+        # pylint: disable=E1101
+        obj_perm.users.add(self.user)
+        # pylint: disable=E1101
+        obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Container))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Mount))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Image))
+
+        host = Host.objects.create(endpoint="http://localhost:8088", name="host8")
+
+        registry = Registry.objects.create(
+            host=host, name="registry8", serveraddress="http://localhost:8089"
+        )
+
+        image = Image.objects.create(host=host, name="image8", registry=registry)
+
+        container = Container.objects.create(host=host, image=image, name="container8")
+
+        response = self.client.patch(
+            reverse(f"plugins-api:{self._get_view_namespace()}:container-list"),
+            # pylint: disable=C0301
+            [
+                {
+                    "id": container.pk,
+                    "host": host.pk,
+                    "mounts": [
+                        {
+                            "source": "/var/lib/docker/volumes/2419be4bd98f41a8e625c9e4479c1c5b0f8da4b5807f08a0340aa7a2e1574fdd/_data",
+                            "volume": {
+                                "name": "2419be4bd98f41a8e625c9e4479c1c5b0f8da4b5807f08a0340aa7a2e1574fdd",
+                                "host": host.pk,
+                            },
+                        },
+                        {
+                            "source": "/var/lib/docker/volumes/844654ee4ac9d716679ed38b83c4ab6830a9db60ebf3fb2c000c7454bc41e9e2/_data",
+                            "volume": {
+                                "name": "844654ee4ac9d716679ed38b83c4ab6830a9db60ebf3fb2c000c7454bc41e9e2",
+                                "host": host.pk,
+                            },
+                        },
+                    ],
+                }
+            ],
+            format="json",
+            **self.header,
+        )
+
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+
+        self.assertTrue(
+            Volume.objects.filter(
+                name="2419be4bd98f41a8e625c9e4479c1c5b0f8da4b5807f08a0340aa7a2e1574fdd",
+                host=host.pk,
+            ).exists()
+        )
+
+        self.assertTrue(
+            Volume.objects.filter(
+                name="844654ee4ac9d716679ed38b83c4ab6830a9db60ebf3fb2c000c7454bc41e9e2",
+                host=host.pk,
+            ).exists()
+        )