Skip to content

Commit 6eb9f44

Browse files
authored
Merge pull request #59 from elixir-cloud-aai/update-alpine
Update filer and taskmaster
2 parents a6e1a2a + 4dd5c6a commit 6eb9f44

13 files changed

Lines changed: 112 additions & 60 deletions

File tree

.github/workflows/tox.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ubuntu-latest
1010
strategy:
1111
matrix:
12-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
12+
python-version: ['3.10', '3.11', '3.12']
1313

1414
steps:
1515
- uses: actions/checkout@v3

README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,36 @@ Since the code is meant to be in kubernetes pods, the code needs to be packaged
1414
Their descriptions can be found in `containers/`.
1515
The root folder assumed to build the containers is the root of this package.
1616

17+
To build the taskmaster container, run:
18+
19+
```
20+
docker build . -f containers/taskmaster.Dockerfile -t taskmaster:latest
21+
```
22+
23+
The command is similar for the filer container:
24+
25+
```
26+
docker build . -f containers/filer.Dockerfile -t filer:latest
27+
```
28+
1729
## Unit testing
1830

1931
Unit testing needs the `tox` package.
32+
33+
You can install the package using `uv`:
34+
35+
```
36+
uv install tox
37+
```
38+
39+
To install different python versions using `uv`, you can type:
40+
41+
```
42+
uv python install 3.10 3.11 3.12
43+
```
44+
2045
This software will take care of creating virtual environments and installing dependencies in them before running the actual tests and generating the coverage reports.
2146

2247
```
23-
$ tox
48+
$ uv run tox
2449
```

containers/filer.Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
# Builder: produce wheels
22

3-
FROM alpine:3.19 as builder
3+
FROM alpine:3.23 AS builder
44

55
RUN apk add --no-cache python3 py3-pip
66
RUN apk add --no-cache git
7-
RUN python3 -m pip install --upgrade setuptools pip wheel --break-system-packages
7+
RUN python3 -m pip install --upgrade setuptools pip wheel build --break-system-packages
88

99
WORKDIR /app/
1010
COPY . .
1111

