Skip to content

Commit

Permalink
Merge pull request #144 from SaaShup/force-pull-3x
Browse files Browse the repository at this point in the history
✨ Add ability to "Force pull" an image
  • Loading branch information
linkdd authored Jul 19, 2024
2 parents 92797df + bb03cc6 commit ac38828
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 2 deletions.
2 changes: 1 addition & 1 deletion netbox_docker_plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class NetBoxDockerConfig(PluginConfig):
name = "netbox_docker_plugin"
verbose_name = " NetBox Docker Plugin"
description = "Manage Docker"
version = "1.12.1"
version = "1.13.0"
max_version = "3.7.8"
base_url = "docker"
author= "Vincent Simonin <[email protected]>, David Delassus <[email protected]>"
Expand Down
41 changes: 41 additions & 0 deletions netbox_docker_plugin/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,47 @@ class ImageViewSet(NetBoxModelViewSet):
serializer_class = ImageSerializer
http_method_names = ["get", "post", "patch", "delete", "options"]

@extend_schema(
operation_id="plugins_docker_image_force_pull",
responses={
(200, "application/json"): OpenApiResponse(response=str),
(502, "application/json"): OpenApiResponse(response=str),
},
)
@action(
detail=True,
methods=["post"],
renderer_classes=[JSONRenderer],
)
def force_pull(self, _request, **_kwargs):
""" Force pull an existing image """

image: Image = self.get_object()
agent_url = image.host.endpoint

url = f"{agent_url}/api/engine/images"

try:
serializer = self.get_serializer(image)
data = serializer.data
data["force"] = True

resp = requests.post(url, timeout=10, json={"data": data})
resp.raise_for_status()

except requests.HTTPError:
return Response(
{"success": False, "payload": resp.text},
status=status.HTTP_502_BAD_GATEWAY,
content_type="application/json",
)

return Response(
{"success": True, "payload": resp.json()},
status=status.HTTP_200_OK,
content_type="application/json",
)


class VolumeViewSet(NetBoxModelViewSet):
"""Volume view set class"""
Expand Down
77 changes: 77 additions & 0 deletions netbox_docker_plugin/templates/netbox_docker_plugin/image.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,83 @@

{% load plugins %}

{% block extra_controls %}
<button
class="btn btn-sm btn-success"
onclick="
(async () => {
this.setAttribute('disabled', 'disabled')
const notificationData = {}
try {
const resp = await fetch(
'/api/plugins/docker/images/{{ object.pk }}/force_pull/',
{
method: 'POST',
headers: {
'X-CSRFToken': window.CSRF_TOKEN,
},
},
)
const data = await resp.json()
console.log(data)
notificationData.kind = 'success'
notificationData.icon = ['mdi', 'mdi-check']
notificationData.message = 'Pulling image again'
}
catch (err) {
console.error(err)
notificationData.kind = 'danger'
notificationData.icon = ['mdi', 'mdi-alert']
notificationData.message = 'Failed to force pull image'
}
finally {
this.removeAttribute('disabled')
}
const notification = document.createElement('div')
notification.classList.add('toast', 'toast-dark', 'border-0', 'shadow-sm', 'rounded-0')
notification.setAttribute('role', 'alert')
notification.setAttribute('aria-live', 'assertive')
notification.setAttribute('aria-atomic', 'true')
notification.dataset['bsDelay'] = 10_000
const header = document.createElement('div')
header.classList.add('toast-header', `bg-${notificationData.kind}`, 'rounded-0')
const headerIcon = document.createElement('i')
headerIcon.classList.add(...notificationData.icon, 'me-2')
const headerTitle = document.createElement('span')
headerTitle.innerText = notificationData.message
const closeButton = document.createElement('button')
closeButton.classList.add('btn-close', 'me-0', 'm-auto')
closeButton.setAttribute('type', 'button')
closeButton.setAttribute('aria-label', 'Close')
closeButton.dataset['bsDismiss'] = 'toast'
header.appendChild(headerIcon)
header.appendChild(headerTitle)
header.appendChild(closeButton)
notification.appendChild(header)
document.querySelector('.toast-container').appendChild(notification)
const toast = new Toast(notification)
toast.show()
})()
"
>
<i class="mdi mdi-reload"></i> Force Pull
</button>
{% endblock %}

{% block content %}
<div class="row mb-3">
<div class="col col-md-6">
Expand Down
37 changes: 37 additions & 0 deletions netbox_docker_plugin/tests/image/test_image_api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
"""Image Test Case"""

import requests_mock
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.image import Image
Expand Down Expand Up @@ -66,3 +71,35 @@ def setUpTestData(cls) -> None:
"registry": registry.pk,
},
]

def test_force_pull_endpoint(self):
"""Test force pull endpoint"""

image = Image.objects.get(name="image1")

endpoint = reverse(
viewname=f"plugins-api:{self._get_view_namespace()}:image-force-pull",
kwargs={"pk": image.pk},
)

# Add object-level permission
obj_perm = ObjectPermission(
name="Test permission",
constraints={"pk": image.pk},
actions=["add"],
)
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))

with requests_mock.Mocker() as m:
m.post(
"http://localhost:8080/api/engine/images",
text="{}",
)

response = self.client.post(endpoint, **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
self.assertEqual(response.data, {"success": True, "payload": {}})
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "netbox-docker-plugin"
version = "1.12.1"
version = "1.13.0"
authors = [
{ name="Vincent Simonin", email="[email protected]" },
{ name="David Delassus", email="[email protected]" }
Expand Down

0 comments on commit ac38828

Please sign in to comment.