e3591223a0
* Update incidental test aliases. * Rewrite target references for renamed targets. * Add incidental tests to CI. * Update sanity tests for incidental cloud tests. * Initial copy of incidental tests. * Copy contrib files into test. * Update paths in test. * Add support plugins. * Update plugin to work around missing deps. * Update sanity ignores. * Fix matrix entries. * Remove debug echo.
1058 lines
40 KiB
Python
1058 lines
40 KiB
Python
#!/usr/bin/python
|
|
#
|
|
# Copyright (c) 2018 Yunge Zhu, <yungez@microsoft.com>
|
|
#
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
__metaclass__ = type
|
|
|
|
|
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|
'status': ['preview'],
|
|
'supported_by': 'community'}
|
|
|
|
|
|
DOCUMENTATION = '''
|
|
---
|
|
module: azure_rm_webappslot
|
|
version_added: "2.8"
|
|
short_description: Manage Azure Web App slot
|
|
description:
|
|
- Create, update and delete Azure Web App slot.
|
|
|
|
options:
|
|
resource_group:
|
|
description:
|
|
- Name of the resource group to which the resource belongs.
|
|
required: True
|
|
name:
|
|
description:
|
|
- Unique name of the deployment slot to create or update.
|
|
required: True
|
|
webapp_name:
|
|
description:
|
|
- Web app name which this deployment slot belongs to.
|
|
required: True
|
|
location:
|
|
description:
|
|
- Resource location. If not set, location from the resource group will be used as default.
|
|
configuration_source:
|
|
description:
|
|
- Source slot to clone configurations from when creating slot. Use webapp's name to refer to the production slot.
|
|
auto_swap_slot_name:
|
|
description:
|
|
- Used to configure target slot name to auto swap, or disable auto swap.
|
|
- Set it target slot name to auto swap.
|
|
- Set it to False to disable auto slot swap.
|
|
swap:
|
|
description:
|
|
- Swap deployment slots of a web app.
|
|
suboptions:
|
|
action:
|
|
description:
|
|
- Swap types.
|
|
- C(preview) is to apply target slot settings on source slot first.
|
|
- C(swap) is to complete swapping.
|
|
- C(reset) is to reset the swap.
|
|
choices:
|
|
- preview
|
|
- swap
|
|
- reset
|
|
default: preview
|
|
target_slot:
|
|
description:
|
|
- Name of target slot to swap. If set to None, then swap with production slot.
|
|
preserve_vnet:
|
|
description:
|
|
- C(True) to preserve virtual network to the slot during swap. Otherwise C(False).
|
|
type: bool
|
|
default: True
|
|
frameworks:
|
|
description:
|
|
- Set of run time framework settings. Each setting is a dictionary.
|
|
- See U(https://docs.microsoft.com/en-us/azure/app-service/app-service-web-overview) for more info.
|
|
suboptions:
|
|
name:
|
|
description:
|
|
- Name of the framework.
|
|
- Supported framework list for Windows web app and Linux web app is different.
|
|
- Windows web apps support C(java), C(net_framework), C(php), C(python), and C(node) from June 2018.
|
|
- Windows web apps support multiple framework at same time.
|
|
- Linux web apps support C(java), C(ruby), C(php), C(dotnetcore), and C(node) from June 2018.
|
|
- Linux web apps support only one framework.
|
|
- Java framework is mutually exclusive with others.
|
|
choices:
|
|
- java
|
|
- net_framework
|
|
- php
|
|
- python
|
|
- ruby
|
|
- dotnetcore
|
|
- node
|
|
version:
|
|
description:
|
|
- Version of the framework. For Linux web app supported value, see U(https://aka.ms/linux-stacks) for more info.
|
|
- C(net_framework) supported value sample, C(v4.0) for .NET 4.6 and C(v3.0) for .NET 3.5.
|
|
- C(php) supported value sample, C(5.5), C(5.6), C(7.0).
|
|
- C(python) supported value sample, C(5.5), C(5.6), C(7.0).
|
|
- C(node) supported value sample, C(6.6), C(6.9).
|
|
- C(dotnetcore) supported value sample, C(1.0), C(1.1), C(1.2).
|
|
- C(ruby) supported value sample, 2.3.
|
|
- C(java) supported value sample, C(1.9) for Windows web app. C(1.8) for Linux web app.
|
|
settings:
|
|
description:
|
|
- List of settings of the framework.
|
|
suboptions:
|
|
java_container:
|
|
description:
|
|
- Name of Java container. This is supported by specific framework C(java) onlys, for example C(Tomcat), C(Jetty).
|
|
java_container_version:
|
|
description:
|
|
- Version of Java container. This is supported by specific framework C(java) only.
|
|
- For C(Tomcat), for example C(8.0), C(8.5), C(9.0). For C(Jetty), for example C(9.1), C(9.3).
|
|
container_settings:
|
|
description:
|
|
- Web app slot container settings.
|
|
suboptions:
|
|
name:
|
|
description:
|
|
- Name of container, for example C(imagename:tag).
|
|
registry_server_url:
|
|
description:
|
|
- Container registry server URL, for example C(mydockerregistry.io).
|
|
registry_server_user:
|
|
description:
|
|
- The container registry server user name.
|
|
registry_server_password:
|
|
description:
|
|
- The container registry server password.
|
|
startup_file:
|
|
description:
|
|
- The slot startup file.
|
|
- This only applies for Linux web app slot.
|
|
app_settings:
|
|
description:
|
|
- Configure web app slot application settings. Suboptions are in key value pair format.
|
|
purge_app_settings:
|
|
description:
|
|
- Purge any existing application settings. Replace slot application settings with app_settings.
|
|
type: bool
|
|
deployment_source:
|
|
description:
|
|
- Deployment source for git.
|
|
suboptions:
|
|
url:
|
|
description:
|
|
- Repository URL of deployment source.
|
|
branch:
|
|
description:
|
|
- The branch name of the repository.
|
|
app_state:
|
|
description:
|
|
- Start/Stop/Restart the slot.
|
|
type: str
|
|
choices:
|
|
- started
|
|
- stopped
|
|
- restarted
|
|
default: started
|
|
state:
|
|
description:
|
|
- State of the Web App deployment slot.
|
|
- Use C(present) to create or update a slot and C(absent) to delete it.
|
|
default: present
|
|
choices:
|
|
- absent
|
|
- present
|
|
|
|
extends_documentation_fragment:
|
|
- azure
|
|
- azure_tags
|
|
|
|
author:
|
|
- Yunge Zhu(@yungezz)
|
|
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
- name: Create a webapp slot
|
|
azure_rm_webappslot:
|
|
resource_group: myResourceGroup
|
|
webapp_name: myJavaWebApp
|
|
name: stage
|
|
configuration_source: myJavaWebApp
|
|
app_settings:
|
|
testkey: testvalue
|
|
|
|
- name: swap the slot with production slot
|
|
azure_rm_webappslot:
|
|
resource_group: myResourceGroup
|
|
webapp_name: myJavaWebApp
|
|
name: stage
|
|
swap:
|
|
action: swap
|
|
|
|
- name: stop the slot
|
|
azure_rm_webappslot:
|
|
resource_group: myResourceGroup
|
|
webapp_name: myJavaWebApp
|
|
name: stage
|
|
app_state: stopped
|
|
|
|
- name: udpate a webapp slot app settings
|
|
azure_rm_webappslot:
|
|
resource_group: myResourceGroup
|
|
webapp_name: myJavaWebApp
|
|
name: stage
|
|
app_settings:
|
|
testkey: testvalue2
|
|
|
|
- name: udpate a webapp slot frameworks
|
|
azure_rm_webappslot:
|
|
resource_group: myResourceGroup
|
|
webapp_name: myJavaWebApp
|
|
name: stage
|
|
frameworks:
|
|
- name: "node"
|
|
version: "10.1"
|
|
'''
|
|
|
|
RETURN = '''
|
|
id:
|
|
description:
|
|
- ID of current slot.
|
|
returned: always
|
|
type: str
|
|
sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Web/sites/testapp/slots/stage1
|
|
'''
|
|
|
|
import time
|
|
from ansible.module_utils.azure_rm_common import AzureRMModuleBase
|
|
|
|
try:
|
|
from msrestazure.azure_exceptions import CloudError
|
|
from msrest.polling import LROPoller
|
|
from msrest.serialization import Model
|
|
from azure.mgmt.web.models import (
|
|
site_config, app_service_plan, Site,
|
|
AppServicePlan, SkuDescription, NameValuePair
|
|
)
|
|
except ImportError:
|
|
# This is handled in azure_rm_common
|
|
pass
|
|
|
|
swap_spec = dict(
|
|
action=dict(
|
|
type='str',
|
|
choices=[
|
|
'preview',
|
|
'swap',
|
|
'reset'
|
|
],
|
|
default='preview'
|
|
),
|
|
target_slot=dict(
|
|
type='str'
|
|
),
|
|
preserve_vnet=dict(
|
|
type='bool',
|
|
default=True
|
|
)
|
|
)
|
|
|
|
container_settings_spec = dict(
|
|
name=dict(type='str', required=True),
|
|
registry_server_url=dict(type='str'),
|
|
registry_server_user=dict(type='str'),
|
|
registry_server_password=dict(type='str', no_log=True)
|
|
)
|
|
|
|
deployment_source_spec = dict(
|
|
url=dict(type='str'),
|
|
branch=dict(type='str')
|
|
)
|
|
|
|
|
|
framework_settings_spec = dict(
|
|
java_container=dict(type='str', required=True),
|
|
java_container_version=dict(type='str', required=True)
|
|
)
|
|
|
|
|
|
framework_spec = dict(
|
|
name=dict(
|
|
type='str',
|
|
required=True,
|
|
choices=['net_framework', 'java', 'php', 'node', 'python', 'dotnetcore', 'ruby']),
|
|
version=dict(type='str', required=True),
|
|
settings=dict(type='dict', options=framework_settings_spec)
|
|
)
|
|
|
|
|
|
def webapp_to_dict(webapp):
|
|
return dict(
|
|
id=webapp.id,
|
|
name=webapp.name,
|
|
location=webapp.location,
|
|
client_cert_enabled=webapp.client_cert_enabled,
|
|
enabled=webapp.enabled,
|
|
reserved=webapp.reserved,
|
|
client_affinity_enabled=webapp.client_affinity_enabled,
|
|
server_farm_id=webapp.server_farm_id,
|
|
host_names_disabled=webapp.host_names_disabled,
|
|
https_only=webapp.https_only if hasattr(webapp, 'https_only') else None,
|
|
skip_custom_domain_verification=webapp.skip_custom_domain_verification if hasattr(webapp, 'skip_custom_domain_verification') else None,
|
|
ttl_in_seconds=webapp.ttl_in_seconds if hasattr(webapp, 'ttl_in_seconds') else None,
|
|
state=webapp.state,
|
|
tags=webapp.tags if webapp.tags else None
|
|
)
|
|
|
|
|
|
def slot_to_dict(slot):
|
|
return dict(
|
|
id=slot.id,
|
|
resource_group=slot.resource_group,
|
|
server_farm_id=slot.server_farm_id,
|
|
target_swap_slot=slot.target_swap_slot,
|
|
enabled_host_names=slot.enabled_host_names,
|
|
slot_swap_status=slot.slot_swap_status,
|
|
name=slot.name,
|
|
location=slot.location,
|
|
enabled=slot.enabled,
|
|
reserved=slot.reserved,
|
|
host_names_disabled=slot.host_names_disabled,
|
|
state=slot.state,
|
|
repository_site_name=slot.repository_site_name,
|
|
default_host_name=slot.default_host_name,
|
|
kind=slot.kind,
|
|
site_config=slot.site_config,
|
|
tags=slot.tags if slot.tags else None
|
|
)
|
|
|
|
|
|
class Actions:
|
|
NoAction, CreateOrUpdate, UpdateAppSettings, Delete = range(4)
|
|
|
|
|
|
class AzureRMWebAppSlots(AzureRMModuleBase):
|
|
"""Configuration class for an Azure RM Web App slot resource"""
|
|
|
|
def __init__(self):
|
|
self.module_arg_spec = dict(
|
|
resource_group=dict(
|
|
type='str',
|
|
required=True
|
|
),
|
|
name=dict(
|
|
type='str',
|
|
required=True
|
|
),
|
|
webapp_name=dict(
|
|
type='str',
|
|
required=True
|
|
),
|
|
location=dict(
|
|
type='str'
|
|
),
|
|
configuration_source=dict(
|
|
type='str'
|
|
),
|
|
auto_swap_slot_name=dict(
|
|
type='raw'
|
|
),
|
|
swap=dict(
|
|
type='dict',
|
|
options=swap_spec
|
|
),
|
|
frameworks=dict(
|
|
type='list',
|
|
elements='dict',
|
|
options=framework_spec
|
|
),
|
|
container_settings=dict(
|
|
type='dict',
|
|
options=container_settings_spec
|
|
),
|
|
deployment_source=dict(
|
|
type='dict',
|
|
options=deployment_source_spec
|
|
),
|
|
startup_file=dict(
|
|
type='str'
|
|
),
|
|
app_settings=dict(
|
|
type='dict'
|
|
),
|
|
purge_app_settings=dict(
|
|
type='bool',
|
|
default=False
|
|
),
|
|
app_state=dict(
|
|
type='str',
|
|
choices=['started', 'stopped', 'restarted'],
|
|
default='started'
|
|
),
|
|
state=dict(
|
|
type='str',
|
|
default='present',
|
|
choices=['present', 'absent']
|
|
)
|
|
)
|
|
|
|
mutually_exclusive = [['container_settings', 'frameworks']]
|
|
|
|
self.resource_group = None
|
|
self.name = None
|
|
self.webapp_name = None
|
|
self.location = None
|
|
|
|
self.auto_swap_slot_name = None
|
|
self.swap = None
|
|
self.tags = None
|
|
self.startup_file = None
|
|
self.configuration_source = None
|
|
self.clone = False
|
|
|
|
# site config, e.g app settings, ssl
|
|
self.site_config = dict()
|
|
self.app_settings = dict()
|
|
self.app_settings_strDic = None
|
|
|
|
# siteSourceControl
|
|
self.deployment_source = dict()
|
|
|
|
# site, used at level creation, or update.
|
|
self.site = None
|
|
|
|
# property for internal usage, not used for sdk
|
|
self.container_settings = None
|
|
|
|
self.purge_app_settings = False
|
|
self.app_state = 'started'
|
|
|
|
self.results = dict(
|
|
changed=False,
|
|
id=None,
|
|
)
|
|
self.state = None
|
|
self.to_do = Actions.NoAction
|
|
|
|
self.frameworks = None
|
|
|
|
# set site_config value from kwargs
|
|
self.site_config_updatable_frameworks = ["net_framework_version",
|
|
"java_version",
|
|
"php_version",
|
|
"python_version",
|
|
"linux_fx_version"]
|
|
|
|
self.supported_linux_frameworks = ['ruby', 'php', 'dotnetcore', 'node', 'java']
|
|
self.supported_windows_frameworks = ['net_framework', 'php', 'python', 'node', 'java']
|
|
|
|
super(AzureRMWebAppSlots, self).__init__(derived_arg_spec=self.module_arg_spec,
|
|
mutually_exclusive=mutually_exclusive,
|
|
supports_check_mode=True,
|
|
supports_tags=True)
|
|
|
|
def exec_module(self, **kwargs):
|
|
"""Main module execution method"""
|
|
|
|
for key in list(self.module_arg_spec.keys()) + ['tags']:
|
|
if hasattr(self, key):
|
|
setattr(self, key, kwargs[key])
|
|
elif kwargs[key] is not None:
|
|
if key == "scm_type":
|
|
self.site_config[key] = kwargs[key]
|
|
|
|
old_response = None
|
|
response = None
|
|
to_be_updated = False
|
|
|
|
# set location
|
|
resource_group = self.get_resource_group(self.resource_group)
|
|
if not self.location:
|
|
self.location = resource_group.location
|
|
|
|
# get web app
|
|
webapp_response = self.get_webapp()
|
|
|
|
if not webapp_response:
|
|
self.fail("Web app {0} does not exist in resource group {1}.".format(self.webapp_name, self.resource_group))
|
|
|
|
# get slot
|
|
old_response = self.get_slot()
|
|
|
|
# set is_linux
|
|
is_linux = True if webapp_response['reserved'] else False
|
|
|
|
if self.state == 'present':
|
|
if self.frameworks:
|
|
# java is mutually exclusive with other frameworks
|
|
if len(self.frameworks) > 1 and any(f['name'] == 'java' for f in self.frameworks):
|
|
self.fail('Java is mutually exclusive with other frameworks.')
|
|
|
|
if is_linux:
|
|
if len(self.frameworks) != 1:
|
|
self.fail('Can specify one framework only for Linux web app.')
|
|
|
|
if self.frameworks[0]['name'] not in self.supported_linux_frameworks:
|
|
self.fail('Unsupported framework {0} for Linux web app.'.format(self.frameworks[0]['name']))
|
|
|
|
self.site_config['linux_fx_version'] = (self.frameworks[0]['name'] + '|' + self.frameworks[0]['version']).upper()
|
|
|
|
if self.frameworks[0]['name'] == 'java':
|
|
if self.frameworks[0]['version'] != '8':
|
|
self.fail("Linux web app only supports java 8.")
|
|
|
|
if self.frameworks[0].get('settings', {}) and self.frameworks[0]['settings'].get('java_container', None) and \
|
|
self.frameworks[0]['settings']['java_container'].lower() != 'tomcat':
|
|
self.fail("Linux web app only supports tomcat container.")
|
|
|
|
if self.frameworks[0].get('settings', {}) and self.frameworks[0]['settings'].get('java_container', None) and \
|
|
self.frameworks[0]['settings']['java_container'].lower() == 'tomcat':
|
|
self.site_config['linux_fx_version'] = 'TOMCAT|' + self.frameworks[0]['settings']['java_container_version'] + '-jre8'
|
|
else:
|
|
self.site_config['linux_fx_version'] = 'JAVA|8-jre8'
|
|
else:
|
|
for fx in self.frameworks:
|
|
if fx.get('name') not in self.supported_windows_frameworks:
|
|
self.fail('Unsupported framework {0} for Windows web app.'.format(fx.get('name')))
|
|
else:
|
|
self.site_config[fx.get('name') + '_version'] = fx.get('version')
|
|
|
|
if 'settings' in fx and fx['settings'] is not None:
|
|
for key, value in fx['settings'].items():
|
|
self.site_config[key] = value
|
|
|
|
if not self.app_settings:
|
|
self.app_settings = dict()
|
|
|
|
if self.container_settings:
|
|
linux_fx_version = 'DOCKER|'
|
|
|
|
if self.container_settings.get('registry_server_url'):
|
|
self.app_settings['DOCKER_REGISTRY_SERVER_URL'] = 'https://' + self.container_settings['registry_server_url']
|
|
|
|
linux_fx_version += self.container_settings['registry_server_url'] + '/'
|
|
|
|
linux_fx_version += self.container_settings['name']
|
|
|
|
self.site_config['linux_fx_version'] = linux_fx_version
|
|
|
|
if self.container_settings.get('registry_server_user'):
|
|
self.app_settings['DOCKER_REGISTRY_SERVER_USERNAME'] = self.container_settings['registry_server_user']
|
|
|
|
if self.container_settings.get('registry_server_password'):
|
|
self.app_settings['DOCKER_REGISTRY_SERVER_PASSWORD'] = self.container_settings['registry_server_password']
|
|
|
|
# set auto_swap_slot_name
|
|
if self.auto_swap_slot_name and isinstance(self.auto_swap_slot_name, str):
|
|
self.site_config['auto_swap_slot_name'] = self.auto_swap_slot_name
|
|
if self.auto_swap_slot_name is False:
|
|
self.site_config['auto_swap_slot_name'] = None
|
|
|
|
# init site
|
|
self.site = Site(location=self.location, site_config=self.site_config)
|
|
|
|
# check if the slot already present in the webapp
|
|
if not old_response:
|
|
self.log("Web App slot doesn't exist")
|
|
|
|
to_be_updated = True
|
|
self.to_do = Actions.CreateOrUpdate
|
|
self.site.tags = self.tags
|
|
|
|
# if linux, setup startup_file
|
|
if self.startup_file:
|
|
self.site_config['app_command_line'] = self.startup_file
|
|
|
|
# set app setting
|
|
if self.app_settings:
|
|
app_settings = []
|
|
for key in self.app_settings.keys():
|
|
app_settings.append(NameValuePair(name=key, value=self.app_settings[key]))
|
|
|
|
self.site_config['app_settings'] = app_settings
|
|
|
|
# clone slot
|
|
if self.configuration_source:
|
|
self.clone = True
|
|
|
|
else:
|
|
# existing slot, do update
|
|
self.log("Web App slot already exists")
|
|
|
|
self.log('Result: {0}'.format(old_response))
|
|
|
|
update_tags, self.site.tags = self.update_tags(old_response.get('tags', None))
|
|
|
|
if update_tags:
|
|
to_be_updated = True
|
|
|
|
# check if site_config changed
|
|
old_config = self.get_configuration_slot(self.name)
|
|
|
|
if self.is_site_config_changed(old_config):
|
|
to_be_updated = True
|
|
self.to_do = Actions.CreateOrUpdate
|
|
|
|
self.app_settings_strDic = self.list_app_settings_slot(self.name)
|
|
|
|
# purge existing app_settings:
|
|
if self.purge_app_settings:
|
|
to_be_updated = True
|
|
self.to_do = Actions.UpdateAppSettings
|
|
self.app_settings_strDic = dict()
|
|
|
|
# check if app settings changed
|
|
if self.purge_app_settings or self.is_app_settings_changed():
|
|
to_be_updated = True
|
|
self.to_do = Actions.UpdateAppSettings
|
|
|
|
if self.app_settings:
|
|
for key in self.app_settings.keys():
|
|
self.app_settings_strDic[key] = self.app_settings[key]
|
|
|
|
elif self.state == 'absent':
|
|
if old_response:
|
|
self.log("Delete Web App slot")
|
|
self.results['changed'] = True
|
|
|
|
if self.check_mode:
|
|
return self.results
|
|
|
|
self.delete_slot()
|
|
|
|
self.log('Web App slot deleted')
|
|
|
|
else:
|
|
self.log("Web app slot {0} not exists.".format(self.name))
|
|
|
|
if to_be_updated:
|
|
self.log('Need to Create/Update web app')
|
|
self.results['changed'] = True
|
|
|
|
if self.check_mode:
|
|
return self.results
|
|
|
|
if self.to_do == Actions.CreateOrUpdate:
|
|
response = self.create_update_slot()
|
|
|
|
self.results['id'] = response['id']
|
|
|
|
if self.clone:
|
|
self.clone_slot()
|
|
|
|
if self.to_do == Actions.UpdateAppSettings:
|
|
self.update_app_settings_slot()
|
|
|
|
slot = None
|
|
if response:
|
|
slot = response
|
|
if old_response:
|
|
slot = old_response
|
|
|
|
if slot:
|
|
if (slot['state'] != 'Stopped' and self.app_state == 'stopped') or \
|
|
(slot['state'] != 'Running' and self.app_state == 'started') or \
|
|
self.app_state == 'restarted':
|
|
|
|
self.results['changed'] = True
|
|
if self.check_mode:
|
|
return self.results
|
|
|
|
self.set_state_slot(self.app_state)
|
|
|
|
if self.swap:
|
|
self.results['changed'] = True
|
|
if self.check_mode:
|
|
return self.results
|
|
|
|
self.swap_slot()
|
|
|
|
return self.results
|
|
|
|
# compare site config
|
|
def is_site_config_changed(self, existing_config):
|
|
for fx_version in self.site_config_updatable_frameworks:
|
|
if self.site_config.get(fx_version):
|
|
if not getattr(existing_config, fx_version) or \
|
|
getattr(existing_config, fx_version).upper() != self.site_config.get(fx_version).upper():
|
|
return True
|
|
|
|
if self.auto_swap_slot_name is False and existing_config.auto_swap_slot_name is not None:
|
|
return True
|
|
elif self.auto_swap_slot_name and self.auto_swap_slot_name != getattr(existing_config, 'auto_swap_slot_name', None):
|
|
return True
|
|
return False
|
|
|
|
# comparing existing app setting with input, determine whether it's changed
|
|
def is_app_settings_changed(self):
|
|
if self.app_settings:
|
|
if len(self.app_settings_strDic) != len(self.app_settings):
|
|
return True
|
|
|
|
if self.app_settings_strDic != self.app_settings:
|
|
return True
|
|
return False
|
|
|
|
# comparing deployment source with input, determine whether it's changed
|
|
def is_deployment_source_changed(self, existing_webapp):
|
|
if self.deployment_source:
|
|
if self.deployment_source.get('url') \
|
|
and self.deployment_source['url'] != existing_webapp.get('site_source_control')['url']:
|
|
return True
|
|
|
|
if self.deployment_source.get('branch') \
|
|
and self.deployment_source['branch'] != existing_webapp.get('site_source_control')['branch']:
|
|
return True
|
|
|
|
return False
|
|
|
|
def create_update_slot(self):
|
|
'''
|
|
Creates or updates Web App slot with the specified configuration.
|
|
|
|
:return: deserialized Web App instance state dictionary
|
|
'''
|
|
self.log(
|
|
"Creating / Updating the Web App slot {0}".format(self.name))
|
|
|
|
try:
|
|
response = self.web_client.web_apps.create_or_update_slot(resource_group_name=self.resource_group,
|
|
slot=self.name,
|
|
name=self.webapp_name,
|
|
site_envelope=self.site)
|
|
if isinstance(response, LROPoller):
|
|
response = self.get_poller_result(response)
|
|
|
|
except CloudError as exc:
|
|
self.log('Error attempting to create the Web App slot instance.')
|
|
self.fail("Error creating the Web App slot: {0}".format(str(exc)))
|
|
return slot_to_dict(response)
|
|
|
|
def delete_slot(self):
|
|
'''
|
|
Deletes specified Web App slot in the specified subscription and resource group.
|
|
|
|
:return: True
|
|
'''
|
|
self.log("Deleting the Web App slot {0}".format(self.name))
|
|
try:
|
|
response = self.web_client.web_apps.delete_slot(resource_group_name=self.resource_group,
|
|
name=self.webapp_name,
|
|
slot=self.name)
|
|
except CloudError as e:
|
|
self.log('Error attempting to delete the Web App slot.')
|
|
self.fail(
|
|
"Error deleting the Web App slots: {0}".format(str(e)))
|
|
|
|
return True
|
|
|
|
def get_webapp(self):
|
|
'''
|
|
Gets the properties of the specified Web App.
|
|
|
|
:return: deserialized Web App instance state dictionary
|
|
'''
|
|
self.log(
|
|
"Checking if the Web App instance {0} is present".format(self.webapp_name))
|
|
|
|
response = None
|
|
|
|
try:
|
|
response = self.web_client.web_apps.get(resource_group_name=self.resource_group,
|
|
name=self.webapp_name)
|
|
|
|
# Newer SDK versions (0.40.0+) seem to return None if it doesn't exist instead of raising CloudError
|
|
if response is not None:
|
|
self.log("Response : {0}".format(response))
|
|
self.log("Web App instance : {0} found".format(response.name))
|
|
return webapp_to_dict(response)
|
|
|
|
except CloudError as ex:
|
|
pass
|
|
|
|
self.log("Didn't find web app {0} in resource group {1}".format(
|
|
self.webapp_name, self.resource_group))
|
|
|
|
return False
|
|
|
|
def get_slot(self):
|
|
'''
|
|
Gets the properties of the specified Web App slot.
|
|
|
|
:return: deserialized Web App slot state dictionary
|
|
'''
|
|
self.log(
|
|
"Checking if the Web App slot {0} is present".format(self.name))
|
|
|
|
response = None
|
|
|
|
try:
|
|
response = self.web_client.web_apps.get_slot(resource_group_name=self.resource_group,
|
|
name=self.webapp_name,
|
|
slot=self.name)
|
|
|
|
# Newer SDK versions (0.40.0+) seem to return None if it doesn't exist instead of raising CloudError
|
|
if response is not None:
|
|
self.log("Response : {0}".format(response))
|
|
self.log("Web App slot: {0} found".format(response.name))
|
|
return slot_to_dict(response)
|
|
|
|
except CloudError as ex:
|
|
pass
|
|
|
|
self.log("Does not find web app slot {0} in resource group {1}".format(self.name, self.resource_group))
|
|
|
|
return False
|
|
|
|
def list_app_settings(self):
|
|
'''
|
|
List webapp application settings
|
|
:return: deserialized list response
|
|
'''
|
|
self.log("List webapp application setting")
|
|
|
|
try:
|
|
|
|
response = self.web_client.web_apps.list_application_settings(
|
|
resource_group_name=self.resource_group, name=self.webapp_name)
|
|
self.log("Response : {0}".format(response))
|
|
|
|
return response.properties
|
|
except CloudError as ex:
|
|
self.fail("Failed to list application settings for web app {0} in resource group {1}: {2}".format(
|
|
self.name, self.resource_group, str(ex)))
|
|
|
|
def list_app_settings_slot(self, slot_name):
|
|
'''
|
|
List application settings
|
|
:return: deserialized list response
|
|
'''
|
|
self.log("List application setting")
|
|
|
|
try:
|
|
|
|
response = self.web_client.web_apps.list_application_settings_slot(
|
|
resource_group_name=self.resource_group, name=self.webapp_name, slot=slot_name)
|
|
self.log("Response : {0}".format(response))
|
|
|
|
return response.properties
|
|
except CloudError as ex:
|
|
self.fail("Failed to list application settings for web app slot {0} in resource group {1}: {2}".format(
|
|
self.name, self.resource_group, str(ex)))
|
|
|
|
def update_app_settings_slot(self, slot_name=None, app_settings=None):
|
|
'''
|
|
Update application settings
|
|
:return: deserialized updating response
|
|
'''
|
|
self.log("Update application setting")
|
|
|
|
if slot_name is None:
|
|
slot_name = self.name
|
|
if app_settings is None:
|
|
app_settings = self.app_settings_strDic
|
|
try:
|
|
response = self.web_client.web_apps.update_application_settings_slot(resource_group_name=self.resource_group,
|
|
name=self.webapp_name,
|
|
slot=slot_name,
|
|
kind=None,
|
|
properties=app_settings)
|
|
self.log("Response : {0}".format(response))
|
|
|
|
return response.as_dict()
|
|
except CloudError as ex:
|
|
self.fail("Failed to update application settings for web app slot {0} in resource group {1}: {2}".format(
|
|
self.name, self.resource_group, str(ex)))
|
|
|
|
return response
|
|
|
|
def create_or_update_source_control_slot(self):
|
|
'''
|
|
Update site source control
|
|
:return: deserialized updating response
|
|
'''
|
|
self.log("Update site source control")
|
|
|
|
if self.deployment_source is None:
|
|
return False
|
|
|
|
self.deployment_source['is_manual_integration'] = False
|
|
self.deployment_source['is_mercurial'] = False
|
|
|
|
try:
|
|
response = self.web_client.web_client.create_or_update_source_control_slot(
|
|
resource_group_name=self.resource_group,
|
|
name=self.webapp_name,
|
|
site_source_control=self.deployment_source,
|
|
slot=self.name)
|
|
self.log("Response : {0}".format(response))
|
|
|
|
return response.as_dict()
|
|
except CloudError as ex:
|
|
self.fail("Failed to update site source control for web app slot {0} in resource group {1}: {2}".format(
|
|
self.name, self.resource_group, str(ex)))
|
|
|
|
def get_configuration(self):
|
|
'''
|
|
Get web app configuration
|
|
:return: deserialized web app configuration response
|
|
'''
|
|
self.log("Get web app configuration")
|
|
|
|
try:
|
|
|
|
response = self.web_client.web_apps.get_configuration(
|
|
resource_group_name=self.resource_group, name=self.webapp_name)
|
|
self.log("Response : {0}".format(response))
|
|
|
|
return response
|
|
except CloudError as ex:
|
|
self.fail("Failed to get configuration for web app {0} in resource group {1}: {2}".format(
|
|
self.webapp_name, self.resource_group, str(ex)))
|
|
|
|
def get_configuration_slot(self, slot_name):
|
|
'''
|
|
Get slot configuration
|
|
:return: deserialized slot configuration response
|
|
'''
|
|
self.log("Get web app slot configuration")
|
|
|
|
try:
|
|
|
|
response = self.web_client.web_apps.get_configuration_slot(
|
|
resource_group_name=self.resource_group, name=self.webapp_name, slot=slot_name)
|
|
self.log("Response : {0}".format(response))
|
|
|
|
return response
|
|
except CloudError as ex:
|
|
self.fail("Failed to get configuration for web app slot {0} in resource group {1}: {2}".format(
|
|
slot_name, self.resource_group, str(ex)))
|
|
|
|
def update_configuration_slot(self, slot_name=None, site_config=None):
|
|
'''
|
|
Update slot configuration
|
|
:return: deserialized slot configuration response
|
|
'''
|
|
self.log("Update web app slot configuration")
|
|
|
|
if slot_name is None:
|
|
slot_name = self.name
|
|
if site_config is None:
|
|
site_config = self.site_config
|
|
try:
|
|
|
|
response = self.web_client.web_apps.update_configuration_slot(
|
|
resource_group_name=self.resource_group, name=self.webapp_name, slot=slot_name, site_config=site_config)
|
|
self.log("Response : {0}".format(response))
|
|
|
|
return response
|
|
except CloudError as ex:
|
|
self.fail("Failed to update configuration for web app slot {0} in resource group {1}: {2}".format(
|
|
slot_name, self.resource_group, str(ex)))
|
|
|
|
def set_state_slot(self, appstate):
|
|
'''
|
|
Start/stop/restart web app slot
|
|
:return: deserialized updating response
|
|
'''
|
|
try:
|
|
if appstate == 'started':
|
|
response = self.web_client.web_apps.start_slot(resource_group_name=self.resource_group, name=self.webapp_name, slot=self.name)
|
|
elif appstate == 'stopped':
|
|
response = self.web_client.web_apps.stop_slot(resource_group_name=self.resource_group, name=self.webapp_name, slot=self.name)
|
|
elif appstate == 'restarted':
|
|
response = self.web_client.web_apps.restart_slot(resource_group_name=self.resource_group, name=self.webapp_name, slot=self.name)
|
|
else:
|
|
self.fail("Invalid web app slot state {0}".format(appstate))
|
|
|
|
self.log("Response : {0}".format(response))
|
|
|
|
return response
|
|
except CloudError as ex:
|
|
request_id = ex.request_id if ex.request_id else ''
|
|
self.fail("Failed to {0} web app slot {1} in resource group {2}, request_id {3} - {4}".format(
|
|
appstate, self.name, self.resource_group, request_id, str(ex)))
|
|
|
|
def swap_slot(self):
|
|
'''
|
|
Swap slot
|
|
:return: deserialized response
|
|
'''
|
|
self.log("Swap slot")
|
|
|
|
try:
|
|
if self.swap['action'] == 'swap':
|
|
if self.swap['target_slot'] is None:
|
|
response = self.web_client.web_apps.swap_slot_with_production(resource_group_name=self.resource_group,
|
|
name=self.webapp_name,
|
|
target_slot=self.name,
|
|
preserve_vnet=self.swap['preserve_vnet'])
|
|
else:
|
|
response = self.web_client.web_apps.swap_slot_slot(resource_group_name=self.resource_group,
|
|
name=self.webapp_name,
|
|
slot=self.name,
|
|
target_slot=self.swap['target_slot'],
|
|
preserve_vnet=self.swap['preserve_vnet'])
|
|
elif self.swap['action'] == 'preview':
|
|
if self.swap['target_slot'] is None:
|
|
response = self.web_client.web_apps.apply_slot_config_to_production(resource_group_name=self.resource_group,
|
|
name=self.webapp_name,
|
|
target_slot=self.name,
|
|
preserve_vnet=self.swap['preserve_vnet'])
|
|
else:
|
|
response = self.web_client.web_apps.apply_slot_configuration_slot(resource_group_name=self.resource_group,
|
|
name=self.webapp_name,
|
|
slot=self.name,
|
|
target_slot=self.swap['target_slot'],
|
|
preserve_vnet=self.swap['preserve_vnet'])
|
|
elif self.swap['action'] == 'reset':
|
|
if self.swap['target_slot'] is None:
|
|
response = self.web_client.web_apps.reset_production_slot_config(resource_group_name=self.resource_group,
|
|
name=self.webapp_name)
|
|
else:
|
|
response = self.web_client.web_apps.reset_slot_configuration_slot(resource_group_name=self.resource_group,
|
|
name=self.webapp_name,
|
|
slot=self.swap['target_slot'])
|
|
response = self.web_client.web_apps.reset_slot_configuration_slot(resource_group_name=self.resource_group,
|
|
name=self.webapp_name,
|
|
slot=self.name)
|
|
|
|
self.log("Response : {0}".format(response))
|
|
|
|
return response
|
|
except CloudError as ex:
|
|
self.fail("Failed to swap web app slot {0} in resource group {1}: {2}".format(self.name, self.resource_group, str(ex)))
|
|
|
|
def clone_slot(self):
|
|
if self.configuration_source:
|
|
src_slot = None if self.configuration_source.lower() == self.webapp_name.lower() else self.configuration_source
|
|
|
|
if src_slot is None:
|
|
site_config_clone_from = self.get_configuration()
|
|
else:
|
|
site_config_clone_from = self.get_configuration_slot(slot_name=src_slot)
|
|
|
|
self.update_configuration_slot(site_config=site_config_clone_from)
|
|
|
|
if src_slot is None:
|
|
app_setting_clone_from = self.list_app_settings()
|
|
else:
|
|
app_setting_clone_from = self.list_app_settings_slot(src_slot)
|
|
|
|
if self.app_settings:
|
|
app_setting_clone_from.update(self.app_settings)
|
|
|
|
self.update_app_settings_slot(app_settings=app_setting_clone_from)
|
|
|
|
|
|
def main():
|
|
"""Main execution"""
|
|
AzureRMWebAppSlots()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|