Skip to content

Commit

Permalink
Merge branch 'develop' into feature/apiddubny_52_same_storage_name_di…
Browse files Browse the repository at this point in the history
…fferent_reservations
  • Loading branch information
GilGald committed Oct 20, 2016
2 parents 48d259c + 16aaa0c commit ecf5578
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 5 deletions.
3 changes: 2 additions & 1 deletion drivers/azure_shell/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ cloudshell-automation-api>=7.1.0.0,<7.2.0.0
cloudshell-shell-core>=2.2.0,<2.3.0
jsonpickle==0.9.3
cloudshell-cp-azure>=0.0.1
azure==2.0.0rc6
azure==2.0.0rc6
pycrypto==2.6.1
3 changes: 2 additions & 1 deletion external_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
jsonpickle==0.9.3
enum==0.4.6
azure==2.0.0rc6
azure==2.0.0rc6
pycrypto==2.6.1
74 changes: 74 additions & 0 deletions package/cloudshell/cp/azure/domain/services/key_pair.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from Crypto.PublicKey import RSA

from azure.storage.file import FileService

from cloudshell.cp.azure.models.ssh_key import SSHKey


class KeyPairService(object):
FILE_SHARE_NAME = "sshkeypair"
FILE_SHARE_DIRECTORY = ""
SSH_PUB_KEY_NAME = "id_rsa.pub"
SSH_PRIVATE_KEY_NAME = "id_rsa"

def generate_key_pair(self, key_length=2048):
"""Generate SSH key pair model
:param key_length: (int) SSH key length
:return: cloudshell.cp.azure.models.ssh_key.SSHKey instance
"""
key = RSA.generate(key_length)
pubkey = key.publickey()

private_key = key.exportKey('PEM')
public_key = pubkey.exportKey('OpenSSH')

return SSHKey(private_key=private_key, public_key=public_key)

def save_key_pair(self, account_key, key_pair, group_name, storage_name):
"""Save SSH key pair to the Azure storage
:param account_key: (str) access key for storage account
:param key_pair: cloudshell.cp.azure.models.ssh_key.SSHKey instance
:param group_name: (str) the name of the resource group on Azure
:param storage_name: (str) the name of the storage on Azure
:return:
"""
file_service = FileService(account_name=storage_name,
account_key=account_key)

file_service.create_share(self.FILE_SHARE_NAME)

file_service.create_file_from_bytes(share_name=self.FILE_SHARE_NAME,
directory_name=self.FILE_SHARE_DIRECTORY,
file_name=self.SSH_PUB_KEY_NAME,
file=key_pair.public_key)

file_service.create_file_from_bytes(share_name=self.FILE_SHARE_NAME,
directory_name=self.FILE_SHARE_DIRECTORY,
file_name=self.SSH_PRIVATE_KEY_NAME,
file=key_pair.private_key)

def get_key_pair(self, account_key, group_name, storage_name):
"""Get SSH key pair from the Azure storage
:param account_key: (str) access key for storage account
:param group_name: (str) the name of the resource group on Azure
:param storage_name: (str) the name of the storage on Azure
:return: cloudshell.cp.azure.models.ssh_key.SSHKey instance
"""
file_service = FileService(account_name=storage_name,
account_key=account_key)

pub_key_file = file_service.get_file_to_bytes(
share_name=self.FILE_SHARE_NAME,
directory_name=self.FILE_SHARE_DIRECTORY,
file_name=self.SSH_PUB_KEY_NAME)

private_key_file = file_service.get_file_to_bytes(
share_name=self.FILE_SHARE_NAME,
directory_name=self.FILE_SHARE_DIRECTORY,
file_name=self.SSH_PRIVATE_KEY_NAME)

return SSHKey(private_key=private_key_file.content,
public_key=pub_key_file.content)
16 changes: 13 additions & 3 deletions package/cloudshell/cp/azure/domain/services/storage_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@


class StorageService(object):
def __init__(self):
pass

