55
66from __future__ import print_function
77import threading
8+
89try :
910 from urllib .parse import urlparse
1011except ImportError :
1112 from urlparse import urlparse # pylint: disable=import-error
13+ from six .moves .urllib .request import urlopen # pylint: disable=import-error, ungrouped-imports
1214from binascii import hexlify
1315from os import urandom
1416import json
17+ import ssl
18+ import sys
1519import OpenSSL .crypto
1620
1721from knack .prompting import prompt_pass , NoTTYException
3034
3135from azure .cli .core .commands .client_factory import get_mgmt_service_client
3236from azure .cli .core .commands import LongRunningOperation
37+ from azure .cli .core .util import in_cloud_console
3338
3439from .vsts_cd_provider import VstsContinuousDeliveryProvider
35- from ._params import AUTH_TYPES
40+ from ._params import AUTH_TYPES , MULTI_CONTAINER_TYPES
3641from ._client_factory import web_client_factory , ex_handler_factory
3742from ._appservice_utils import _generic_site_operation
3843
4752
4853def create_webapp (cmd , resource_group_name , name , plan , runtime = None , startup_file = None ,
4954 deployment_container_image_name = None , deployment_source_url = None , deployment_source_branch = 'master' ,
50- deployment_local_git = None ):
55+ deployment_local_git = None , multicontainer_config_type = None , multicontainer_config_file = None ):
5156 if deployment_source_url and deployment_local_git :
5257 raise CLIError ('usage error: --deployment-source-url <url> | --deployment-local-git' )
5358 client = web_client_factory (cmd .cli_ctx )
@@ -66,8 +71,10 @@ def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_fi
6671 helper = _StackRuntimeHelper (client , linux = is_linux )
6772
6873 if is_linux :
69- if runtime and deployment_container_image_name :
70- raise CLIError ('usage error: --runtime | --deployment-container-image-name' )
74+ if not validate_linux_create_options (runtime , deployment_container_image_name ,
75+ multicontainer_config_type , multicontainer_config_file ):
76+ raise CLIError ("usage error: --runtime | --deployment-container-image-name |"
77+ " --multicontainer-config-type TYPE --multicontainer-config-file FILE" )
7178 if startup_file :
7279 site_config .app_command_line = startup_file
7380
@@ -77,16 +84,17 @@ def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_fi
7784 if not match :
7885 raise CLIError ("Linux Runtime '{}' is not supported."
7986 "Please invoke 'list-runtimes' to cross check" .format (runtime ))
80-
8187 elif deployment_container_image_name :
8288 site_config .linux_fx_version = _format_linux_fx_version (deployment_container_image_name )
8389 site_config .app_settings .append (NameValuePair ("WEBSITES_ENABLE_APP_SERVICE_STORAGE" , "false" ))
84- else : # must specify runtime
85- raise CLIError ('usage error: must specify --runtime | --deployment-container-image-name' ) # pylint: disable=line-too-long
90+ elif multicontainer_config_type and multicontainer_config_file :
91+ encoded_config_file = _get_linux_multicontainer_encoded_config_from_file (multicontainer_config_file )
92+ site_config .linux_fx_version = _format_linux_fx_version (encoded_config_file , multicontainer_config_type )
8693
8794 elif runtime : # windows webapp with runtime specified
88- if startup_file or deployment_container_image_name :
89- raise CLIError ("usage error: --startup-file or --deployment-container-image-name is "
95+ if any ([startup_file , deployment_container_image_name , multicontainer_config_file , multicontainer_config_type ]):
96+ raise CLIError ("usage error: --startup-file or --deployment-container-image-name or "
97+ "--multicontainer-config-type and --multicontainer-config-file is "
9098 "only appliable on linux webapp" )
9199 match = helper .resolve (runtime )
92100 if not match :
@@ -117,6 +125,14 @@ def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_fi
117125 return webapp
118126
119127
128+ def validate_linux_create_options (runtime = None , deployment_container_image_name = None ,
129+ multicontainer_config_type = None , multicontainer_config_file = None ):
130+ if bool (multicontainer_config_type ) != bool (multicontainer_config_file ):
131+ return False
132+ opts = [runtime , deployment_container_image_name , multicontainer_config_type ]
133+ return len ([x for x in opts if x ]) == 1 # you can only specify one out the combinations
134+
135+
120136def update_app_settings (cmd , resource_group_name , name , settings = None , slot = None , slot_settings = None ):
121137 if not settings and not slot_settings :
122138 raise CLIError ('Usage Error: --settings |--slot-settings' )
@@ -378,12 +394,14 @@ def _fill_ftp_publishing_url(cmd, webapp, resource_group_name, name, slot=None):
378394 return webapp
379395
380396
381- def _format_linux_fx_version (custom_image_name ):
397+ def _format_linux_fx_version (custom_image_name , container_config_type = None ):
382398 fx_version = custom_image_name .strip ()
383399 fx_version_lower = fx_version .lower ()
384400 # handles case of only spaces
385401 if fx_version :
386- if not fx_version_lower .startswith ('docker|' ):
402+ if container_config_type :
403+ fx_version = '{}|{}' .format (container_config_type , custom_image_name )
404+ elif not fx_version_lower .startswith ('docker|' ):
387405 fx_version = '{}|{}' .format ('DOCKER' , custom_image_name )
388406 else :
389407 fx_version = ' '
@@ -404,6 +422,36 @@ def _get_linux_fx_version(cmd, resource_group_name, name, slot=None):
404422 return site_config .linux_fx_version
405423
406424
425+ def url_validator (url ):
426+ try :
427+ result = urlparse (url )
428+ return all ([result .scheme , result .netloc , result .path ])
429+ except ValueError :
430+ return False
431+
432+
433+ def _get_linux_multicontainer_decoded_config (cmd , resource_group_name , name , slot = None ):
434+ from base64 import b64decode
435+ linux_fx_version = _get_linux_fx_version (cmd , resource_group_name , name , slot )
436+ if not any ([linux_fx_version .startswith (s ) for s in MULTI_CONTAINER_TYPES ]):
437+ raise CLIError ("Cannot decode config that is not one of the"
438+ " following types: {}" .format (',' .join (MULTI_CONTAINER_TYPES )))
439+ return b64decode (linux_fx_version .split ('|' )[1 ].encode ('utf-8' ))
440+
441+
442+ def _get_linux_multicontainer_encoded_config_from_file (file_name ):
443+ from base64 import b64encode
444+ config_file_bytes = None
445+ if url_validator (file_name ):
446+ response = urlopen (file_name , context = _ssl_context ())
447+ config_file_bytes = response .read ()
448+ else :
449+ with open (file_name , 'rb' ) as f :
450+ config_file_bytes = f .read ()
451+ # Decode base64 encoded byte array into string
452+ return b64encode (config_file_bytes ).decode ('utf-8' )
453+
454+
407455# for any modifications to the non-optional parameters, adjust the reflection logic accordingly
408456# in the method
409457def 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
459507 return _build_app_settings_output (result .properties , slot_cfg_names .app_setting_names )
460508
461509
510+ def _ssl_context ():
511+ if sys .version_info < (3 , 4 ) or (in_cloud_console () and sys .platform .system () == 'Windows' ):
512+ try :
513+ return ssl .SSLContext (ssl .PROTOCOL_TLS ) # added in python 2.7.13 and 3.6
514+ except AttributeError :
515+ return ssl .SSLContext (ssl .PROTOCOL_TLSv1 )
516+
517+ return ssl .create_default_context ()
518+
519+
462520def _build_app_settings_output (app_settings , slot_cfg_names ):
463521 slot_cfg_names = slot_cfg_names or []
464522 return [{'name' : p ,
@@ -528,7 +586,7 @@ def delete_connection_strings(cmd, resource_group_name, name, setting_names, slo
528586def update_container_settings (cmd , resource_group_name , name , docker_registry_server_url = None ,
529587 docker_custom_image_name = None , docker_registry_server_user = None ,
530588 websites_enable_app_service_storage = None , docker_registry_server_password = None ,
531- slot = None ):
589+ multicontainer_config_type = None , multicontainer_config_file = None , slot = None ):
532590 settings = []
533591 if docker_registry_server_url is not None :
534592 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
556614 update_app_settings (cmd , resource_group_name , name , settings , slot )
557615 settings = get_app_settings (cmd , resource_group_name , name , slot )
558616
617+ if multicontainer_config_file and multicontainer_config_type :
618+ encoded_config_file = _get_linux_multicontainer_encoded_config_from_file (multicontainer_config_file )
619+ linux_fx_version = _format_linux_fx_version (encoded_config_file , multicontainer_config_type )
620+ update_site_configs (cmd , resource_group_name , name , linux_fx_version = linux_fx_version )
621+ elif multicontainer_config_file or multicontainer_config_type :
622+ logger .warning ('Must change both settings --multicontainer-config-file FILE --multicontainer-config-type TYPE' )
623+
559624 return _mask_creds_related_appsettings (_filter_for_container_settings (cmd , resource_group_name , name , settings ))
560625
561626
@@ -585,19 +650,25 @@ def delete_container_settings(cmd, resource_group_name, name, slot=None):
585650 delete_app_settings (cmd , resource_group_name , name , CONTAINER_APPSETTING_NAMES , slot )
586651
587652
588- def show_container_settings (cmd , resource_group_name , name , slot = None ):
653+ def show_container_settings (cmd , resource_group_name , name , show_multicontainer_config = None , slot = None ):
589654 settings = get_app_settings (cmd , resource_group_name , name , slot )
590- return _mask_creds_related_appsettings (_filter_for_container_settings (cmd , resource_group_name ,
591- name , settings , slot ))
655+ return _mask_creds_related_appsettings (_filter_for_container_settings (cmd , resource_group_name , name , settings ,
656+ show_multicontainer_config , slot ))
592657
593658
594- def _filter_for_container_settings (cmd , resource_group_name , name , settings , slot = None ):
659+ def _filter_for_container_settings (cmd , resource_group_name , name , settings ,
660+ show_multicontainer_config = None , slot = None ):
595661 result = [x for x in settings if x ['name' ] in CONTAINER_APPSETTING_NAMES ]
596662 fx_version = _get_linux_fx_version (cmd , resource_group_name , name , slot ).strip ()
597663 if fx_version :
598664 added_image_name = {'name' : 'DOCKER_CUSTOM_IMAGE_NAME' ,
599665 'value' : fx_version }
600666 result .append (added_image_name )
667+ if show_multicontainer_config :
668+ decoded_value = _get_linux_multicontainer_decoded_config (cmd , resource_group_name , name , slot )
669+ decoded_image_name = {'name' : 'DOCKER_CUSTOM_IMAGE_NAME_DECODED' ,
670+ 'value' : decoded_value }
671+ result .append (decoded_image_name )
601672 return result
602673
603674
@@ -1116,7 +1187,6 @@ def view_in_browser(cmd, resource_group_name, name, slot=None, logs=False):
11161187
11171188
11181189def _open_page_in_browser (url ):
1119- import sys
11201190 if sys .platform .lower () == 'darwin' :
11211191 # handle 2 things:
11221192 # 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):
12891359
12901360
12911361def _get_log (url , user_name , password , log_file = None ):
1292- import sys
12931362 import certifi
12941363 import urllib3
12951364 try :
0 commit comments