12-
RUN python3 setup.py bdist_wheel
12+
RUN python3 -m build --wheel && rm -rf dist/*.tar.gz
1313

1414
# Install: copy tesk-core*.whl and install it with dependencies
1515

16-
FROM alpine:3.19
16+
FROM alpine:3.23
1717

1818
RUN apk add --no-cache python3 py3-pip
1919

containers/taskmaster.Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
# Builder: produce wheels
22

3-
FROM alpine:3.19 as builder
3+
FROM alpine:3.23 AS builder
44

55
RUN apk add --no-cache python3 py3-pip
66
RUN apk add --no-cache git
7-
RUN python3 -m pip install --upgrade setuptools pip wheel --break-system-packages
7+
RUN python3 -m pip install --upgrade setuptools pip wheel build --break-system-packages
88

99
WORKDIR /app/
1010
COPY . .
1111

12-
RUN python3 setup.py bdist_wheel
12+
RUN python3 -m build --wheel && rm -rf dist/*.tar.gz
1313

1414
# Install: copy tesk-core*.whl and install it with dependencies
1515

16-
FROM alpine:3.19
16+
FROM alpine:3.23
1717

1818
RUN apk add --no-cache python3 py3-pip
1919

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[metadata]
2-
description-file=README.md
2+
description_file=README.md
33
[aliases]
44
test=pytest

setup.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,11 @@
88
with codecs.open(path.join(HERE, 'README.md'), encoding='utf-8') as f:
99
LONG_DESC = f.read()
1010

11-
INSTALL_DEPS = ['kubernetes==9.0.0',
11+
INSTALL_DEPS = ['kubernetes==35.0.0',
1212
'requests>=2.20.0',
1313

14-
# urllib3 constraint
15-
'urllib3>=1.26,<2.0 ; python_version < "3.10"',
16-
'urllib3>=2.0,<3.0 ; python_version >= "3.10"',
14+
'urllib3>=2.6.0,<3.0 ; python_version >= "3.10"',
1715

18-
# boto3 constraint
19-
'boto3<=1.28 ; python_version == "3.8"',
2016
'boto3>=1.28,<2.0 ; python_version >= "3.9"',
2117
]
2218
TEST_DEPS = [ 'pytest',
@@ -56,10 +52,8 @@
5652

5753
'Intended Audience :: System Administrators',
5854

59-
'License :: OSI Approved :: Apache Software License',
60-
6155
'Programming Language :: Python :: 3',
62-
'Programming Language :: Python :: 3.7'
56+
'Programming Language :: Python :: >=3.10'
6357
],
6458

6559
# What does your project relate to?
@@ -74,7 +68,6 @@
7468
'taskmaster = tesk_core.taskmaster:main'
7569
]
7670
},
77-
test_suite='tests',
7871

7972
# List run-time dependencies here. These will be installed by pip when
8073
# your project is installed. For an analysis of "install_requires" vs pip's
@@ -84,9 +77,7 @@
8477

8578
setup_requires=['setuptools_scm'],
8679

87-
tests_require=TEST_DEPS,
88-
89-
python_requires='>=3.5, <4.0',
80+
python_requires='>=3.10, <4.0',
9081

9182
# List additional groups of dependencies here (e.g. development
9283
# dependencies). You can install these using the following syntax,

src/tesk_core/filer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import json
88
import re
99
import os
10-
import distutils.dir_util
1110
import logging
1211
import netrc
1312
import requests
@@ -112,7 +111,7 @@ def copyFile(src, dst):
112111
'''
113112

114113
# If there is any * in 'dst', use only the dirname (base path)
115-
p = re.compile('.*\*.*')
114+
p = re.compile(r'.*\*.*')
116115
if p.match(dst):
117116
dst=os.path.dirname(dst)
118117

@@ -229,7 +228,8 @@ def download_file(self):
229228
logging.debug('Downloading ftp file: "%s" Target: %s', self.url,
230229
self.path)
231230
basedir = os.path.dirname(self.path)
232-
distutils.dir_util.mkpath(basedir)
231+
if basedir and not os.path.exists(basedir):
232+
os.makedirs(basedir, exist_ok=True)
233233

234234
return ftp_download_file(self.ftp_connection, self.url_path, self.path)
235235

src/tesk_core/filer_s3.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55
import botocore
66
import boto3
7+
import mimetypes
78
from tesk_core.transput import Transput, Type
89

910
class S3Transput(Transput):
@@ -50,10 +51,24 @@ def download_file(self):
5051
os.makedirs(basedir, exist_ok=True)
5152
return self.get_s3_file(self.path, self.file_path)
5253

54+
def get_content_type(self):
55+
# Guess content type based on filename; fallback to binary stream
56+
mime, encoding = mimetypes.guess_type(self.path)
57+
if mime is None:
58+
return 'application/octet-stream'
59+
elif mime.startswith('text/') or mime in ('application/json', 'application/xml', 'application/javascript'):
60+
mime = f'{mime}; charset=utf-8'
61+
return mime
62+
63+
5364
def upload_file(self):
5465
logging.debug('Uploading s3 object: "%s" Target: %s', self.path, self.bucket + "/" + self.file_path)
66+
content_type = self.get_content_type()
67+
logging.debug('Guessed Content-Type: %s for file: %s', content_type, self.path)
5568
try:
56-
self.bucket_obj.upload_file(Filename=self.path, Key=self.file_path)
69+
# Pass ContentType via ExtraArgs so the object is uploaded with the right MIME type
70+
self.bucket_obj.upload_file(Filename=self.path, Key=self.file_path,
71+
ExtraArgs={'ContentType': content_type})
5772
except (botocore.exceptions.ClientError, OSError) as err:
5873
logging.error("File upload failed for '%s'", self.bucket + "/" + self.file_path)
5974
logging.error(err)
@@ -100,7 +115,8 @@ def download_dir(self):
100115
for obj in objects["Contents"]:
101116
file_name = os.path.basename(obj["Key"])
102117
dir_name = os.path.dirname(obj["Key"])
103-
path_to_create = re.sub(r'^' + self.file_path.strip('/').replace('/', '\/') + '', "", dir_name).strip('/')
118+
prefix = re.escape(self.file_path.strip('/'))
119+
path_to_create = re.sub(r'^' + prefix, '', dir_name).strip('/')
104120
path_to_create = os.path.join(self.path, path_to_create)
105121
os.makedirs(path_to_create, exist_ok=True)
106122
if self.get_s3_file(os.path.join(path_to_create, file_name), obj["Key"]):

tests/FilerClassTest.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ def test_env_vars(self):
3131

3232
pprint(f.spec)
3333

34-
self.assertEquals(f.getEnv(), [
34+
self.assertEqual(f.getEnv(), [
3535

3636
{ 'name': 'JSON_INPUT' , 'value': '{"a": 1}' }
3737
,{ 'name': 'HOST_BASE_PATH' , 'value': '/home/tfga/workspace/cwl-tes' }
3838
,{ 'name': 'CONTAINER_BASE_PATH' , 'value': '/transfer' }
3939
,{"name": "AWS_CONFIG_FILE", "value": "/aws/config"}
4040
,{"name": "AWS_SHARED_CREDENTIALS_FILE", "value": "/aws/credentials"},
4141
])
42-
self.assertEquals(f.spec['spec']['backoffLimit'], 10)
42+
self.assertEqual(f.spec['spec']['backoffLimit'], 10)
4343

4444

4545
def test_mounts(self):
@@ -69,15 +69,15 @@ def test_mounts(self):
6969

7070
pprint(f.getVolumeMounts())
7171

72-
self.assertEquals(f.getVolumeMounts(), [
72+
self.assertEqual(f.getVolumeMounts(), [
7373

7474
{ "name" : 'transfer-volume'
7575
, 'mountPath' : path.CONTAINER_BASE_PATH,
7676
},
7777
{'mountPath': '/aws', 'name': 's3-conf', 'readOnly': True}
7878
])
7979

80-
self.assertEquals(f.getVolumes(), [
80+
self.assertEqual(f.getVolumes(), [
8181

8282
{ "name" : 'transfer-volume'
8383
, 'persistentVolumeClaim' : { 'claimName' : 'transfer-pvc' }
@@ -112,10 +112,10 @@ def test_mounts_file_disabled(self):
112112

113113
pprint(f.getVolumeMounts())
114114

115-
self.assertEquals(f.getVolumeMounts() , [
115+
self.assertEqual(f.getVolumeMounts() , [
116116
{'mountPath': '/aws', 'name': 's3-conf', 'readOnly': True}
117117
])
118-
self.assertEquals(f.getVolumes() , [
118+
self.assertEqual(f.getVolumes() , [
119119
{
120120
"name": "s3-conf",
121121
"secret": {
@@ -139,11 +139,10 @@ def test_mounts_file_disabled(self):
139139
def test_image_pull_policy(self):
140140

141141
f = Filer('name', {'a': 1})
142-
self.assertEquals(f.getImagePullPolicy() , 'IfNotPresent')
142+
self.assertEqual(f.getImagePullPolicy() , 'IfNotPresent')
143143

144144
f = Filer('name', {'a': 1}, pullPolicyAlways = True)
145-
self.assertEquals(f.getImagePullPolicy() , 'Always')
146-
145+
self.assertEqual(f.getImagePullPolicy() , 'Always')
147146

148147

149148

tests/TaskMasterTest.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010

1111

1212

13-
def pvcCreateMock(self): print '[mock] Creating PVC...'
14-
def pvcDeleteMock(self): print '[mock] Deleting PVC...'
13+
def pvcCreateMock(self):
14+
print('[mock] Creating PVC...')
15+
16+
def pvcDeleteMock(self):
17+
print('[mock] Deleting PVC...')
18+
1519

1620
def jobRunToCompletionMock(job, b, c):
17-
18-
print "[mock] Creating job '{}'...".format(job.name)
19-
21+
print('[mock] Creating job "{}"...'.format(job.name))
2022
return 'Complete'
2123

2224

@@ -31,7 +33,7 @@ def test_defaults(self):
3133

3234
print(args)
3335

34-
self.assertEquals( args
36+
self.assertEqual( args
3537
, Namespace( debug=False, file=None, filer_version='v0.1.9', json='json', namespace='default', poll_interval=5, state_file='/tmp/.teskstate'
3638
, localKubeConfig=False
3739
, pull_policy_always=False
@@ -47,7 +49,7 @@ def test_localKubeConfig(self):
4749

4850
print(args)
4951

50-
self.assertEquals( args
52+
self.assertEqual( args
5153
, Namespace( debug=False, file=None, filer_version='v0.1.9', json='json', namespace='default', poll_interval=5, state_file='/tmp/.teskstate'
5254
, localKubeConfig=True
5355
, pull_policy_always=False
@@ -59,8 +61,8 @@ def test_pullPolicyAlways(self):
5961

6062
parser = newParser()
6163

62-
self.assertEquals( parser.parse_args(['json' ]).pull_policy_always, False )
63-
self.assertEquals( parser.parse_args(['json', '--pull-policy-always']).pull_policy_always, True )
64+
self.assertEqual( parser.parse_args(['json' ]).pull_policy_always, False )
65+
self.assertEqual( parser.parse_args(['json', '--pull-policy-always']).pull_policy_always, True )
6466

6567

6668

0 commit comments

Comments
 (0)