Merge branch 'master' of https://github.com/tjprescott/azure-cli into StorageAccountMergeAttempt2

# Conflicts:
#	azure-cli.pyproj
#	src/command_modules/azure-cli-resource/azure/cli/command_modules/resource/__init__.py
#	testall.bat
This commit is contained in:
Travis Prescott 2016-04-08 16:23:49 -07:00
Родитель 7c96231871 e778eef2a4
Коммит a40fcbdb90
6 изменённых файлов: 272 добавлений и 80 удалений

Просмотреть файл

@ -12,7 +12,7 @@
<OutputPath>.</OutputPath>
<ProjectTypeGuids>{888888a0-9f3d-457c-b088-3a5042f75d52}</ProjectTypeGuids>
<LaunchProvider>Standard Python launcher</LaunchProvider>
<InterpreterId>{1dd9c42b-5980-42ce-a2c3-46d3bf0eede4}</InterpreterId>
<InterpreterId>{54f4b6dc-0859-46dc-99bb-b275c9d0aca3}</InterpreterId>
<InterpreterVersion>3.5</InterpreterVersion>
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
<CommandLineArguments>
@ -36,6 +36,10 @@
<Compile Include="azure\cli\extensions\transform.py" />
<Compile Include="azure\cli\extensions\__init__.py" />
<Compile Include="azure\cli\main.py" />
<Compile Include="azure\cli\tests\test_add_resourcegroup_transform.py" />
<Compile Include="command_modules\azure-cli-resource\azure\cli\command_modules\resource\tests\test_api_check.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="azure\cli\parser.py" />
<Compile Include="azure\cli\tests\test_add_resourcegroup_transform.py" />
<Compile Include="azure\cli\tests\test_parser.py" />
@ -178,6 +182,19 @@
<Content Include="command_modules\azure-cli-taskhelp\requirements.txt" />
<Content Include="command_modules\azure-cli-vm\requirements.txt" />
</ItemGroup>
<ItemGroup>
<Interpreter Include="..\env\">
<Id>{54f4b6dc-0859-46dc-99bb-b275c9d0aca3}</Id>
<BaseInterpreter>{2af0f10d-7135-4994-9156-5d01c9c11b7e}</BaseInterpreter>
<Version>3.5</Version>
<Description>env (Python 3.5)</Description>
<InterpreterPath>Scripts\python.exe</InterpreterPath>
<WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
<LibraryPath>Lib\</LibraryPath>
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
<Architecture>X86</Architecture>
</Interpreter>
</ItemGroup>
<Import Project="$(PtvsTargetsFile)" Condition="Exists($(PtvsTargetsFile))" />
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="!Exists($(PtvsTargetsFile))" />
</Project>

Просмотреть файл

@ -21,13 +21,14 @@ from codecs import open
from setuptools import setup
VERSION = '0.0.32'
INSTALL_FROM_PUBLIC = False
PRIVATE_PYPI_URL_ENV_NAME = 'AZURE_CLI_PRIVATE_PYPI_URL'
PRIVATE_PYPI_URL = os.environ.get(PRIVATE_PYPI_URL_ENV_NAME)
PRIVATE_PYPI_HOST_ENV_NAME = 'AZURE_CLI_PRIVATE_PYPI_HOST'
PRIVATE_PYPI_HOST = os.environ.get(PRIVATE_PYPI_HOST_ENV_NAME)
INSTALL_FROM_PRIVATE = bool(PRIVATE_PYPI_URL and PRIVATE_PYPI_HOST)
# If we have source, validate that our version numbers match
# This should prevent uploading releases with mismatched versions.
try:
@ -83,10 +84,7 @@ def _post_install(dir):
from subprocess import check_call
# Upgrade/update will install if it doesn't exist.
# We do this so these components are updated when the user updates the CLI.
if INSTALL_FROM_PUBLIC:
pip.main(['install', '--upgrade', 'azure-cli-component', '--disable-pip-version-check'])
check_call(['az', 'component', 'update', '-n', 'profile'])
else:
if INSTALL_FROM_PRIVATE:
# use private PyPI server.
if not PRIVATE_PYPI_URL:
raise RuntimeError('{} environment variable not set.'.format(PRIVATE_PYPI_URL_ENV_NAME))
@ -96,6 +94,9 @@ def _post_install(dir):
PRIVATE_PYPI_URL, '--trusted-host', PRIVATE_PYPI_HOST,
'--disable-pip-version-check'])
check_call(['az', 'component', 'update', '-n', 'profile', '-p'])
else:
pip.main(['install', '--upgrade', 'azure-cli-component', '--disable-pip-version-check'])
check_call(['az', 'component', 'update', '-n', 'profile'])
class OnInstall(install):
def run(self):

