-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Websites: Add Multicontainer Capabilities #6255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
68e0227
9d383bf
dbb2757
6787ac7
c3787a9
b192243
fb9eb34
b3bd113
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,13 +5,17 @@ | |
|
|
||
| from __future__ import print_function | ||
| import threading | ||
|
|
||
| try: | ||
| from urllib.parse import urlparse | ||
| except ImportError: | ||
| from urlparse import urlparse # pylint: disable=import-error | ||
| from six.moves.urllib.request import urlopen # pylint: disable=import-error, ungrouped-imports | ||
| from binascii import hexlify | ||
| from os import urandom | ||
| import json | ||
| import ssl | ||
| import sys | ||
| import OpenSSL.crypto | ||
|
|
||
| from knack.prompting import prompt_pass, NoTTYException | ||
|
|
@@ -30,9 +34,10 @@ | |
|
|
||
| from azure.cli.core.commands.client_factory import get_mgmt_service_client | ||
| from azure.cli.core.commands import LongRunningOperation | ||
| from azure.cli.core.util import in_cloud_console | ||
|
|
||
| from .vsts_cd_provider import VstsContinuousDeliveryProvider | ||
| from ._params import AUTH_TYPES | ||
| from ._params import AUTH_TYPES, MULTI_CONTAINER_TYPES | ||
| from ._client_factory import web_client_factory, ex_handler_factory | ||
| from ._appservice_utils import _generic_site_operation | ||
|
|
||
|
|
@@ -47,7 +52,7 @@ | |
|
|
||
| def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_file=None, | ||
| deployment_container_image_name=None, deployment_source_url=None, deployment_source_branch='master', | ||
| deployment_local_git=None): | ||
| deployment_local_git=None, multicontainer_config_type=None, multicontainer_config_file=None): | ||
| if deployment_source_url and deployment_local_git: | ||
| raise CLIError('usage error: --deployment-source-url <url> | --deployment-local-git') | ||
| client = web_client_factory(cmd.cli_ctx) | ||
|
|
@@ -66,8 +71,10 @@ def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_fi | |
| helper = _StackRuntimeHelper(client, linux=is_linux) | ||
|
|
||
| if is_linux: | ||
| if runtime and deployment_container_image_name: | ||
| raise CLIError('usage error: --runtime | --deployment-container-image-name') | ||
| if not validate_linux_create_options(runtime, deployment_container_image_name, | ||
| multicontainer_config_type, multicontainer_config_file): | ||
| raise CLIError("usage error: --runtime | --deployment-container-image-name |" | ||
| " --multicontainer-config-type TYPE --multicontainer-config-file FILE") | ||
| if startup_file: | ||
| site_config.app_command_line = startup_file | ||
|
|
||
|
|
@@ -77,16 +84,17 @@ def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_fi | |
| if not match: | ||
| raise CLIError("Linux Runtime '{}' is not supported." | ||
| "Please invoke 'list-runtimes' to cross check".format(runtime)) | ||
|
|
||
| elif deployment_container_image_name: | ||
| site_config.linux_fx_version = _format_linux_fx_version(deployment_container_image_name) | ||
| site_config.app_settings.append(NameValuePair("WEBSITES_ENABLE_APP_SERVICE_STORAGE", "false")) | ||
| else: # must specify runtime | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why you remove this? Now we won't report anything if none of the arguments are supplied
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. validate_linux_create_options() will catch this right?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okay |
||
| raise CLIError('usage error: must specify --runtime | --deployment-container-image-name') # pylint: disable=line-too-long | ||
| elif multicontainer_config_type and multicontainer_config_file: | ||
| encoded_config_file = _get_linux_multicontainer_encoded_config_from_file(multicontainer_config_file) | ||
| site_config.linux_fx_version = _format_linux_fx_version(encoded_config_file, multicontainer_config_type) | ||
|
|
||
| elif runtime: # windows webapp with runtime specified | ||
| if startup_file or deployment_container_image_name: | ||
| raise CLIError("usage error: --startup-file or --deployment-container-image-name is " | ||
| if any([startup_file, deployment_container_image_name, multicontainer_config_file, multicontainer_config_type]): | ||
| raise CLIError("usage error: --startup-file or --deployment-container-image-name or " | ||
| "--multicontainer-config-type and --multicontainer-config-file is " | ||
| "only appliable on linux webapp") | ||
| match = helper.resolve(runtime) | ||
| if not match: | ||
|
|
@@ -117,6 +125,14 @@ def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_fi | |
| return webapp | ||
|
|
||
|
|
||
| def validate_linux_create_options(runtime=None, deployment_container_image_name=None, | ||
| multicontainer_config_type=None, multicontainer_config_file=None): | ||
| if bool(multicontainer_config_type) != bool(multicontainer_config_file): | ||
| return False | ||
| opts = [runtime, deployment_container_image_name, multicontainer_config_type] | ||
| return len([x for x in opts if x]) == 1 # you can only specify one out the combinations | ||
|
|
||
|
|
||
| def update_app_settings(cmd, resource_group_name, name, settings=None, slot=None, slot_settings=None): | ||
| if not settings and not slot_settings: | ||
| raise CLIError('Usage Error: --settings |--slot-settings') | ||
|
|
@@ -378,12 +394,14 @@ def _fill_ftp_publishing_url(cmd, webapp, resource_group_name, name, slot=None): | |
| return webapp | ||
|
|
||
|
|
||
| def _format_linux_fx_version(custom_image_name): | ||
| def _format_linux_fx_version(custom_image_name, container_config_type=None): | ||
| fx_version = custom_image_name.strip() | ||
| fx_version_lower = fx_version.lower() | ||
| # handles case of only spaces | ||
| if fx_version: | ||
| if not fx_version_lower.startswith('docker|'): | ||
| if container_config_type: | ||
| fx_version = '{}|{}'.format(container_config_type, custom_image_name) | ||
| elif not fx_version_lower.startswith('docker|'): | ||
| fx_version = '{}|{}'.format('DOCKER', custom_image_name) | ||
| else: | ||
| fx_version = ' ' | ||
|
|
@@ -404,6 +422,36 @@ def _get_linux_fx_version(cmd, resource_group_name, name, slot=None): | |
| return site_config.linux_fx_version | ||
|
|
||
|
|
||
| def url_validator(url): | ||
| try: | ||
| result = urlparse(url) | ||
| return all([result.scheme, result.netloc, result.path]) | ||
| except ValueError: | ||
| return False | ||
|
|
||
|
|
||
| def _get_linux_multicontainer_decoded_config(cmd, resource_group_name, name, slot=None): | ||
| from base64 import b64decode | ||
| linux_fx_version = _get_linux_fx_version(cmd, resource_group_name, name, slot) | ||
| if not any([linux_fx_version.startswith(s) for s in MULTI_CONTAINER_TYPES]): | ||
| raise CLIError("Cannot decode config that is not one of the" | ||
| " following types: {}".format(','.join(MULTI_CONTAINER_TYPES))) | ||
| return b64decode(linux_fx_version.split('|')[1].encode('utf-8')) | ||
|
|
||
|
|
||
| def _get_linux_multicontainer_encoded_config_from_file(file_name): | ||
| from base64 import b64encode | ||
| config_file_bytes = None | ||
| if url_validator(file_name): | ||
| response = urlopen(file_name, context=_ssl_context()) | ||
| config_file_bytes = response.read() | ||
| else: | ||
| with open(file_name, 'rb') as f: | ||
| config_file_bytes = f.read() | ||
| # Decode base64 encoded byte array into string | ||
| return b64encode(config_file_bytes).decode('utf-8') | ||
|
|
||
|
|
||
| # for any modifications to the non-optional parameters, adjust the reflection logic accordingly | ||
| # in the method | ||
| def update_site_configs(cmd, resource_group_name, name, slot=None, | ||
|
|
@@ -459,6 +507,16 @@ def delete_app_settings(cmd, resource_group_name, name, setting_names, slot=None | |
| return _build_app_settings_output(result.properties, slot_cfg_names.app_setting_names) | ||
|
|
||
|
|
||
| def _ssl_context(): | ||
| if sys.version_info < (3, 4) or (in_cloud_console() and sys.platform.system() == 'Windows'): | ||
| try: | ||
| return ssl.SSLContext(ssl.PROTOCOL_TLS) # added in python 2.7.13 and 3.6 | ||
| except AttributeError: | ||
| return ssl.SSLContext(ssl.PROTOCOL_TLSv1) | ||
|
|
||
| return ssl.create_default_context() | ||
|
|
||
|
|
||
| def _build_app_settings_output(app_settings, slot_cfg_names): | ||
| slot_cfg_names = slot_cfg_names or [] | ||
| return [{'name': p, | ||
|
|
@@ -528,7 +586,7 @@ def delete_connection_strings(cmd, resource_group_name, name, setting_names, slo | |
| def update_container_settings(cmd, resource_group_name, name, docker_registry_server_url=None, | ||
| docker_custom_image_name=None, docker_registry_server_user=None, | ||
| websites_enable_app_service_storage=None, docker_registry_server_password=None, | ||
| slot=None): | ||
| multicontainer_config_type=None, multicontainer_config_file=None, slot=None): | ||
| settings = [] | ||
| if docker_registry_server_url is not None: | ||
| settings.append('DOCKER_REGISTRY_SERVER_URL=' + docker_registry_server_url) | ||
|
|
@@ -556,6 +614,13 @@ def update_container_settings(cmd, resource_group_name, name, docker_registry_se | |
| update_app_settings(cmd, resource_group_name, name, settings, slot) | ||
| settings = get_app_settings(cmd, resource_group_name, name, slot) | ||
|
|
||
| if multicontainer_config_file and multicontainer_config_type: | ||
| encoded_config_file = _get_linux_multicontainer_encoded_config_from_file(multicontainer_config_file) | ||
| linux_fx_version = _format_linux_fx_version(encoded_config_file, multicontainer_config_type) | ||
| update_site_configs(cmd, resource_group_name, name, linux_fx_version=linux_fx_version) | ||
| elif multicontainer_config_file or multicontainer_config_type: | ||
| logger.warning('Must change both settings --multicontainer-config-file FILE --multicontainer-config-type TYPE') | ||
|
|
||
| return _mask_creds_related_appsettings(_filter_for_container_settings(cmd, resource_group_name, name, settings)) | ||
|
|
||
|
|
||
|
|
@@ -585,19 +650,25 @@ def delete_container_settings(cmd, resource_group_name, name, slot=None): | |
| delete_app_settings(cmd, resource_group_name, name, CONTAINER_APPSETTING_NAMES, slot) | ||
|
|
||
|
|
||
| def show_container_settings(cmd, resource_group_name, name, slot=None): | ||
| def show_container_settings(cmd, resource_group_name, name, show_multicontainer_config=None, slot=None): | ||
| settings = get_app_settings(cmd, resource_group_name, name, slot) | ||
| return _mask_creds_related_appsettings(_filter_for_container_settings(cmd, resource_group_name, | ||
| name, settings, slot)) | ||
| return _mask_creds_related_appsettings(_filter_for_container_settings(cmd, resource_group_name, name, settings, | ||
| show_multicontainer_config, slot)) | ||
|
|
||
|
|
||
| def _filter_for_container_settings(cmd, resource_group_name, name, settings, slot=None): | ||
| def _filter_for_container_settings(cmd, resource_group_name, name, settings, | ||
| show_multicontainer_config=None, slot=None): | ||
| result = [x for x in settings if x['name'] in CONTAINER_APPSETTING_NAMES] | ||
| fx_version = _get_linux_fx_version(cmd, resource_group_name, name, slot).strip() | ||
| if fx_version: | ||
| added_image_name = {'name': 'DOCKER_CUSTOM_IMAGE_NAME', | ||
| 'value': fx_version} | ||
| result.append(added_image_name) | ||
| if show_multicontainer_config: | ||
| decoded_value = _get_linux_multicontainer_decoded_config(cmd, resource_group_name, name, slot) | ||
| decoded_image_name = {'name': 'DOCKER_CUSTOM_IMAGE_NAME_DECODED', | ||
| 'value': decoded_value} | ||
| result.append(decoded_image_name) | ||
| return result | ||
|
|
||
|
|
||
|
|
@@ -1116,7 +1187,6 @@ def view_in_browser(cmd, resource_group_name, name, slot=None, logs=False): | |
|
|
||
|
|
||
| def _open_page_in_browser(url): | ||
| import sys | ||
| if sys.platform.lower() == 'darwin': | ||
| # handle 2 things: | ||
| # a. On OSX sierra, 'python -m webbrowser -t <url>' emits out "execution error: <url> doesn't | ||
|
|
@@ -1289,7 +1359,6 @@ def _get_site_credential(cli_ctx, resource_group_name, name, slot=None): | |
|
|
||
|
|
||
| def _get_log(url, user_name, password, log_file=None): | ||
| import sys | ||
| import certifi | ||
| import urllib3 | ||
| try: | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this linux only as well like the one used in
webapp create? I suggested we could consolidate them to avoid dupes.