Skip to content
Merged
11 changes: 11 additions & 0 deletions src/command_modules/azure-cli-botservice/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
Release History
===============

0.1.5
+++++
* Improve UX around `az bot publish`
* Add warning for timeouts when running `npm install` during `az bot publish`
* Remove invalid char "." from `--name` in `az bot create`
* Stop randomizing resource names when creating Azure Storage, App Service Plan, Function/Web App and Application Insights
* Code cleanup in BotTemplateDeployer
* Deprecate `--proj-name` argument for `--proj-file-path`
* Update old `--proj-file` messages to instead use `--proj-file-path`


0.1.4
+++++
* Add deployment status updates to `az bot create`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
def load_arguments(self, _):
with self.argument_context('bot') as c:
c.argument('resource_group_name', arg_type=resource_group_name_type)
c.argument('resource_name', options_list=['--name', '-n'], help='The resource name of the bot.', arg_type=name_arg_type)
c.argument('resource_name', options_list=['--name', '-n'],
help='The resource name of the bot. Bot name must be between 4 and 42 characters in length. '
'Bot name can only have the following characters -, a - z, A - Z, 0 - 9, and _.',
arg_type=name_arg_type)

with self.argument_context('bot create') as c:
c.argument('sku_name', options_list=['--sku'], arg_type=get_enum_type(['F0', 'S1']), help='The Sku of the bot.', arg_group='Registration bot Specific')
Expand All @@ -39,7 +42,10 @@ def load_arguments(self, _):

with self.argument_context('bot publish') as c:
c.argument('code_dir', options_list=['--code-dir'], help='The directory to upload bot code from.')
c.argument('proj_name', help='Name of the start up project file name.')
c.argument('proj_file_path', options_list=['--proj-file-path', c.deprecate(target='--proj-name',
redirect='--proj-file-path',
hide=True, expiration='2.1.0')],
help='Path to the start up project file name. (E.g. "./EchoBotWithCounter.csproj")')
c.argument('version', options_list=['-v', '--version'],
help='The Microsoft Bot Builder SDK version of the bot.')

Expand All @@ -50,7 +56,11 @@ def load_arguments(self, _):
c.argument('bot_json', options_list=['--msbot'], help='Show the output as JSON compatible with a .bot file.', arg_type=get_three_state_flag())