Просмотреть файл

@ -326,15 +326,15 @@ class CredsCache(object):
self.adal_token_cache = adal.TokenCache(json.dumps(real_token))
return self.adal_token_cache
def save_service_principal_cred(self, client_id, secret, tenant):
def save_service_principal_cred(self, service_principal_id, secret, tenant):
entry = {
_SERVICE_PRINCIPAL_ID: client_id,
_SERVICE_PRINCIPAL_ID: service_principal_id,
_SERVICE_PRINCIPAL_TENANT: tenant,
_ACCESS_TOKEN: secret
}
matched = [x for x in self._service_principal_creds
if client_id == x[_SERVICE_PRINCIPAL_ID] and
if service_principal_id == x[_SERVICE_PRINCIPAL_ID] and
tenant == x[_SERVICE_PRINCIPAL_TENANT]]
state_changed = False
if matched:

Просмотреть файл

@ -226,26 +226,181 @@ class Test_Profile(unittest.TestCase):
self.assertEqual(mock_read_cred_file.call_count, 1)
self.assertEqual(mock_persist_creds.call_count, 1)
def test_find_subscriptions_thru_username_password(self):
finder = SubscriptionFinder(lambda _,_2:AuthenticationContextStub(Test_Profile),
@mock.patch('adal.AuthenticationContext', autospec=True)
def test_find_subscriptions_thru_username_password(self, mock_auth_context):
mock_auth_context.acquire_token_with_username_password.return_value = self.token_entry1
mock_auth_context.acquire_token.return_value = self.token_entry1
mock_arm_client = mock.MagicMock()
mock_arm_client.tenants.list.return_value = [TenantStub(self.tenant_id)]
mock_arm_client.subscriptions.list.return_value = [self.subscription1]
finder = SubscriptionFinder(lambda _,_2: mock_auth_context,
None,
lambda _: ArmClientStub(Test_Profile))
subs = finder.find_from_user_account('foo', 'bar')
self.assertEqual([self.subscription1], subs)
lambda _: mock_arm_client)
def test_find_through_interactive_flow(self):
finder = SubscriptionFinder(lambda _,_2:AuthenticationContextStub(Test_Profile),
#action
subs = finder.find_from_user_account(self.user1, 'bar')
#assert
self.assertEqual([self.subscription1], subs)
mock_auth_context.acquire_token_with_username_password.assert_called_once_with(
'https://management.core.windows.net/', self.user1, 'bar', mock.ANY)
mock_auth_context.acquire_token.assert_called_once_with(
'https://management.core.windows.net/', self.user1, mock.ANY)
@mock.patch('adal.AuthenticationContext', autospec=True)
def test_find_subscriptions_through_interactive_flow(self, mock_auth_context):
test_nonsense_code = {'message':'magic code for you'}
mock_auth_context.acquire_user_code.return_value = test_nonsense_code
mock_auth_context.acquire_token_with_device_code.return_value = self.token_entry1
mock_arm_client = mock.MagicMock()
mock_arm_client.tenants.list.return_value = [TenantStub(self.tenant_id)]
mock_arm_client.subscriptions.list.return_value = [self.subscription1]
finder = SubscriptionFinder(lambda _,_2: mock_auth_context,
None,
lambda _: ArmClientStub(Test_Profile))
lambda _: mock_arm_client)
#action
subs = finder.find_through_interactive_flow()
#assert
self.assertEqual([self.subscription1], subs)
def test_find_from_service_principal_id(self):
finder = SubscriptionFinder(lambda _,_2:AuthenticationContextStub(Test_Profile),
mock_auth_context.acquire_user_code.assert_called_once_with(
'https://management.core.windows.net/', mock.ANY)
mock_auth_context.acquire_token_with_device_code.assert_called_once_with(
'https://management.core.windows.net/', test_nonsense_code, mock.ANY)
mock_auth_context.acquire_token.assert_called_once_with(
'https://management.core.windows.net/', self.user1, mock.ANY)
@mock.patch('adal.AuthenticationContext', autospec=True)
def test_find_subscriptions_from_service_principal_id(self, mock_auth_context):
mock_auth_context.acquire_token_with_client_credentials.return_value = self.token_entry1
mock_arm_client = mock.MagicMock()
mock_arm_client.subscriptions.list.return_value = [self.subscription1]
finder = SubscriptionFinder(lambda _,_2:mock_auth_context,
None,
lambda _: ArmClientStub(Test_Profile))
lambda _: mock_arm_client)
#action
subs = finder.find_from_service_principal_id('my app', 'my secret', self.tenant_id)
#assert
self.assertEqual([self.subscription1], subs)
mock_arm_client.tenants.list.assert_not_called()
mock_auth_context.acquire_token.assert_not_called()
mock_auth_context.acquire_token_with_client_credentials.assert_called_once_with(
'https://management.core.windows.net/', 'my app', 'my secret')
@mock.patch('azure.cli._profile._read_file_content', autospec=True)
def test_credscache_load_tokens_and_sp_creds(self, mock_read_file):
test_sp = {
"servicePrincipalId": "myapp",
"servicePrincipalTenant": "mytenant",
"accessToken": "Secret"
}
mock_read_file.return_value = json.dumps([self.token_entry1, test_sp])
#action
creds_cache = CredsCache()
#assert
token_entries = [entry for _, entry in creds_cache.adal_token_cache.read_items()]
self.assertEqual(token_entries, [self.token_entry1])
self.assertEqual(creds_cache._service_principal_creds,[test_sp])
@mock.patch('azure.cli._profile._read_file_content', autospec=True)
@mock.patch('azure.cli._profile.codecs_open', autospec=True)
def test_credscache_add_new_sp_creds(self, mock_open_for_write, mock_read_file):
test_sp = {
"servicePrincipalId": "myapp",
"servicePrincipalTenant": "mytenant",
"accessToken": "Secret"
}
test_sp2 = {
"servicePrincipalId": "myapp2",
"servicePrincipalTenant": "mytenant2",
"accessToken": "Secret2"
}
mock_open_for_write.return_value = FileHandleStub()
mock_read_file.return_value = json.dumps([self.token_entry1, test_sp])
creds_cache = CredsCache()
#action
creds_cache.save_service_principal_cred(
test_sp2['servicePrincipalId'],
test_sp2['accessToken'],
test_sp2['servicePrincipalTenant'])
#assert
token_entries = [entry for _, entry in creds_cache.adal_token_cache.read_items()]
self.assertEqual(token_entries, [self.token_entry1])
self.assertEqual(creds_cache._service_principal_creds,[test_sp, test_sp2])
mock_open_for_write.assert_called_with(mock.ANY, 'w', encoding='ascii')
@mock.patch('azure.cli._profile._read_file_content', autospec=True)
@mock.patch('azure.cli._profile.codecs_open', autospec=True)
def test_credscache_remove_creds(self, mock_open_for_write, mock_read_file):
test_sp = {
"servicePrincipalId": "myapp",
"servicePrincipalTenant": "mytenant",
"accessToken": "Secret"
}
mock_open_for_write.return_value = FileHandleStub()
mock_read_file.return_value = json.dumps([self.token_entry1, test_sp])
creds_cache = CredsCache()
#action #1, logout a user
creds_cache.remove_cached_creds(self.user1)
#assert #1
token_entries = [entry for _, entry in creds_cache.adal_token_cache.read_items()]
self.assertEqual(token_entries, [])
#action #2 logout a service principal
creds_cache.remove_cached_creds('myapp')
#assert #2
self.assertEqual(creds_cache._service_principal_creds,[])
mock_open_for_write.assert_called_with(mock.ANY, 'w', encoding='ascii')
self.assertEqual(mock_open_for_write.call_count, 2)
@mock.patch('azure.cli._profile._read_file_content', autospec=True)
@mock.patch('azure.cli._profile.codecs_open', autospec=True)
@mock.patch('adal.AuthenticationContext', autospec=True)
def test_credscache_new_token_added_by_adal(self, mock_adal_auth_context, mock_open_for_write, mock_read_file):
token_entry2 = {
"accessToken": "new token",
"userId": self.user1
}
def acquire_token_side_effect(*args):
creds_cache.adal_token_cache.has_state_changed = True
return token_entry2
def get_auth_context(authority, **kwargs):
mock_adal_auth_context.cache = kwargs['cache']
return mock_adal_auth_context
mock_adal_auth_context.acquire_token.side_effect = acquire_token_side_effect
mock_open_for_write.return_value = FileHandleStub()
mock_read_file.return_value = json.dumps([self.token_entry1])
creds_cache = CredsCache(auth_ctx_factory=get_auth_context)
token = creds_cache.retrieve_token_for_user(self.user1, self.tenant_id)
#action
mock_adal_auth_context.acquire_token.assert_called_once_with(
'https://management.core.windows.net/',
self.user1,
mock.ANY)
#assert
mock_open_for_write.assert_called_with(mock.ANY, 'w', encoding='ascii')
self.assertEqual(token, 'new token')
class FileHandleStub:
def write(self, content):
pass
def __enter__(self):
return self
def __exit__(self, _2, _3, _4):
pass
class SubscriptionStub:
def __init__(self, id, display_name, state, tenant_id):
@ -254,47 +409,9 @@ class SubscriptionStub:
self.state = state
self.tenant_id = tenant_id
class AuthenticationContextStub:
def __init__(self, test_profile_cls, return_token1=True):
#we need to reference some pre-defined test artifacts in Test_Profile
self._test_profile_cls = test_profile_cls
if not return_token1:
raise ValueError('Please update to return other test tokens')
def acquire_token_with_username_password(self, _, _2, _3, _4):
return self._test_profile_cls.token_entry1
def acquire_token_with_device_code(self, _, _2, _3):
return self._test_profile_cls.token_entry1
def acquire_token_with_client_credentials(self, _, _2, _3):
return self._test_profile_cls.token_entry1
def acquire_token(self, _, _2, _3):
return self._test_profile_cls.token_entry1
def acquire_user_code(self, _, _2):
return {'message': 'secret code for you'}
class ArmClientStub:
class TenantStub:
def __init__(self, tenant_id):
self.tenant_id = tenant_id
class OperationsStub:
def __init__(self, list_result):
self._list_result = list_result
def list(self):
return self._list_result
def __init__(self, test_profile_cls, use_tenant1_and_subscription1=True):
self._test_profile_cls = test_profile_cls
if use_tenant1_and_subscription1:
self.tenants = ArmClientStub.OperationsStub([ArmClientStub.TenantStub(test_profile_cls.tenant_id)])
self.subscriptions = ArmClientStub.OperationsStub([test_profile_cls.subscription1])
else:
raise ValueError('Please update to return other test subscriptions')
class TenantStub:
def __init__(self, tenant_id):
self.tenant_id = tenant_id
if __name__ == '__main__':
unittest.main()

