Skip to content

Commit 0322587

Browse files
authored
fix: Accept SVG as avatar_url (#2083)
Make download_avatar task to accept .svg up to 3MB Issue: AAH-2836
1 parent 9ca7aa4 commit 0322587

File tree

4 files changed

+81
-11
lines changed

4 files changed

+81
-11
lines changed

CHANGES/2836.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support SVG avatar image on namespaces

galaxy_ng/app/management/commands/download-namespace-logos.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,51 @@ class Command(BaseCommand):
2424

2525
def add_arguments(self, parser):
2626
parser.add_argument("--namespace", help="find and sync only this namespace name")
27+
parser.add_argument(
28+
"--sha-report",
29+
default=False,
30+
action="store_true",
31+
required=False,
32+
help="report the number of namespaces with avatar_url but missing avatar_sha256",
33+
dest="sha_report",
34+
)
35+
parser.add_argument(
36+
"--only-missing-sha",
37+
default=False,
38+
action="store_true",
39+
required=False,
40+
help="When set it will limit the logo download only to those namespaces missing sha",
41+
dest="only_missing_sha",
42+
)
2743

2844
def echo(self, message, style=None):
2945
style = style or self.style.SUCCESS
3046
self.stdout.write(style(message))
3147

3248
def handle(self, *args, **options):
49+
# all namespaces having avatar_url must have avatar_sha256 set
50+
# query for namespaces missing avatar_sha256
51+
ns_missing_avatar_sha = Namespace.objects.filter(
52+
_avatar_url__isnull=False,
53+
last_created_pulp_metadata__avatar_sha256__isnull=True
54+
)
55+
if ns_missing_avatar_sha:
56+
self.echo(
57+
f"{ns_missing_avatar_sha.count()} Namespaces missing avatar_sha256",
58+
self.style.ERROR
59+
)
60+
self.echo(", ".join(ns_missing_avatar_sha.values_list("name", flat=True)))
61+
else:
62+
self.echo("There are no namespaces missing avatar_sha256!")
63+
64+
if options["sha_report"]: # --sha-report indicated only report was requested
65+
return
66+
67+
self.echo("Proceeding with namespace logo downloads")
3368

3469
kwargs = {
3570
'namespace_name': options['namespace'],
71+
'only_missing_sha': options['only_missing_sha'],
3672
}
3773

3874
task = dispatch(
@@ -52,11 +88,23 @@ def handle(self, *args, **options):
5288
sys.exit(1)
5389

5490

55-
def download_all_logos(namespace_name=None):
91+
def download_all_logos(namespace_name=None, only_missing_sha=False):
92+
"""Force logo downloads.
93+
namespace: limit to specified namespace (or list of namespaces)
94+
only_missing_sha: Limit to namespaces having avatar_url but missing avatar_sha256
95+
"""
5696
if namespace_name:
57-
qs = Namespace.objects.filter(name=namespace_name)
97+
namespaces = namespace_name.split(",")
98+
qs = Namespace.objects.filter(name__in=namespaces)
5899
else:
59100
qs = Namespace.objects.all()
101+
102+
if only_missing_sha:
103+
qs = qs.filter(
104+
_avatar_url__isnull=False,
105+
last_created_pulp_metadata__avatar_sha256__isnull=True
106+
)
107+
60108
for namespace in qs:
61109
download_logo = False
62110
if namespace._avatar_url:

galaxy_ng/app/tasks/namespaces.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import aiohttp
22
import asyncio
3+
import contextlib
4+
import xml.etree.cElementTree as et
35

46
from django.db import transaction
57
from django.forms.fields import ImageField
@@ -15,6 +17,9 @@
1517
from galaxy_ng.app.models import Namespace
1618

1719

20+
MAX_AVATAR_SIZE = 3 * 1024 * 1024 # 3MB
21+
22+
1823
def dispatch_create_pulp_namespace_metadata(galaxy_ns, download_logo):
1924

2025
dispatch(
@@ -26,11 +31,16 @@ def dispatch_create_pulp_namespace_metadata(galaxy_ns, download_logo):
2631
)
2732

2833

29-
def _download_avatar(url):
34+
def _download_avatar(url, namespace_name):
35+
# User-Agent needs to be added to avoid timing out on throtled servers.
36+
headers = {
37+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0)' # +
38+
' Gecko/20100101 Firefox/71.0'
39+
}
3040
timeout = aiohttp.ClientTimeout(total=None, sock_connect=600, sock_read=600)
3141
conn = aiohttp.TCPConnector(force_close=True)
3242
session = aiohttp.ClientSession(
33-
connector=conn, timeout=timeout, headers=None, requote_redirect_url=False
43+
connector=conn, timeout=timeout, headers=headers, requote_redirect_url=False
3444
)
3545

3646
try:
@@ -41,19 +51,29 @@ def _download_avatar(url):
4151
finally:
4252
asyncio.get_event_loop().run_until_complete(session.close())
4353

44-
try:
54+
# Limit size of the avatar to avoid memory issues when validating it
55+
if img.artifact_attributes["size"] > MAX_AVATAR_SIZE:
56+
raise ValidationError(
57+
f"Avatar for {namespace_name} on {url} larger than {MAX_AVATAR_SIZE / 1024 / 1024}MB"
58+
)
59+
60+
with contextlib.suppress(Artifact.DoesNotExist):
4561
return Artifact.objects.get(sha256=img.artifact_attributes["sha256"])
46-
except Artifact.DoesNotExist:
47-
pass
4862

4963
with open(img.path, "rb") as f:
5064
tf = PulpTemporaryUploadedFile.from_file(f)
51-
5265
try:
5366
ImageField().to_python(tf)
5467
except ValidationError:
55-
print("file is not an image")
56-
return
68+
# Not a PIL valid image lets handle SVG case
69+
tag = None
70+
with contextlib.suppress(et.ParseError):
71+
f.seek(0)
72+
tag = et.parse(f).find(".").tag
73+
if tag != '{http://www.w3.org/2000/svg}svg':
74+
raise ValidationError(
75+
f"Provided avatar_url for {namespace_name} on {url} is not a valid image"
76+
)
5777

5878
# the artifact has to be saved before the file is closed, or s3transfer
5979
# will throw an error.
@@ -71,7 +91,7 @@ def _create_pulp_namespace(galaxy_ns_pk, download_logo):
7191
avatar_artifact = None
7292

7393
if download_logo:
74-
avatar_artifact = _download_avatar(galaxy_ns._avatar_url)
94+
avatar_artifact = _download_avatar(galaxy_ns._avatar_url, galaxy_ns.name)
7595

7696
avatar_sha = None
7797
if avatar_artifact:

galaxy_ng/tests/integration/api/test_namespace_management.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ def test_namespace_edit_logo(galaxy_client):
177177
wait_for_all_tasks_gk(gc)
178178
updated_again_namespace = gc.get(f"_ui/v1/my-namespaces/{name}/")
179179
assert updated_namespace["avatar_url"] != updated_again_namespace["avatar_url"]
180+
assert updated_namespace["avatar_sha256"] is not None
180181

181182
# verify no additional namespaces are created
182183
resp = gc.get("_ui/v1/my-namespaces/")

0 commit comments

Comments
 (0)