with self.argument_context('bot prepare-publish') as c:
c.argument('proj_name', help='Name of the start up project file name. Required only for C#.')
c.argument('proj_file_path', options_list=['--proj-file-path', c.deprecate(target='--proj-name',
redirect='--proj-file-path',
hide=True, expiration='2.1.0')],
help='Path to the start up project file name. (E.g. "./EchoBotWithCounter.csproj") '
'Required only for C#.')
c.argument('sln_name', help='Name of the start up solution file name. Required only for C#.')
c.argument('code_dir', options_list=['--code-dir'], help='The directory to download deployment scripts to.')
c.argument('version', options_list=['-v', '--version'], help='The Microsoft Bot Builder SDK version to be used '
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def prepare_publish_v4(logger, code_dir, proj_file):
logger.info('Detected bot language C#, Bot Builder version v4.')

if proj_file is None:
raise CLIError('Expected --proj-file argument provided with the full path to the '
raise CLIError('Expected --proj-file-path argument provided with the full path to the '
'bot csproj file for csharp bot with Bot Builder SDK v4 project.')
with open('.deployment', 'w') as f:
f.write('[config]\n')
Expand Down Expand Up @@ -95,4 +95,4 @@ def __find_proj(proj_file):
for file_name in files:
if proj_file == file_name.lower():
return os.path.relpath(os.path.join(root, file_name))
raise CLIError('project file not found. Please pass a valid --proj-file.')
raise CLIError('project file not found. Please pass a valid --proj-file-path.')
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@

import json
import os
import random
import re
import string
import requests

from knack.util import CLIError
Expand Down Expand Up @@ -57,11 +55,11 @@ def deploy_arm_template(cli_ctx, resource_group_name,
properties = DeploymentProperties(template=template, template_link=None,
parameters=parameters, mode=mode)

smc = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES)
return LongRunningOperation(cli_ctx, 'Deploying ARM Tempalte')(smc.deployments.create_or_update(
resource_group_name,
deployment_name,
properties, raw=False))
resource_management_client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES)
return LongRunningOperation(cli_ctx, 'Deploying ARM Tempalte')(
resource_management_client.deployments.create_or_update(resource_group_name,
deployment_name,
properties, raw=False))

@staticmethod
def create_app(cmd, logger, client, resource_group_name, resource_name, description, kind, appid, password, # pylint:disable=too-many-statements
Expand Down Expand Up @@ -129,12 +127,13 @@ def create_app(cmd, logger, client, resource_group_name, resource_name, descript
create_new_storage = False
if not storageAccountName:
create_new_storage = True
storageAccountName = re.sub(r'[^a-z0-9]', '', resource_name[:24])
site_name = re.sub(r'[^a-z0-9\-]', '', resource_name[:40])

storageAccountName = re.sub(r'[^a-z0-9]', '', resource_name[:10] +
''.join(
random.choice(string.ascii_lowercase + string.digits) for _ in range(4)))
site_name = re.sub(r'[^a-z0-9]', '', resource_name[:15] +
''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(4)))
# The name of Azure Web Sites cannot end with "-", e.g. "testname-.azurewbesites.net" is invalid.
# The valid name would be "testname.azurewebsites.net"
while site_name[-1] == '-':
site_name = site_name[:-1]

logger.debug('Storage name not provided. If storage is to be created, name to be used is %s.',
storageAccountName)
Expand Down Expand Up @@ -177,8 +176,7 @@ def create_app(cmd, logger, client, resource_group_name, resource_name, descript

logger.debug('Detected V4 bot. Adding bot encryption key to Azure parameters.')

bot_encryption_key = BotTemplateDeployer.get_bot_file_encryption_key()
paramsdict['botFileEncryptionKey'] = bot_encryption_key
paramsdict['botFileEncryptionKey'] = BotTemplateDeployer.get_bot_file_encryption_key()
params = {k: {'value': v} for k, v in paramsdict.items()}

# Get and deploy ARM template
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,6 @@ def create(cmd, client, resource_group_name, resource_name, kind, description=No
:return:
"""

# If display name was not provided, just use the resource name
display_name = display_name or resource_name

# Kind parameter validation
kind = kind.lower()

Expand All @@ -66,6 +63,15 @@ def create(cmd, client, resource_group_name, resource_name, kind, description=No
webapp_kind = 'webapp'
function_kind = 'function'

if resource_name.find(".") > -1:
logger.warning('"." found in --name parameter ("%s"). "." is an invalid character for Azure Bot resource names '
'and will been removed.', resource_name)
# Remove or replace invalid "." character
resource_name = resource_name.replace(".", "")

# If display name was not provided, just use the resource name
display_name = display_name or resource_name

# Mapping: registration is deprecated, we now use 'bot' kind for registration bots
if kind == registration_kind:
kind = bot_kind
Expand Down Expand Up @@ -340,7 +346,8 @@ def get_service_providers(client, name=None):
return service_provider_response


def prepare_publish(cmd, client, resource_group_name, resource_name, sln_name, proj_name, code_dir=None, version='v3'): # pylint:disable=too-many-statements
def prepare_publish(cmd, client, resource_group_name, resource_name, sln_name, proj_file_path, code_dir=None, # pylint:disable=too-many-statements
version='v3'):
"""Adds PostDeployScripts folder with necessary scripts to deploy v3 bot to Azure.

This method is directly called via "bot prepare-publish"
Expand All @@ -350,7 +357,7 @@ def prepare_publish(cmd, client, resource_group_name, resource_name, sln_name, p
:param resource_group_name:
:param resource_name:
:param sln_name:
:param proj_name:
:param proj_file_path:
:param code_dir:
:param version:
:return:
Expand Down Expand Up @@ -429,7 +436,7 @@ def prepare_publish(cmd, client, resource_group_name, resource_name, sln_name, p
break
if fileName == sln_name:
solution_path = os.path.relpath(os.path.join(root, fileName))
if fileName == proj_name:
if fileName == proj_file_path:
csproj_path = os.path.relpath(os.path.join(root, fileName))

# Read deploy script contents
Expand All @@ -452,7 +459,7 @@ def prepare_publish(cmd, client, resource_group_name, resource_name, sln_name, p
logger.info('Bot prepare publish completed successfully.')


def publish_app(cmd, client, resource_group_name, resource_name, code_dir=None, proj_name=None, version='v3'):
def publish_app(cmd, client, resource_group_name, resource_name, code_dir=None, proj_file_path=None, version='v3'):
"""Publish local bot code to Azure.

This method is directly called via "bot publish"
Expand All @@ -462,7 +469,7 @@ def publish_app(cmd, client, resource_group_name, resource_name, code_dir=None,
:param resource_group_name:
:param resource_name:
:param code_dir:
:param proj_name:
:param proj_file_path:
:param version:
:return:
"""
Expand Down Expand Up @@ -492,10 +499,10 @@ def publish_app(cmd, client, resource_group_name, resource_name, code_dir=None,
if version == 'v4':

logger.info('Detected SDK version v4. Running prepare publish in code directory %s and for project file %s' # pylint:disable=logging-not-lazy
% (code_dir, proj_name))
% (code_dir, proj_file_path))

# Automatically run prepare-publish in case of v4.
BotPublishPrep.prepare_publish_v4(logger, code_dir, proj_name)
BotPublishPrep.prepare_publish_v4(logger, code_dir, proj_file_path)
else:
logger.info('Detected SDK version v3. PostDeploymentScripts folder not found in directory provided: %s',
code_dir)
Expand All @@ -518,6 +525,14 @@ def publish_app(cmd, client, resource_group_name, resource_name, code_dir=None,
logger.info('Detected language javascript. Installing node dependencies in remote bot.')
kudu_client.install_node_dependencies()

logger.info('Bot publish completed successfully.')
if output.get('active'):
logger.info('Deployment successful!')

if not output.get('active'):
scm_url = output.get('url')
deployment_id = output.get('id')
# Instead of replacing "latest", which would could be in the bot name, we replace "deployments/latest"
deployment_url = scm_url.replace('deployments/latest', 'deployments/%s' % deployment_id)
logger.error('Deployment failed. To find out more information about this deployment, please visit %s.',
deployment_url)
return output
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,23 @@ def install_node_dependencies(self):
'command': 'npm install',
'dir': r'site\wwwroot'
}
response = requests.post(self.__scm_url + '/api/command', data=json.dumps(payload),
headers=self.__get_application_json_headers())
HttpResponseValidator.check_response_status(response)
# npm install can take a very long time to complete. By default, Azure's load balancer will terminate
# connections after 230 seconds of no inbound or outbound packets. This timeout is not configurable.
try:
response = requests.post(self.__scm_url + '/api/command', data=json.dumps(payload),
headers=self.__get_application_json_headers())
HttpResponseValidator.check_response_status(response)
except CLIError as e:
if response.status_code == 500 and 'The request timed out.' in response.text:
self.__logger.warning('npm install is taking longer than expected and did not finish within the '
'Azure-specified timeout of 230 seconds.')
self.__logger.warning('The installation is likely still in progress. This is a known issue, please wait'
' a short while before messaging your bot. You can also visit Kudu to manually '
'install the npm dependencies. (https://github.com/projectkudu/kudu/wiki)')
self.__logger.warning('Your Kudu website for this bot is: %s' % self.__scm_url)
else:
raise e

return response.json()

def publish(self, zip_file_path):
Expand Down
Loading