Change the src folder location, and update the scripts.

This commit is contained in:
xingwu1 2017-03-02 10:57:50 -08:00
Родитель e7f303d5d6
Коммит 03923a6dfe
51 изменённых файлов: 645 добавлений и 106 удалений

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

@ -1,52 +0,0 @@
#---------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
#---------------------------------------------------------------------------------------------
# This Dockerfile uses the latest code from the Git repo.
# Clone the repo then run 'docker build' with this Dockerfile file to get the latest versions of
# *all* CLI modules as in the Git repo.
FROM python:3.5.2-alpine
WORKDIR azure-cli
COPY . /azure-cli
# pip wheel - required for CLI packaging
# jmespath-terminal - we include jpterm as a useful tool
RUN pip install --upgrade pip wheel jmespath-terminal
# bash gcc openssl-dev libffi-dev musl-dev - dependencies required for CLI
# jq - we include jq as a useful tool
# openssh - included for ssh-keygen
# ca-certificates
# wget - required for installing jp
RUN apk update && apk add bash gcc openssl-dev libffi-dev musl-dev jq openssh ca-certificates wget openssl && update-ca-certificates
# We also, install jp
RUN wget https://github.com/jmespath/jp/releases/download/0.1.2/jp-linux-amd64 -qO /usr/local/bin/jp && chmod +x /usr/local/bin/jp
# 1. Build packages and store in tmp dir
# 2. Install the cli and the other command modules that weren't included
RUN /bin/bash -c 'TMP_PKG_DIR=$(mktemp -d); \
for d in src/azure-cli src/azure-cli-core src/azure-cli-nspkg src/command_modules/azure-cli-*/; \
do cd $d; python setup.py bdist_wheel -d $TMP_PKG_DIR; cd -; \
done; \
MODULE_NAMES=""; \
for m in src/command_modules/azure-cli-*/; \
do MODULE_NAMES="$MODULE_NAMES $(echo $m | cut -d '/' -f 3)"; \
done; \
pip install azure-cli $MODULE_NAMES -f $TMP_PKG_DIR;'
# Tab completion
RUN echo -e "\
_python_argcomplete() {\n\
local IFS='\v'\n\
COMPREPLY=( \$(IFS=\"\$IFS\" COMP_LINE=\"\$COMP_LINE\" COMP_POINT=\"\$COMP_POINT\" _ARGCOMPLETE_COMP_WORDBREAKS=\"\$COMP_WORDBREAKS\" _ARGCOMPLETE=1 \"\$1\" 8>&1 9>&2 1>/dev/null 2>/dev/null) )\n\
if [[ \$? != 0 ]]; then\n\
unset COMPREPLY\n\
fi\n\
}\n\
complete -o nospace -F _python_argcomplete \"az\"\n\
" > ~/.bashrc
WORKDIR /
CMD bash

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

