Skip to content

Commit eb83037

Browse files
committed
Merge remote-tracking branch 'origin/master' into edge
2 parents 69023ec + 5e8ae4c commit eb83037

File tree

6 files changed

+110
-15
lines changed

6 files changed

+110
-15
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ else
6464
./envhelp/py3.depends: $(REQS_PATH)
6565
endif
6666
@rm -f ./envhelp/py3.depends
67+
@echo "upgrading venv pip as required for some dependencies"
68+
@./envhelp/venv/bin/pip3 install --upgrade pip
6769
@echo "installing dependencies from $(REQS_PATH)"
6870
@./envhelp/venv/bin/pip3 install -r $(REQS_PATH)
6971
ifeq ($(MIG_ENV),'local')

envhelp/docker/Dockerfile.python2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM python:2
22

33
WORKDIR /usr/src/app
44

5-
COPY requirements.txt ./
6-
RUN pip install --no-cache-dir -r requirements.txt
5+
COPY requirements.txt local-requirements.txt ./
6+
RUN pip install --no-cache-dir -r requirements.txt -r local-requirements.txt
77

88
CMD [ "python", "--version" ]

local-requirements.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
# migrid dependencies on a format suitable for pip install as described on
22
# https://pip.pypa.io/en/stable/reference/requirement-specifiers/
3-
autopep8
3+
# This list is mainly used to specify addons needed for the unit tests.
4+
# We only need autopep8 on py 3 as it's used in 'make fmt' (with py3)
5+
autopep8;python_version >= "3"
6+
# We need paramiko for the ssh unit tests
7+
# NOTE: paramiko-3.0.0 dropped python2 and python3.6 support
8+
paramiko;python_version >= "3.7"
9+
paramiko<3;python_version < "3.7"

mig/shared/compat.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@
3434
from past.builtins import basestring
3535

3636
import codecs
37+
import io
3738
import sys
39+
# NOTE: StringIO is only available in python2
40+
try:
41+
import StringIO
42+
except ImportError:
43+
StringIO = None
3844

3945
PY2 = sys.version_info[0] < 3
4046
_TYPE_UNICODE = type(u"")
@@ -72,3 +78,15 @@ def ensure_native_string(string_or_bytes):
7278
else:
7379
textual_output = string_or_bytes
7480
return textual_output
81+
82+
83+
def NativeStringIO(initial_value=''):
84+
"""Mock StringIO pseudo-class to create a StringIO matching the native
85+
string coding form. That is a BytesIO with utf8 on python 2 and unicode
86+
StringIO otherwise. Optional string helpers are automatically converted
87+
accordingly.
88+
"""
89+
if PY2 and StringIO is not None:
90+
return StringIO.StringIO(initial_value)
91+
else:
92+
return io.StringIO(initial_value)

mig/shared/ssh.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# --- BEGIN_HEADER ---
55
#
66
# ssh - remote command wrappers using ssh/scp
7-
# Copyright (C) 2003-2023 The MiG Project lead by Brian Vinter
7+
# Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
88
#
99
# This file is part of MiG.
1010
#
@@ -35,14 +35,6 @@
3535
import sys
3636
import tempfile
3737

38-
# NOTE: we would prefer the modern io.BytesIO but it fails in python2 during
39-
# rsa_key.write_private_key(string_io_obj) with a Paramiko error:
40-
# TypeError: 'unicode' does not have the buffer interface
41-
try:
42-
from cStringIO import StringIO as LegacyStringIO
43-
except ImportError:
44-
from io import BytesIO as LegacyStringIO
45-
4638
try:
4739
import paramiko
4840
except ImportError:
@@ -51,6 +43,7 @@
5143

5244
from mig.shared.base import client_id_dir, force_utf8
5345
from mig.shared.conf import get_resource_exe, get_configuration_object
46+
from mig.shared.compat import NativeStringIO
5447
from mig.shared.defaults import ssh_conf_dir
5548
from mig.shared.safeeval import subprocess_popen, subprocess_pipe
5649