def create_storage_account(self, storage_client, group_name, region, storage_account_name, tags):
"""
Expand All @@ -27,3 +24,16 @@ def create_storage_account(self, storage_client, group_name, region, storage_acc
location=region,
tags=tags))
storage_accounts_create.wait() # async operation

def get_storage_account_key(self, storage_client, group_name, storage_name):
"""Get firsts storage account access key for some storage
:param storage_client: azure.mgmt.storage.StorageManagementClient instance
:param group_name: (str) the name of the resource group on Azure
:param storage_name: (str) the name of the storage on Azure
:return: (str) storage access key
"""
account_keys = storage_client.storage_accounts.list_keys(group_name, storage_name)
account_key = account_keys.keys[0]

return account_key.value
4 changes: 4 additions & 0 deletions package/cloudshell/cp/azure/models/ssh_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class SSHKey(object):
def __init__(self, private_key, public_key):
self.private_key = private_key
self.public_key = public_key
92 changes: 92 additions & 0 deletions package/tests/test_services/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@
from azure.mgmt.network.models import IPAllocationMethod
from azure.mgmt.storage.models import StorageAccountCreateParameters
from mock import MagicMock
import mock

from cloudshell.cp.azure.domain.services.network_service import NetworkService
from cloudshell.cp.azure.domain.services.key_pair import KeyPairService
from cloudshell.cp.azure.domain.services.storage_service import StorageService
from cloudshell.cp.azure.domain.services.virtual_machine_service import VirtualMachineService


class TestStorageService(TestCase):
def setUp(self):
self.storage_service = StorageService()
self.group_name = "test_group_name"
self.storage_name = "teststoragename"
self.storage_client = mock.MagicMock()

def test_create_storage_account(self):
# Arrange
Expand All @@ -35,6 +40,19 @@ def test_create_storage_account(self):
location=region,
tags=tags))

def test_get_storage_account_key(self):
"""Check that method uses storage client to retrieve first access key for the storage account"""
storage_key = mock.MagicMock()

self.storage_client.storage_accounts.list_keys.return_value = mock.MagicMock(keys=[storage_key])

key = self.storage_service.get_storage_account_key(
storage_client=self.storage_client,
group_name=self.group_name,
storage_name=self.storage_name)

self.assertEqual(key, storage_key.value)


class TestNetworkService(TestCase):
def setUp(self):
Expand Down Expand Up @@ -191,3 +209,77 @@ def test_stop_vm_with_async_mode_true(self):

operation_poller.result.assert_not_called()
self.assertIsNone(res)


class TestKeyPairService(TestCase):
def setUp(self):
self.key_pair_service = KeyPairService()
self.group_name = "test_group_name"
self.storage_name = "teststoragename"
self.account_key = "test_account_key"
self.storage_client = mock.MagicMock()

@mock.patch("cloudshell.cp.azure.domain.services.key_pair.RSA")
@mock.patch("cloudshell.cp.azure.domain.services.key_pair.SSHKey")
def test_generate_key_pair(self, ssh_key_class, rsa_module):
"""Check that method uses RSA module to generate key pair and returns SSHKey model"""
ssh_key_class.return_value = ssh_key_mock = mock.MagicMock()

ssh_key = self.key_pair_service.generate_key_pair()

ssh_key_class.assert_called_with(private_key=rsa_module.generate().exportKey(),
public_key=rsa_module.generate().publickey().exportKey())
self.assertIs(ssh_key, ssh_key_mock)

@mock.patch("cloudshell.cp.azure.domain.services.key_pair.FileService")
def test_save_key_pair(self, file_service_class):
"""Check that method uses storage client to save key pair to the Azure"""
key_pair = mock.MagicMock()
file_service = mock.MagicMock()
file_service_class.return_value = file_service

self.key_pair_service.save_key_pair(
account_key=self.account_key,
key_pair=key_pair,
group_name=self.group_name,
storage_name=self.storage_name)

file_service_class.assert_called_once_with(account_key=self.account_key, account_name=self.storage_name)
file_service.create_share.assert_called_once_with(self.key_pair_service.FILE_SHARE_NAME)

file_service.create_file_from_bytes.assert_any_call(share_name=self.key_pair_service.FILE_SHARE_NAME,
directory_name=self.key_pair_service.FILE_SHARE_DIRECTORY,
file_name=self.key_pair_service.SSH_PUB_KEY_NAME,
file=key_pair.public_key)

file_service.create_file_from_bytes.assert_any_call(share_name=self.key_pair_service.FILE_SHARE_NAME,
directory_name=self.key_pair_service.FILE_SHARE_DIRECTORY,
file_name=self.key_pair_service.SSH_PRIVATE_KEY_NAME,
file=key_pair.private_key)

@mock.patch("cloudshell.cp.azure.domain.services.key_pair.SSHKey")
@mock.patch("cloudshell.cp.azure.domain.services.key_pair.FileService")
def test_get_key_pair(self, file_service_class, ssh_key_class):
"""Check that method uses storage client to retrieve key pair from the Azure"""
file_service = mock.MagicMock()
file_service_class.return_value = file_service
ssh_key_class.return_value = mocked_key_pair = mock.MagicMock()

key_pair = self.key_pair_service.get_key_pair(
account_key=self.account_key,
group_name=self.group_name,
storage_name=self.storage_name)

file_service_class.assert_called_once_with(account_key=self.account_key, account_name=self.storage_name)

file_service.get_file_to_bytes.assert_any_call(
share_name=self.key_pair_service.FILE_SHARE_NAME,
directory_name=self.key_pair_service.FILE_SHARE_DIRECTORY,
file_name=self.key_pair_service.SSH_PUB_KEY_NAME)

file_service.get_file_to_bytes.assert_any_call(
share_name=self.key_pair_service.FILE_SHARE_NAME,
directory_name=self.key_pair_service.FILE_SHARE_DIRECTORY,
file_name=self.key_pair_service.SSH_PRIVATE_KEY_NAME)

self.assertIs(key_pair, mocked_key_pair)

0 comments on commit ecf5578

Please sign in to comment.