@ -1,21 +0,0 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

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

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{938454f7-93bd-41a7-84b2-3c89d64b969d}</ProjectGuid>
<ProjectHome>src\</ProjectHome>
<StartupFile>azure-cli\azure\cli\__main__.py</StartupFile>
<SearchPath>.</SearchPath>
<WorkingDirectory>
</WorkingDirectory>
<OutputPath>.</OutputPath>
<ProjectTypeGuids>{888888a0-9f3d-457c-b088-3a5042f75d52}</ProjectTypeGuids>
<LaunchProvider>Standard Python launcher</LaunchProvider>
<InterpreterId>{54f4b6dc-0859-46dc-99bb-b275c9d0aca3}</InterpreterId>
<InterpreterVersion>3.5</InterpreterVersion>
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
<CommandLineArguments>
</CommandLineArguments>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'" />
<PropertyGroup Condition="'$(Configuration)' == 'Release'" />
<PropertyGroup>
<VisualStudioVersion Condition=" '$(VisualStudioVersion)' == '' ">10.0</VisualStudioVersion>
<PtvsTargetsFile>$(MSBuildextensionPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets</PtvsTargetsFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\commands.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\custom.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\test_file_utils.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\test_template_utils.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\__init__.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\_client_factory.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\_file_utils.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\_help.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\_job_utils.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\_params.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\_pool_utils.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\_shipyard_utils.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\_template_utils.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\_validators.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\__init__.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\__init__.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\cli\__init__.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\azure\__init__.py" />
<Compile Include="command_modules\azure-cli-batch-extensions\setup.py" />
<Folder Include="command_modules\" />
<Folder Include="command_modules\azure-cli-batch-extensions\" />
<Folder Include="command_modules\azure-cli-batch-extensions\azure\" />
<Folder Include="command_modules\azure-cli-batch-extensions\azure\cli\" />
<Folder Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\" />
<Folder Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\" />
<Folder Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\" />
<Folder Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\" />
</ItemGroup>
<ItemGroup>
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch-applicationTemplate-parameters.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch-applicationTemplate-prohibitedApplicationTemplateInfo.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch-applicationTemplate-prohibitedId.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch-applicationTemplate-prohibitedPoolInfo.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch-applicationTemplate-prohibitedPriority.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch-applicationTemplate-static.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch-applicationTemplate-unsupportedProperty.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch-applicationTemplate-untypedParameter.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch.job.parameters.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch.job.parametricsweep.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch.job.simple.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch.pool.parameters.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch.pool.simple.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch.shipyard.job.json" />
<Content Include="command_modules\azure-cli-batch-extensions\azure\cli\command_modules\batch_extensions\tests\data\batch.shipyard.pool.json" />
<Content Include="command_modules\azure-cli-batch-extensions\HISTORY.rst" />
<Content Include="command_modules\azure-cli-batch-extensions\MANIFEST.in" />
<Content Include="command_modules\azure-cli-batch-extensions\README.rst" />
<Content Include="command_modules\azure-cli-batch-extensions\setup.cfg" />
</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>

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

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "azure-batch-cli-extensions", "azure-batch-cli-extensions.pyproj", "{938454F7-93BD-41A7-84B2-3C89D64B969D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{938454F7-93BD-41A7-84B2-3C89D64B969D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{938454F7-93BD-41A7-84B2-3C89D64B969D}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

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

@ -16,4 +16,5 @@ max-locals=25
max-branches=20
[SIMILARITIES]
min-similarity-lines=10
[Master]
init-hook='import sys; sys.path.extend(["./src/command_modules/azure-cli-batch-extensions/azure", "./env/Lib/site-packages/azure"])'

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

@ -68,14 +68,6 @@ if __name__ == '__main__':
# Run pylint on all modules
return_code_sum = run_pylint(selected_modules)
# Run flake8 on white-listed modules
pep8_ready_modules = automation_path.filter_user_selected_modules(
['azure-cli', 'azure-cli-core', 'azure-cli-nspkg', 'acs',
'component', 'cloud', 'feedback', 'profile', 'sql', 'storage',
'vm'])
return_code_sum += run_pep8(pep8_ready_modules)
sys.exit(return_code_sum)
selected_modules = automation_path.filter_user_selected_modules(args.modules)

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

@ -19,7 +19,7 @@ def get_repo_root():
def get_all_module_paths():
"""List all core and command modules"""
return list(get_core_modules_paths()) + get_command_modules_paths(include_prefix=True)
return get_command_modules_paths(include_prefix=True)
def get_command_modules_paths(include_prefix=False):
@ -40,23 +40,6 @@ def get_command_modules_paths_with_tests():
yield name, module_path, test_path
def get_core_modules_paths():
def _get_path(name):
return os.path.join(get_repo_root(), 'src', name)
yield 'azure-cli', _get_path('azure-cli')
yield 'azure-cli-core', _get_path('azure-cli-core')
yield 'azure-cli-nspkg', _get_path('azure-cli-nspkg')
def get_core_modules_paths_with_tests():
# the pattern for the test folder here is not consistent with command modules'
yield 'azure-cli', os.path.join(get_repo_root(), 'src', 'azure-cli'), \
os.path.join(get_repo_root(), 'src', 'azure-cli', 'azure', 'cli', 'tests')
yield 'azure-cli-core', os.path.join(get_repo_root(), 'src', 'azure-cli-core'), \
os.path.join(get_repo_root(), 'src', 'azure-cli-core', 'azure', 'cli', 'core', 'tests')
def make_dirs(path):
"""Create a directories recursively"""
import errno
@ -96,8 +79,7 @@ def get_test_results_dir(with_timestamp=None, prefix=None):
def filter_user_selected_modules(user_input_modules):
import itertools
existing_modules = list(itertools.chain(get_core_modules_paths(),
get_command_modules_paths()))
existing_modules = list(itertools.chain(get_command_modules_paths()))
if user_input_modules:
selected_modules = set(user_input_modules)
@ -115,8 +97,7 @@ def filter_user_selected_modules(user_input_modules):
def filter_user_selected_modules_with_tests(user_input_modules):
import itertools
existing_modules = list(itertools.chain(get_core_modules_paths_with_tests(),
get_command_modules_paths_with_tests()))
existing_modules = list(itertools.chain(get_command_modules_paths_with_tests()))
if user_input_modules:
selected_modules = set(user_input_modules)

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

@ -28,6 +28,6 @@ print('Root directory \'{}\'\n'.format(root_dir))
exec_command('pip install -r requirements.txt')
# install reference to extension module package
exec_command('pip install -e src')
exec_command('pip install -e src/command_modules/azure-cli-batch-extensions')
print('Finished dev setup.')

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

@ -11,7 +11,7 @@ import datetime
from azure.storage import CloudStorageAccount
from azure.storage.blob import BlobPermissions
from azure.batch.models import BatchErrorException
from azure.cli.core.test_utils.vcr_test_base import VCRTestBase
from azure.cli.command_modules.batch_extensions.tests.vcr_test_base import VCRTestBase
class TestFileUpload(VCRTestBase):

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

@ -0,0 +1,522 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
# pylint: disable=wrong-import-order
from __future__ import print_function
import collections
import json
import os
import re
import shlex
import sys
import tempfile
from random import choice
from string import digits, ascii_lowercase
from six.moves.urllib.parse import urlparse, parse_qs # pylint: disable=import-error
import unittest
try:
import unittest.mock as mock
except ImportError:
import mock
import vcr
import jmespath
from six import StringIO
# TODO Should not depend on azure.cli.main package here.
# Will be ok if this test file is not part of azure.cli.core.utils
from azure.cli.main import main as cli_main
from azure.cli.core import __version__ as core_version
import azure.cli.core._debug as _debug
from azure.cli.core._profile import Profile
from azure.cli.core._util import CLIError, random_string
LIVE_TEST_CONTROL_ENV = 'AZURE_CLI_TEST_RUN_LIVE'
COMMAND_COVERAGE_CONTROL_ENV = 'AZURE_CLI_TEST_COMMAND_COVERAGE'
MOCKED_SUBSCRIPTION_ID = '00000000-0000-0000-0000-000000000000'
MOCKED_TENANT_ID = '00000000-0000-0000-0000-000000000000'
MOCKED_STORAGE_ACCOUNT = 'dummystorage'
# MOCK METHODS
# Workaround until https://github.com/kevin1024/vcrpy/issues/293 is fixed.
vcr_connection_request = vcr.stubs.VCRConnection.request
def patch_vcr_connection_request(*args, **kwargs):
kwargs.pop('encode_chunked', None)
vcr_connection_request(*args, **kwargs)
vcr.stubs.VCRConnection.request = patch_vcr_connection_request
def _mock_get_mgmt_service_client(client_type, subscription_bound=True, subscription_id=None,
api_version=None):
# version of _get_mgmt_service_client to use when recording or playing tests
profile = Profile()
cred, subscription_id, _ = profile.get_login_credentials(subscription_id=subscription_id)
if subscription_bound:
client = client_type(cred, subscription_id, api_version=api_version) \
if api_version else client_type(cred, subscription_id)
else:
client = client_type(cred, api_version=api_version) \
if api_version else client_type(cred)
client = _debug.allow_debug_connection(client)
client.config.add_user_agent("AZURECLI/TEST/{}".format(core_version))
return (client, subscription_id)
def _mock_generate_deployment_name(namespace):
if not namespace.deployment_name:
namespace.deployment_name = 'mock-deployment'
def _mock_handle_exceptions(ex):
raise ex
def _mock_subscriptions(self): # pylint: disable=unused-argument
return [{
"id": MOCKED_SUBSCRIPTION_ID,
"user": {
"name": "example@example.com",
"type": "user"
},
"state": "Enabled",
"name": "Example",
"tenantId": MOCKED_TENANT_ID,
"isDefault": True}]
def _mock_user_access_token(_, _1, _2, _3): # pylint: disable=unused-argument
return ('Bearer', 'top-secret-token-for-you')
def _mock_operation_delay(_):
# don't run time.sleep()
return
# TEST CHECKS
class JMESPathCheckAssertionError(AssertionError):
def __init__(self, comparator, actual_result, json_data):
message = "Actual value '{}' != Expected value '{}'. ".format(
actual_result,
comparator.expected_result)
message += "Query '{}' used on json data '{}'".format(comparator.query, json_data)
super(JMESPathCheckAssertionError, self).__init__(message)
class JMESPathCheck(object): # pylint: disable=too-few-public-methods
def __init__(self, query, expected_result):
self.query = query
self.expected_result = expected_result
def compare(self, json_data):
actual_result = _search_result_by_jmespath(json_data, self.query)
if not actual_result == self.expected_result:
raise JMESPathCheckAssertionError(self, actual_result, json_data)
class JMESPathPatternCheck(object): # pylint: disable=too-few-public-methods
def __init__(self, query, expected_result):
self.query = query
self.expected_result = expected_result
def compare(self, json_data):
actual_result = _search_result_by_jmespath(json_data, self.query)
if not re.match(self.expected_result, str(actual_result), re.IGNORECASE):
raise JMESPathCheckAssertionError(self, actual_result, json_data)
class BooleanCheck(object): # pylint: disable=too-few-public-methods
def __init__(self, expected_result):
self.expected_result = expected_result
def compare(self, data):
result = str(str(data).lower() in ['yes', 'true', '1'])
try:
assert result == str(self.expected_result)
except AssertionError:
raise AssertionError("Actual value '{}' != Expected value {}".format(
result, self.expected_result))
class NoneCheck(object): # pylint: disable=too-few-public-methods
def __init__(self):
pass
def compare(self, data): # pylint: disable=no-self-use
none_strings = ['[]', '{}', 'false']
try:
assert not data or data in none_strings
except AssertionError:
raise AssertionError("Actual value '{}' != Expected value falsy (None, '', []) or "
"string in {}".format(data, none_strings))
class StringCheck(object): # pylint: disable=too-few-public-methods
def __init__(self, expected_result):
self.expected_result = expected_result
def compare(self, data):
try:
result = data.replace('"', '')
assert result == self.expected_result
except AssertionError:
raise AssertionError("Actual value '{}' != Expected value {}".format(
data, self.expected_result))
# HELPER METHODS
def _scrub_deployment_name(uri):
return re.sub('/deployments/([^/?]+)', '/deployments/mock-deployment', uri)
def _search_result_by_jmespath(json_data, query):
if not json_data:
json_data = '{}'
json_val = json.loads(json_data)
return jmespath.search(
query,
json_val,
jmespath.Options(collections.OrderedDict))
def _custom_request_matcher(r1, r2):
""" Ensure method, path, and query parameters match. """
if r1.method != r2.method:
return False
url1 = urlparse(r1.uri)
url2 = urlparse(r2.uri)
if url1.path != url2.path:
return False
q1 = parse_qs(url1.query)
q2 = parse_qs(url2.query)
shared_keys = set(q1.keys()).intersection(set(q2.keys()))
if len(shared_keys) != len(q1) or len(shared_keys) != len(q2):
return False
for key in shared_keys:
if q1[key][0].lower() != q2[key][0].lower():
return False
return True
# MAIN CLASS
class VCRTestBase(unittest.TestCase): # pylint: disable=too-many-instance-attributes
FILTER_HEADERS = [
'authorization',
'client-request-id',
'x-ms-client-request-id',
'x-ms-correlation-request-id',
'x-ms-ratelimit-remaining-subscription-reads',
'x-ms-request-id',
'x-ms-routing-request-id',
'x-ms-gateway-service-instanceid',
'x-ms-ratelimit-remaining-tenant-reads',
'x-ms-served-by',
]
# pylint: disable=too-many-arguments
def __init__(self, test_file, test_name, run_live=False, debug=False, debug_vcr=False,
skip_setup=False, skip_teardown=False):
super(VCRTestBase, self).__init__(test_name)
self.test_name = test_name
self.recording_dir = os.path.join(os.path.dirname(test_file), 'recordings')
self.cassette_path = os.path.join(self.recording_dir, '{}.yaml'.format(test_name))
self.playback = os.path.isfile(self.cassette_path)
if os.environ.get(LIVE_TEST_CONTROL_ENV, None) == 'True':
self.run_live = True
else:
self.run_live = run_live
self.skip_setup = skip_setup
self.skip_teardown = skip_teardown
self.success = False
self.exception = None
self.track_commands = os.environ.get(COMMAND_COVERAGE_CONTROL_ENV, None)
self._debug = debug
if not self.playback and ('--buffer' in sys.argv) and not run_live:
self.exception = CLIError('No recorded result provided for {}.'.format(self.test_name))
if debug_vcr:
import logging
logging.basicConfig()
vcr_log = logging.getLogger('vcr')
vcr_log.setLevel(logging.INFO)
self.my_vcr = vcr.VCR(
cassette_library_dir=self.recording_dir,
before_record_request=self._before_record_request,
before_record_response=self._before_record_response,
decode_compressed_response=True
)
self.my_vcr.register_matcher('custom', _custom_request_matcher)
self.my_vcr.match_on = ['custom']
def _track_executed_commands(self, command):
if self.track_commands:
with open(self.track_commands, 'a+') as f:
f.write(' '.join(command))
f.write('\n')
def _before_record_request(self, request): # pylint: disable=no-self-use
# scrub subscription from the uri
request.uri = re.sub('/subscriptions/([^/]+)/',
'/subscriptions/{}/'.format(MOCKED_SUBSCRIPTION_ID), request.uri)
request.uri = re.sub('/graph.windows.net/([^/]+)/',
'/graph.windows.net/{}/'.format(MOCKED_TENANT_ID), request.uri)
request.uri = re.sub('/sig=([^/]+)&', '/sig=0000&', request.uri)
request.uri = _scrub_deployment_name(request.uri)
# replace random storage account name with dummy name
request.uri = re.sub(r'(vcrstorage[\d]+)', MOCKED_STORAGE_ACCOUNT, request.uri)
# prevents URI mismatch between Python 2 and 3 if request URI has extra / chars
request.uri = re.sub('//', '/', request.uri)
request.uri = re.sub('/', '//', request.uri, count=1)
# do not record requests sent for token refresh'
if (request.body and 'grant-type=refresh_token' in str(request.body)) or \
('/oauth2/token' in request.uri):
request = None
return request
def _before_record_response(self, response): # pylint: disable=no-self-use
for key in VCRTestBase.FILTER_HEADERS:
if key in response['headers']:
del response['headers'][key]
def _scrub_body_parameters(value):
value = re.sub('/subscriptions/([^/]+)/',
'/subscriptions/{}/'.format(MOCKED_SUBSCRIPTION_ID), value)
return value
for key in response['body']:
value = response['body'][key].decode('utf-8')
value = _scrub_body_parameters(value)
try:
response['body'][key] = bytes(value, 'utf-8')
except TypeError:
response['body'][key] = value.encode('utf-8')
return response
@mock.patch('azure.cli.main.handle_exception', _mock_handle_exceptions)
@mock.patch('azure.cli.core.commands.client_factory._get_mgmt_service_client',
_mock_get_mgmt_service_client) # pylint: disable=line-too-long
def _execute_live_or_recording(self):
# pylint: disable=no-member
try:
set_up = getattr(self, "set_up", None)
if callable(set_up) and not self.skip_setup:
self.set_up()
if self.run_live:
self.body()
else:
with self.my_vcr.use_cassette(self.cassette_path):
self.body()
self.success = True
except Exception as ex:
raise ex
finally:
tear_down = getattr(self, "tear_down", None)
if callable(tear_down) and not self.skip_teardown:
self.tear_down()
@mock.patch('azure.cli.core._profile.Profile.load_cached_subscriptions', _mock_subscriptions)
@mock.patch('azure.cli.core._profile.CredsCache.retrieve_token_for_user',
_mock_user_access_token) # pylint: disable=line-too-long
@mock.patch('azure.cli.main.handle_exception', _mock_handle_exceptions)
@mock.patch('azure.cli.core.commands.client_factory._get_mgmt_service_client',
_mock_get_mgmt_service_client) # pylint: disable=line-too-long
@mock.patch('msrestazure.azure_operation.AzureOperationPoller._delay', _mock_operation_delay)
@mock.patch('time.sleep', _mock_operation_delay)
@mock.patch('azure.cli.core.commands.LongRunningOperation._delay', _mock_operation_delay)
@mock.patch('azure.cli.core.commands.validators.generate_deployment_name',
_mock_generate_deployment_name)
def _execute_playback(self):
# pylint: disable=no-member
with self.my_vcr.use_cassette(self.cassette_path):
self.body()
self.success = True
def _post_recording_scrub(self):
""" Perform post-recording cleanup on the YAML file that can't be accomplished with the
VCR recording hooks. """
src_path = self.cassette_path
rg_name = getattr(self, 'resource_group', None)
rg_original = getattr(self, 'resource_group_original', None)
t = tempfile.NamedTemporaryFile('r+')
with open(src_path, 'r') as f:
for line in f:
# scrub resource group names
if rg_name != rg_original:
line = line.replace(rg_name, rg_original)
# omit bearer tokens
if 'authorization:' not in line.lower():
t.write(line)
t.seek(0)
with open(src_path, 'w') as f:
for line in t:
f.write(line)
t.close()
# COMMAND METHODS
def cmd(self, command, checks=None, allowed_exceptions=None,
debug=False): # pylint: disable=no-self-use
allowed_exceptions = allowed_exceptions or []
if not isinstance(allowed_exceptions, list):
allowed_exceptions = [allowed_exceptions]
if self._debug or debug:
print('\n\tRUNNING: {}'.format(command))
command_list = shlex.split(command)
output = StringIO()
try:
cli_main(command_list, file=output)
except Exception as ex: # pylint: disable=broad-except
ex_msg = str(ex)
if not next((x for x in allowed_exceptions if x in ex_msg), None):
raise ex
self._track_executed_commands(command_list)
result = output.getvalue().strip()
output.close()
if self._debug or debug:
print('\tRESULT: {}\n'.format(result))
if checks:
checks = [checks] if not isinstance(checks, list) else checks
for check in checks:
check.compare(result)
if '-o' in command_list and 'tsv' in command_list:
return result
else:
try:
result = result or '{}'
return json.loads(result)
except Exception: # pylint: disable=broad-except
return result
def set_env(self, key, val): # pylint: disable=no-self-use
os.environ[key] = val
def pop_env(self, key): # pylint: disable=no-self-use
return os.environ.pop(key, None)
def execute(self):
''' Method to actually start execution of the test. Must be called from the test_<name>
method of the test class. '''
try:
if self.run_live:
print('RUN LIVE: {}'.format(self.test_name))
self._execute_live_or_recording()
elif self.playback:
print('PLAYBACK: {}'.format(self.test_name))
self._execute_playback()
else:
print('RECORDING: {}'.format(self.test_name))
self._execute_live_or_recording()
except Exception as ex:
raise ex
finally:
if not self.success and not self.playback and os.path.isfile(self.cassette_path):
print('DISCARDING RECORDING: {}'.format(self.cassette_path))
os.remove(self.cassette_path)
elif self.success and not self.playback and os.path.isfile(self.cassette_path):
try:
self._post_recording_scrub()
except Exception: # pylint: disable=broad-except
os.remove(self.cassette_path)
class ResourceGroupVCRTestBase(VCRTestBase):
# pylint: disable=too-many-arguments
def __init__(self, test_file, test_name, resource_group='vcr_resource_group', run_live=False,
debug=False, debug_vcr=False, skip_setup=False, skip_teardown=False):
super(ResourceGroupVCRTestBase, self).__init__(test_file, test_name, run_live=run_live,
debug=debug, debug_vcr=debug_vcr,
skip_setup=skip_setup,
skip_teardown=skip_teardown)
self.resource_group_original = resource_group
random_tag = '_{}_'.format(''.join((choice(ascii_lowercase + digits) for _ in range(4))))
self.resource_group = '{}{}'.format(resource_group, '' if self.playback else random_tag)
self.location = 'westus'
def set_up(self):
self.cmd('group create --location {} --name {} --tags use=az-test'.format(
self.location, self.resource_group))
def tear_down(self):
self.cmd('group delete --name {} --no-wait --yes'.format(self.resource_group))
class StorageAccountVCRTestBase(VCRTestBase):
account_location = 'westus'
account_sku = 'Standard_LRS'
# pylint: disable=too-many-arguments
def __init__(self, test_file, test_name, resource_group='vcr_resource_group', run_live=False,
debug=False, debug_vcr=False, skip_setup=False, skip_teardown=False):
super(StorageAccountVCRTestBase, self).__init__(test_file, test_name, run_live=run_live,
debug=debug, debug_vcr=debug_vcr,
skip_setup=skip_setup,
skip_teardown=skip_teardown)
self.resource_group_original = resource_group
self.resource_group = '{}{}'.format(resource_group,
'' if self.playback else self.generate_random_tag())
self.account = MOCKED_STORAGE_ACCOUNT if self.playback else self.generate_account_name()
def set_up(self):
self.cmd('group create --location {} --name {} --tags use=az-test'.format(
self.account_location, self.resource_group))
self.cmd('storage account create --sku {} -l {} -n {} -g {}'.format(
self.account_sku, self.account_location, self.account, self.resource_group))
def tear_down(self):
self.cmd('storage account delete -g {} -n {} --yes'.format(
self.resource_group, self.account))
self.cmd('group delete --name {} --no-wait --yes'.format(self.resource_group))
@classmethod
def generate_account_name(cls):
return 'vcrstorage{}'.format(random_string(12, digits_only=True))
@classmethod
def generate_random_tag(cls):
return '_{}_'.format(random_string(4, force_lower=True))

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

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