@@ -95,8 +88,8 @@ def parse_pub_key(public_key):
9588
msg = 'Invalid ssh public key: (%s)' % public_key
9689
raise ValueError(msg)
9790

98-
head, tail = public_key.split(' ')[ssh_type_idx:2+ssh_type_idx]
99-
bits = base64.decodestring(tail)
91+
head, tail = public_key.split(' ')[ssh_type_idx:2 + ssh_type_idx]
92+
bits = base64.b64decode(tail)
10093
msg = paramiko.Message(bits)
10194
parse_key = type_map[head]
10295
return parse_key(msg)
@@ -511,7 +504,12 @@ def generate_ssh_rsa_key_pair(size=2048, public_key_prefix='',
511504
raise Exception("You need paramiko to provide the ssh/sftp service")
512505
rsa_key = paramiko.RSAKey.generate(size)
513506

514-
string_io_obj = LegacyStringIO()
507+
# NOTE: we would prefer the modern io.BytesIO on python2 but it fails
508+
# during rsa_key.write_private_key(string_io_obj) with a Paramiko
509+
# TypeError: 'unicode' does not have the buffer interface.
510+
# Use compat NativeStringIO to pick a suitable StringIO for the
511+
# active platform.
512+
string_io_obj = NativeStringIO()
515513
rsa_key.write_private_key(string_io_obj)
516514

517515
private_key = string_io_obj.getvalue()

tests/test_mig_shared_ssh.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# --- BEGIN_HEADER ---
4+
#
5+
# test_mig_shared_ssh - unit test of the corresponding mig shared module
6+
# Copyright (C) 2003-2024 The MiG Project by the Science HPC Center at UCPH
7+
#
8+
# This file is part of MiG.
9+
#
10+
# MiG is free software: you can redistribute it and/or modify
11+
# it under the terms of the GNU General Public License as published by
12+
# the Free Software Foundation; either version 2 of the License, or
13+
# (at your option) any later version.
14+
#
15+
# MiG is distributed in the hope that it will be useful,
16+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
# GNU General Public License for more details.
19+
#
20+
# You should have received a copy of the GNU General Public License
21+
# along with this program; if not, write to the Free Software
22+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
23+
# USA.
24+
#
25+
# --- END_HEADER ---
26+
#
27+
28+
"""Unit tests for the migrid module pointed to in the filename"""
29+
30+
import os
31+
import sys
32+
33+
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__))))
34+
35+
from tests.support import TEST_OUTPUT_DIR, MigTestCase, FakeConfiguration, \
36+
cleanpath, testmain
37+
from mig.shared.ssh import supported_pub_key_parsers, parse_pub_key, \
38+
generate_ssh_rsa_key_pair
39+
40+
41+
class MigSharedSsh(MigTestCase):
42+
"""Wrap unit tests for the corresponding module"""
43+
44+
def test_ssh_key_generate_and_parse(self):
45+
parsers = supported_pub_key_parsers()
46+
# NOTE: should return a non-empty dict of algos and parsers
47+
self.assertTrue(parsers)
48+
self.assertTrue('ssh-rsa' in parsers)
49+
50+
# Generate common sized keys and parse the result
51+
for keysize in (2048, 3072, 4096):
52+
(priv_key, pub_key) = generate_ssh_rsa_key_pair(size=keysize)
53+
self.assertTrue(priv_key)
54+
self.assertTrue(pub_key)
55+
56+
# NOTE: parse_pub_key expects a native string so we use this case
57+
try:
58+
parsed = parse_pub_key(pub_key)
59+
except ValueError as vae:
60+
#print("Error in parsing pub key: %r" % vae)
61+
parsed = None
62+
self.assertTrue(parsed is not None)
63+
64+
(priv_key, pub_key) = generate_ssh_rsa_key_pair(size=keysize,
65+
encode_utf8=True)
66+
self.assertTrue(priv_key)
67+
self.assertTrue(pub_key)
68+
69+
70+
if __name__ == '__main__':
71+
testmain()

0 commit comments

Comments
 (0)