-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding webapp extension for quickstart command (#42)
* Adding webapp extension plus the whl file Updating resource client factory by adding def Fixing build adding blank line More build fixes fixing pylint errors Build errors PYlint fix More PYlint fixes FW: * Updating code owners for webapps * addressing PR feedback * adding return None * PYlint fixes * diabling too-long pylint * FW: * FW: Pylint * FW: fixing an issue where passing location value was not working correctly * fet default was failing when no default was set
- Loading branch information
1 parent
6baf006
commit dbf5f18
Showing
8 changed files
with
331 additions
and
0 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,5 @@ | |
/src/servicebus/ @v-ajnava | ||
|
||
/src/eventhubs/ @v-ajnava | ||
|
||
/src/webapps/ @panchagnula |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# -------------------------------------------------------------------------------------------- | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||
# -------------------------------------------------------------------------------------------- | ||
|
||
from azure.cli.core import AzCommandsLoader | ||
|
||
# pylint: disable=unused-import | ||
|
||
import azext_webapps._help | ||
|
||
|
||
class WebappsExtCommandLoader(AzCommandsLoader): | ||
|
||
def __init__(self, cli_ctx=None): | ||
from azure.cli.core.commands import CliCommandType | ||
webapps_custom = CliCommandType( | ||
operations_tmpl='azext_webapps.custom#{}') | ||
super(WebappsExtCommandLoader, self).__init__(cli_ctx=cli_ctx, | ||
custom_command_type=webapps_custom, | ||
min_profile="2017-03-10-profile") | ||
|
||
def load_command_table(self, _): | ||
with self.command_group('webapp') as g: | ||
g.custom_command('quickstart', 'create_deploy_webapp') | ||
return self.command_table | ||
|
||
def load_arguments(self, _): | ||
with self.argument_context('webapp quickstart') as c: | ||
c.argument('name', options_list=['--name', '-n'], help='name of the new webapp') | ||
c.argument('dryrun', | ||
help="shows summary of the create and deploy operation instead of executing it", | ||
default=False, action='store_true') | ||
|
||
|
||
COMMAND_LOADER_CLS = WebappsExtCommandLoader |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# -------------------------------------------------------------------------------------------- | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||
# -------------------------------------------------------------------------------------------- | ||
|
||
from knack.help_files import helps | ||
|
||
|
||
helps['webapp quickstart'] = """ | ||
type: command | ||
short-summary: Create and deploy a node web app | ||
examples: | ||
- name: Create a web app with the default configuration. | ||
text: > | ||
az webapp quickstart -n MyUniqueAppName | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"azext.minCliCoreVersion": "2.0.24" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# -------------------------------------------------------------------------------------------- | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||
# -------------------------------------------------------------------------------------------- | ||
|
||
import os | ||
import zipfile | ||
from azure.cli.core.commands.client_factory import get_mgmt_service_client | ||
from azure.mgmt.resource.resources.models import ResourceGroup | ||
|
||
|
||
def _resource_client_factory(cli_ctx, **_): | ||
from azure.cli.core.profiles import ResourceType | ||
return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) | ||
|
||
|
||
def web_client_factory(cli_ctx, **_): | ||
from azure.mgmt.web import WebSiteManagementClient | ||
return get_mgmt_service_client(cli_ctx, WebSiteManagementClient) | ||
|
||
|
||
def zip_contents_from_dir(dirPath): | ||
relroot = os.path.abspath(os.path.join(dirPath, os.pardir)) | ||
path_and_file = os.path.splitdrive(dirPath)[1] | ||
file_val = os.path.split(path_and_file)[1] | ||
zip_file_path = relroot + "\\" + file_val + ".zip" | ||
abs_src = os.path.abspath(dirPath) | ||
with zipfile.ZipFile("{}".format(zip_file_path), "w", zipfile.ZIP_DEFLATED) as zf: | ||
for dirname, subdirs, files in os.walk(dirPath): | ||
# skip node_modules folder for Node apps, | ||
# since zip_deployment will perfom the build operation | ||
if 'node_modules' in subdirs: | ||
subdirs.remove('node_modules') | ||
for filename in files: | ||
absname = os.path.abspath(os.path.join(dirname, filename)) | ||
arcname = absname[len(abs_src) + 1:] | ||
zf.write(absname, arcname) | ||
return zip_file_path | ||
|
||
|
||
def is_node_application(path): | ||
# for node application, package.json should exisit in the application root dir | ||
# if this exists we pass the path of the file to read it contents & get version | ||
package_json_file = os.path.join(path, 'package.json') | ||
if os.path.isfile(package_json_file): | ||
return package_json_file | ||
return '' | ||
|
||
|
||
def get_node_runtime_version_toSet(): | ||
version_val = "8.0" | ||
# trunc_version = float(node_version[:3]) | ||
# TODO: call the list_runtimes once there is an API that returns the supported versions | ||
return version_val | ||
|
||
|
||
def create_resource_group(cmd, rg_name, location): | ||
rcf = _resource_client_factory(cmd.cli_ctx) | ||
rg_params = ResourceGroup(location=location) | ||
return rcf.resource_groups.create_or_update(rg_name, rg_params) | ||
|
||
|
||
def check_resource_group_exists(cmd, rg_name): | ||
rcf = _resource_client_factory(cmd.cli_ctx) | ||
return rcf.resource_groups.check_existence(rg_name) | ||
|
||
|
||
def check_resource_group_supports_linux(cmd, rg_name, location): | ||
# get all appservice plans from RG | ||
client = web_client_factory(cmd.cli_ctx) | ||
plans = list(client.app_service_plans.list_by_resource_group(rg_name)) | ||
# filter by location & reserverd=false | ||
for item in plans: | ||
if item.location == location and not item.reserved: | ||
return False | ||
return True | ||
|
||
|
||
def check_if_asp_exists(cmd, rg_name, asp_name): | ||
# get all appservice plans from RG | ||
client = web_client_factory(cmd.cli_ctx) | ||
for item in list(client.app_service_plans.list_by_resource_group(rg_name)): | ||
if item.name == asp_name: | ||
return True | ||
return False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
# -------------------------------------------------------------------------------------------- | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||
# -------------------------------------------------------------------------------------------- | ||
|
||
from __future__ import print_function | ||
from knack.log import get_logger | ||
|
||
from azure.mgmt.web.models import (AppServicePlan, SkuDescription) | ||
|
||
from azure.cli.command_modules.appservice.custom import ( | ||
enable_zip_deploy, | ||
create_webapp, | ||
update_app_settings, | ||
_get_sku_name) | ||
|
||
from .create_util import ( | ||
zip_contents_from_dir, | ||
is_node_application, | ||
get_node_runtime_version_toSet, | ||
create_resource_group, | ||
check_resource_group_exists, | ||
check_resource_group_supports_linux, | ||
check_if_asp_exists, | ||
web_client_factory | ||
) | ||
|
||
logger = get_logger(__name__) | ||
|
||
# pylint:disable=no-member,too-many-lines,too-many-locals,too-many-statements | ||
|
||
|
||
def create_deploy_webapp(cmd, name, location=None, dryrun=False): | ||
import os | ||
import json | ||
|
||
client = web_client_factory(cmd.cli_ctx) | ||
sku = "S1" | ||
os_val = "Linux" | ||
language = "node" | ||
full_sku = _get_sku_name(sku) | ||
|
||
if location is None: | ||
locs = client.list_geo_regions(sku, True) | ||
available_locs = [] | ||
for loc in locs: | ||
available_locs.append(loc.geo_region_name) | ||
location = available_locs[0] | ||
|
||
# Remove spaces from the location string, incase the GeoRegion string is used | ||
loc_name = location.replace(" ", "") | ||
|
||
asp = "appsvc_asp_{}_{}".format(os_val, loc_name) | ||
rg = "appsvc_rg_{}_{}".format(os_val, loc_name) | ||
|
||
# the code to deploy is expected to be the current directory the command is running from | ||
src_dir = os.getcwd() | ||
|
||
# if dir is empty, show a message in dry run | ||
do_deployment = False if os.listdir(src_dir) == [] else True | ||
package_json_path = is_node_application(src_dir) | ||
|
||
str_no_contents_warn = "" | ||
if not do_deployment: | ||
str_no_contents_warn = "[Empty directory, no deployment will be triggered]" | ||
|
||
if package_json_path == '': | ||
node_version = "[No package.json file found in root directory, not a Node app?]" | ||
version_used_create = "8.0" | ||
else: | ||
with open(package_json_path) as data_file: | ||
data = json.load(data_file) | ||
node_version = data['version'] | ||
version_used_create = get_node_runtime_version_toSet() | ||
|
||
# Resource group: check if default RG is set | ||
default_rg = cmd.cli_ctx.config.get('defaults', 'group', fallback=None) | ||
if (default_rg and check_resource_group_supports_linux(cmd, default_rg, location)): | ||
rg = default_rg | ||
rg_mssg = "[Using default Resource group]" | ||
else: | ||
rg_mssg = "" | ||
|
||
runtime_version = "{}|{}".format(language, version_used_create) | ||
src_path = "{} {}".format(src_dir.replace("\\", "\\\\"), str_no_contents_warn) | ||
rg_str = "{} {}".format(rg, rg_mssg) | ||
|
||
dry_run_str = r""" { | ||
"name" : "%s", | ||
"serverfarm" : "%s", | ||
"resourcegroup" : "%s", | ||
"sku": "%s", | ||
"os": "%s", | ||
"location" : "%s", | ||
"src_path" : "%s", | ||
"version_detected": "%s", | ||
"version_to_create": "%s" | ||
} | ||
""" % (name, asp, rg_str, full_sku, os_val, location, src_path, | ||
node_version, runtime_version) | ||
|
||
create_json = json.dumps(json.loads(dry_run_str), indent=4, sort_keys=True) | ||
if dryrun: | ||
logger.warning(""" | ||
Web app will be created with the below configuration, | ||
re-run command without the --dryrun flag to create & deploy a new app | ||
""") | ||
logger.warning(create_json) | ||
return None | ||
|
||
# create RG if the RG doesn't already exist | ||
if not check_resource_group_exists(cmd, rg): | ||
logger.warning("Creating Resource group '%s' ...", rg) | ||
create_resource_group(cmd, rg, location) | ||
logger.warning("Resource group creation complete") | ||
else: | ||
logger.warning("Resource group '%s' already exists.", rg) | ||
|
||
# create asp | ||
if not check_if_asp_exists(cmd, rg, asp): | ||
logger.warning("Creating App service plan '%s' ...", asp) | ||
sku_def = SkuDescription(tier=full_sku, name=sku, capacity=1) | ||
plan_def = AppServicePlan(loc_name, app_service_plan_name=asp, | ||
sku=sku_def, reserved=True) | ||
client.app_service_plans.create_or_update(rg, asp, plan_def) | ||
logger.warning("App service plan creation complete") | ||
else: | ||
logger.warning("App service plan '%s' already exists.", asp) | ||
|
||
# create the Linux app | ||
logger.warning("Creating app '%s' ....", name) | ||
create_webapp(cmd, rg, name, asp, runtime_version) | ||
logger.warning("Webapp creation complete") | ||
|
||
# setting to build after deployment | ||
logger.warning("Updating app settings to enable build after deployment") | ||
update_app_settings(cmd, rg, name, ["SCM_DO_BUILD_DURING_DEPLOYMENT=true"]) | ||
|
||
# zip contents & deploy | ||
logger.warning("Creating zip with contents of dir %s ...", src_dir) | ||
zip_file_path = zip_contents_from_dir(src_dir) | ||
|
||
logger.warning("Deploying and building contents to app. This operation can take some time to finish...") | ||
enable_zip_deploy(cmd, rg, name, zip_file_path) | ||
logger.warning("All done. %s", create_json) | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[bdist_wheel] | ||
universal=1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#!/usr/bin/env python | ||
|
||
# -------------------------------------------------------------------------------------------- | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. See License.txt in the project root for license information. | ||
# -------------------------------------------------------------------------------------------- | ||
|
||
from codecs import open | ||
from setuptools import setup, find_packages | ||
|
||
VERSION = "0.0.4" | ||
|
||
CLASSIFIERS = [ | ||
'Development Status :: 4 - Beta', | ||
'Intended Audience :: Developers', | ||
'Intended Audience :: System Administrators', | ||
'Programming Language :: Python', | ||
'Programming Language :: Python :: 2', | ||
'Programming Language :: Python :: 2.7', | ||
'Programming Language :: Python :: 3', | ||
'Programming Language :: Python :: 3.4', | ||
'Programming Language :: Python :: 3.5', | ||
'Programming Language :: Python :: 3.6', | ||
'License :: OSI Approved :: MIT License', | ||
] | ||
|
||
DEPENDENCIES = [] | ||
|
||
setup( | ||
name='webapps', | ||
version=VERSION, | ||
description='An Azure CLI Extension to manage appservice resources', | ||
long_description='An Azure CLI Extension to manage appservice resources', | ||
license='MIT', | ||
author='Sisira Panchagnula', | ||
author_email='sisirap@microsoft.com', | ||
url='https://github.com/Azure/azure-cli-extensions', | ||
classifiers=CLASSIFIERS, | ||
packages=find_packages(exclude=["tests"]), | ||
install_requires=DEPENDENCIES | ||
) |