184 lines
6.0 KiB
Python
184 lines
6.0 KiB
Python
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
import os
|
|
|
|
import mock
|
|
import pytest
|
|
|
|
from octane import magic_consts
|
|
from octane.util import ssh
|
|
|
|
|
|
@pytest.mark.parametrize('username', ['root', 'test'])
|
|
def test_get_sftp(mocker, username):
|
|
mclient = mocker.patch('octane.util.ssh.get_client')
|
|
msftp_client = mocker.patch('paramiko.SFTPClient')
|
|
mtransport = mocker.Mock()
|
|
mchannel = mocker.Mock()
|
|
mclient.return_value.get_transport.return_value = mtransport
|
|
mtransport.open_channel.return_value = mchannel
|
|
|
|
mtransport.get_username.return_value = username
|
|
|
|
# call not cached version
|
|
ssh._get_sftp.new(mocker.Mock(data={'id': 1, 'hostname': 'test'}))
|
|
|
|
if username == 'root':
|
|
msftp_client.from_transport.assert_called_once_with(mtransport)
|
|
else:
|
|
mchannel.exec_command.assert_called_once_with(
|
|
'sudo ' + magic_consts.SFTP_SERVER_BIN)
|
|
msftp_client.assert_called_once_with(mchannel)
|
|
|
|
|
|
@pytest.mark.parametrize("prefix", ["prefix_name"])
|
|
@pytest.mark.parametrize("cwd", ["/test/path"])
|
|
@pytest.mark.parametrize("patches, error_idx", [
|
|
(["patch_1"], None),
|
|
(["patch_1", "patch_2"], None),
|
|
(["patch_1", "patch_2", "patch_3"], 0),
|
|
(["patch_1", "patch_2", "patch_3"], 1),
|
|
(["patch_1", "patch_2", "patch_3"], 2),
|
|
])
|
|
@pytest.mark.parametrize("error", [True, False])
|
|
def test_applied_patches(mocker, node, mock_open,
|
|
prefix, patches, cwd, error, error_idx):
|
|
|
|
class TestError(Exception):
|
|
pass
|
|
|
|
mock_popen = mocker.patch("octane.util.ssh.popen")
|
|
mock_patch_calls = []
|
|
mock_shutil_calls = []
|
|
mock_open_calls = []
|
|
|
|
error = error or error_idx is not None
|
|
error_idx = error_idx or len(patches) + 1
|
|
mock_shutil_side_effect = []
|
|
|
|
revert_patches = []
|
|
|
|
for idx, path in enumerate(patches):
|
|
if idx < error_idx:
|
|
revert_patches.append(path)
|
|
if idx <= error_idx:
|
|
mock_open_calls.append(mock.call(path, "rb"))
|
|
mock_patch_calls.append(mock.call(
|
|
["patch", "-N", "-p1", "-d", cwd], node=node, stdin=ssh.PIPE
|
|
))
|
|
mock_shutil_calls.append(mock.call(
|
|
mock_open.return_value,
|
|
mock_popen.return_value.__enter__.return_value.stdin
|
|
))
|
|
if idx == error_idx:
|
|
mock_shutil_side_effect.append(TestError)
|
|
else:
|
|
mock_shutil_side_effect.append(None)
|
|
|
|
revert_patches.reverse()
|
|
for path in revert_patches:
|
|
mock_shutil_side_effect.append(None)
|
|
mock_patch_calls.append(mock.call(
|
|
["patch", "-R", "-p1", "-d", cwd], node=node, stdin=ssh.PIPE
|
|
))
|
|
mock_open_calls.append(mock.call(path, "rb"))
|
|
mock_shutil_calls.append(mock.call(
|
|
mock_open.return_value,
|
|
mock_popen.return_value.__enter__.return_value.stdin
|
|
))
|
|
|
|
mock_shutil = mocker.patch(
|
|
"shutil.copyfileobj", side_effect=mock_shutil_side_effect)
|
|
|
|
if error:
|
|
with pytest.raises(TestError):
|
|
with ssh.applied_patches(cwd, node, *patches):
|
|
raise TestError
|
|
else:
|
|
with ssh.applied_patches(cwd, node, *patches):
|
|
pass
|
|
|
|
assert mock_open_calls == mock_open.call_args_list
|
|
assert mock_patch_calls == mock_popen.call_args_list
|
|
assert mock_shutil_calls == mock_shutil.call_args_list
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"editable,result", [
|
|
({}, None),
|
|
({'name': {'value': 'a'}, 'password': {'value': 'b'}},
|
|
{'user': 'a', 'password': 'b'}),
|
|
]
|
|
)
|
|
def test_ssh_credentials(mocker, editable, result):
|
|
env = mocker.Mock()
|
|
env.get_attributes.return_value = {
|
|
'editable': {'service_user': editable},
|
|
}
|
|
|
|
assert ssh.get_env_credentials(env) == result
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"editable,result", [
|
|
({'name': {'value': 'a'}, 'password': {'value': 'b'}},
|
|
{'username': 'a'}),
|
|
({}, {'username': 'root'}),
|
|
]
|
|
)
|
|
def test_get_client_credentials(mocker, editable, result):
|
|
node = mocker.Mock()
|
|
result['key_filename'] = magic_consts.SSH_KEYS
|
|
|
|
attrs = {
|
|
'editable': {'service_user': editable},
|
|
}
|
|
|
|
ip = '8.8.8.8'
|
|
|
|
node.data = {'ip': ip, 'id': 1}
|
|
node.env.get_attributes.return_value = attrs
|
|
mocker.patch('paramiko.SSHClient')
|
|
client = ssh.get_client.new(node)
|
|
client.connect.assert_called_with(ip, **result)
|
|
|
|
|
|
@pytest.mark.parametrize("dir_names,lists_files", [
|
|
(["test_dir_1", "test_dir_2"], [["file_1", "file_2"], ["file_3"]]),
|
|
(["test_dir_3", "test_dir_4"], [[], []])
|
|
])
|
|
def test_remove_all_files_from_dirs(mocker, dir_names, lists_files):
|
|
node = mock.Mock()
|
|
mock_sftp = mocker.patch("octane.util.ssh.sftp")
|
|
mock_list_dir = mocker.patch('paramiko.SFTPClient.listdir')
|
|
mock_list_dir.side_effect = lists_files
|
|
mock_unlink = mocker.patch("paramiko.SFTPClient.unlink")
|
|
ssh.remove_all_files_from_dirs(dir_names, node)
|
|
mock_sftp.assert_called_once_with(node)
|
|
mock_list_dir.call_args_list == [
|
|
mock.call(dir_name) for dir_name in dir_names
|
|
]
|
|
mock_unlink.call_args_list == [
|
|
mock.call(os.path.join(dir_name, file)
|
|
for dir_name, files in zip(dir_names, lists_files)
|
|
for file in files)
|
|
]
|
|
|
|
|
|
def test_write_content_to_file(mocker):
|
|
filename = "filename"
|
|
content = "content"
|
|
sftp = mocker.patch("paramiko.SFTPClient")
|
|
mock_open = mocker.patch("paramiko.SFTPClient.open")
|
|
ssh.write_content_to_file(sftp, filename, content)
|
|
mock_open.assert_called_once_with(filename, 'w')
|