Просмотреть файл

@ -6,8 +6,8 @@ from azure.cli._locale import L
command_table = CommandTable()
def _resource_client_factory(*args): # pylint: disable=unused-argument
from azure.mgmt.resource.resources import (ResourceManagementClient,
ResourceManagementClientConfiguration)
from azure.mgmt.resource.resources import (ResourceManagementClient,
ResourceManagementClientConfiguration)
return get_mgmt_service_client(ResourceManagementClient, ResourceManagementClientConfiguration)
@command_table.command('resource group list', description=L('List resource groups'))
@ -38,7 +38,7 @@ def list_groups(args):
help=L('the resource type in format: <provider-namespace>/<type>'),
required=True)
@command_table.option('--api-version -o', help=L('the API version of the resource provider'))
@command_table.option('--parent',
@command_table.option('--parent', default='',
help=L('the name of the parent resource (if needed), ' + \
'in <parent-type>/<parent-name> format'))
def show_resource(args):
@ -56,9 +56,8 @@ def show_resource(args):
raise IncorrectUsageError(
L('API version is required and could not be resolved for resource {}'
.format(full_type)))
results = rmc.resources.get(
resource_group_name=args.get('resource_group'),
resource_group_name=args.get('resourcegroup'),
resource_name=args.get('name'),
resource_provider_namespace=provider_namespace,
resource_type=resource_type,
@ -88,18 +87,12 @@ def _resolve_api_version(args, rmc):
raise IncorrectUsageError('Parameter --parent must be in <type>/<name> format.')
resource_type = "{}/{}".format(parent_type, resource_type)
else:
resource_type = resource_type
provider = rmc.providers.get(provider_namespace)
for t in provider.resource_types:
if t.resource_type == resource_type:
# Return first non-preview version
for version in t.api_versions:
if not version.find('preview'):
return version
# No non-preview version found. Take first preview version
try:
return t.api_versions[0]
except IndexError:
return None
rt = [t for t in provider.resource_types if t.resource_type == resource_type]
if not rt:
raise IncorrectUsageError('Resource type {} not found.'.format(full_type))
if len(rt) == 1 and rt[0].api_versions:
npv = [v for v in rt[0].api_versions if "preview" not in v]
return npv[0] if npv else rt[0].api_versions[0]
return None

Просмотреть файл

@ -0,0 +1,64 @@
import unittest
try:
from unittest.mock import MagicMock
except ImportError:
from mock import MagicMock
from azure.cli.command_modules.resource import _resolve_api_version as resolve_api_version
class TestApiCheck(unittest.TestCase):
@classmethod
def setUpClass(cls):
pass
@classmethod
def tearDownClass(cls):
pass
def setUp(self):
pass
def tearDown(self):
pass
def test_resolve_api_max_priority_option(self):
""" Verifies the --api-version parameter has maximum priority. """
args = {'api-version': '2015-01-01', 'resource-type': 'Mock/test'}
self.assertEqual(resolve_api_version(args, self._get_mock_client()), "2015-01-01")
def test_resolve_api_provider_backup(self):
""" Verifies provider is used as backup if api-version not specified. """
args = {'resource-type': 'Mock/test'}
self.assertEqual(resolve_api_version(args, self._get_mock_client()), "2016-01-01")
def test_resolve_api_provider_with_parent_backup(self):
""" Verifies provider (with parent) is used as backup if api-version not specified. """
args = {'resource-type': 'Mock/bar', 'parent': 'foo/testfoo123'}
self.assertEqual(resolve_api_version(args, self._get_mock_client()), "1999-01-01")
def test_resolve_api_all_previews(self):
""" Verifies most recent preview version returned only if there are no non-preview versions. """
args = {'resource-type': 'Mock/preview'}
self.assertEqual(resolve_api_version(args, self._get_mock_client()), "2005-01-01-preview")
def _get_mock_client(self):
client = MagicMock()
provider = MagicMock()
provider.resource_types = [
self._get_mock_resource_type('skip', ['2000-01-01-preview', '2000-01-01']),
self._get_mock_resource_type('test', ['2016-01-01-preview', '2016-01-01']),
self._get_mock_resource_type('foo/bar', ['1999-01-01-preview', '1999-01-01']),
self._get_mock_resource_type('preview', ['2005-01-01-preview', '2004-01-01-preview'])
]
client.providers.get.return_value = provider
return client
def _get_mock_resource_type(self, name, api_versions):
rt = MagicMock()
rt.resource_type = name
rt.api_versions = api_versions
return rt
if __name__ == '__main__':
unittest.main()