This commit is contained in:
Atul Bagga 2020-01-14 17:17:53 +05:30
Родитель b9dba091fb
Коммит 8de472e5f5
18 изменённых файлов: 93 добавлений и 83 удалений

Просмотреть файл

@ -5,6 +5,7 @@
from azure.cli.core import AzCommandsLoader
class DevCommandsLoader(AzCommandsLoader):
def __init__(self, cli_ctx=None):
@ -26,4 +27,5 @@ class DevCommandsLoader(AzCommandsLoader):
load_aks_arguments(self, command)
load_functionapp_arguments(self, command)
COMMAND_LOADER_CLS = DevCommandsLoader

Просмотреть файл

@ -3,6 +3,7 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
def load_aks_arguments(self, _):
with self.argument_context('aks up') as context:
context.argument('pat', options_list='--pat')

Просмотреть файл

@ -9,6 +9,7 @@ aksops = CliCommandType(
operations_tmpl='azext_aks_deploy.dev.aks.up#{}'
)
def load_aks_commands(self, _):
with self.command_group('aks', command_type=aksops) as g:
g.command('up', 'aks_deploy')

Просмотреть файл

@ -12,7 +12,7 @@ from azext_aks_deploy.dev.common.const import (APP_NAME_DEFAULT, APP_NAME_PLACEH
from azext_aks_deploy.dev.common.github_api_helper import Files\
logger = get_logger(__name__)
PACKS_ROOT_STRING = os.path.sep+'resources'+os.path.sep+'packs'+os.path.sep
PACKS_ROOT_STRING = os.path.sep + 'resources' + os.path.sep + 'packs' + os.path.sep
FILE_ABSOLUTE_PATH = abspath(dirname(dirname(abspath(__file__))))
@ -44,7 +44,7 @@ def get_helm_charts(language, acr_details, port):
files = []
if language_packs_path:
try:
abs_pack_path = FILE_ABSOLUTE_PATH+language_packs_path
abs_pack_path = FILE_ABSOLUTE_PATH + language_packs_path
# r=root, d=directories, f = files
logger.debug("Checking in helm charts")
for r, _d, f in os.walk(abs_pack_path):
@ -77,13 +77,16 @@ def get_file_content(path):
except Exception as ex:
raise CLIError(ex)
def replace_values(file_content, acr_details):
content = file_content.replace(APP_NAME_PLACEHOLDER, APP_NAME_DEFAULT).replace(ACR_PLACEHOLDER, acr_details['name'])
return content
def replace_port(file_content, port):
content = file_content.replace(PORT_NUMBER_PLACEHOLDER, port)
return content
def get_supported_language_packs_path(language):
return PACKS_ROOT_STRING + language.lower() + os.path.sep

Просмотреть файл

@ -7,7 +7,7 @@ from knack.prompting import prompt
from knack.log import get_logger
from knack.util import CLIError
from azext_aks_deploy.dev.common.git import get_repository_url_from_local_repo, uri_parse
from azext_aks_deploy.dev.common.git import get_repository_url_from_local_repo
from azext_aks_deploy.dev.common.github_api_helper import (Files, get_work_flow_check_runID,
get_check_run_status_and_conclusion,
get_github_pat_token,
@ -45,7 +45,7 @@ def aks_deploy(aks_cluster=None, acr=None, repository=None, port=None, branch_na
"""
if repository is None:
repository = get_repository_url_from_local_repo()
logger.debug('Github Remote url detected local repo is {}'.format(repository))
logger.debug('Github Remote url detected local repo is %s', repository)
if not repository:
repository = prompt('GitHub Repository url (e.g. https://github.com/atbagga/aks-deploy):')
if not repository:
@ -64,11 +64,10 @@ def aks_deploy(aks_cluster=None, acr=None, repository=None, port=None, branch_na
if language:
logger.warning('%s repository detected.', language)
else:
logger.debug('Languages detected : {} '.format(languages))
logger.debug('Languages detected : %s', languages)
raise CLIError('The languages in this repository are not yet supported from up command.')
from azext_aks_deploy.dev.common.azure_cli_resources import (get_default_subscription_info,
get_aks_details,
from azext_aks_deploy.dev.common.azure_cli_resources import (get_aks_details,
get_acr_details,
configure_aks_credentials)
cluster_details = get_aks_details(aks_cluster)
@ -105,8 +104,8 @@ def aks_deploy(aks_cluster=None, acr=None, repository=None, port=None, branch_na
# File checkin
for file_name in files:
logger.debug("Checkin file path: {}".format(file_name.path))
logger.debug("Checkin file content: {}".format(file_name.content))
logger.debug("Checkin file path: %s", file_name.path)
logger.debug("Checkin file content: %s", file_name.content)
default_branch = get_default_branch(repo_name)
workflow_commit_sha = push_files_to_repository(repo_name, default_branch, files, branch_name)
@ -125,7 +124,7 @@ def aks_deploy(aks_cluster=None, acr=None, repository=None, port=None, branch_na
return
def get_yaml_template_for_repo(language, cluster_details, acr_details, repo_name):
def get_yaml_template_for_repo(cluster_details, acr_details):
files_to_return = []
github_workflow_path = '.github/workflows/'
# Read template file
@ -165,15 +164,13 @@ def get_new_workflow_yaml_name():
def choose_supported_language(languages):
# check if top three languages are supported or not
# check if one of top three languages are supported or not
list_languages = list(languages.keys())
first_language = list_languages[0]
if 'JavaScript' == first_language or 'Java' == first_language or 'Python' == first_language:
if first_language in ('JavaScript', 'Java', 'Python'):
return first_language
elif len(list_languages) >= 1 and ('JavaScript' == list_languages[1]
or 'Java' == list_languages[1] or 'Python' == list_languages[1]):
elif len(list_languages) >= 1 and list_languages[1] in ('JavaScript', 'Java', 'Python'):
return list_languages[1]
elif len(list_languages) >= 2 and ('JavaScript' == list_languages[2]
or 'Java' == list_languages[2] or 'Python' == list_languages[2]):
elif len(list_languages) >= 2 and list_languages[2] in ('JavaScript', 'Java', 'Python'):
return list_languages[2]
return None

Просмотреть файл

@ -20,7 +20,7 @@ def get_default_subscription_info():
"""
from azure.cli.core._profile import Profile
profile = Profile()
dummy_user = profile.get_current_account_user()
_ = profile.get_current_account_user()
subscriptions = profile.load_cached_subscriptions(False)
for subscription in subscriptions:
if subscription['isDefault']:
@ -137,12 +137,15 @@ def get_sku():
sku_choice = prompt_user_friendly_choice_list(
"Select the SKU of the container registry?", sku_list)
return sku_list[sku_choice]
def configure_aks_credentials(cluster_name,resource_group):
def configure_aks_credentials(cluster_name, resource_group):
try:
subscription_id, subscription_name, tenant_id, environment_name = get_default_subscription_info()
logger.warning("Using your default Azure subscription %s for getting AKS cluster credentials.", subscription_name)
aks_creds = subprocess.check_output('az aks get-credentials -n {cluster_name} -g {group_name} -o json'.format(cluster_name=cluster_name,group_name=resource_group), shell=True)
logger.warning("Using your default Azure subscription %s for getting AKS cluster credentials.",
subscription_name)
aks_creds = subprocess.check_output(
'az aks get-credentials -n {cluster_name} -g {group_name} -o json'.format(
cluster_name=cluster_name, group_name=resource_group), shell=True)
except Exception as ex:
raise CLIError(ex)
raise CLIError(ex)

Просмотреть файл

@ -15,4 +15,3 @@ APP_NAME_DEFAULT = 'k8sdemo'
RELEASE_NAME = 'aksupdemo'
RELEASE_PLACEHOLDER = 'release_name_place_holder'

Просмотреть файл

@ -13,6 +13,7 @@ from knack.log import get_logger
logger = get_logger(__name__)
def get_repository_url_from_local_repo():
return get_remote_url(is_github_url_candidate)
@ -42,6 +43,7 @@ def get_remote_url(validation_function=None):
return value
return None
def get_git_remotes():
import subprocess
import sys
@ -96,4 +98,4 @@ _git_remotes = {}
_GIT_EXE = 'git'
_ORIGIN_PUSH_KEY = 'origin(push)'
REF_HEADS_PREFIX = 'refs/heads/'
REF_PULL_PREFIX = 'refs/pull/'
REF_PULL_PREFIX = 'refs/pull/'

Просмотреть файл

@ -22,10 +22,10 @@ class Files: # pylint: disable=too-few-public-methods
self.content = content
def get_github_pat_token(token_prefix,display_warning=False):
def get_github_pat_token(token_prefix, display_warning=False):
from azext_aks_deploy.dev.common.github_credential_manager import GithubCredentialManager
github_manager = GithubCredentialManager()
return github_manager.get_token(token_prefix=token_prefix,display_warning=display_warning)
return github_manager.get_token(token_prefix=token_prefix, display_warning=display_warning)
def get_github_repos_api_url(repo_id):
@ -221,14 +221,16 @@ def get_languages_for_repo(repo_name):
import json
return json.loads(get_response.text)
def get_check_runs_for_commit(repo_name,commmit_sha):
def get_check_runs_for_commit(repo_name, commmit_sha):
"""
API Documentation - https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-specific-ref
"""
token = get_github_pat_token(repo_name)
headers = get_application_json_header_for_preview()
time.sleep(1)
get_check_runs_url = 'https://api.github.com/repos/{repo_id}/commits/{ref}/check-runs'.format(repo_id=repo_name,ref=commmit_sha)
get_check_runs_url = 'https://api.github.com/repos/{repo_id}/commits/{ref}/check-runs'.format(
repo_id=repo_name, ref=commmit_sha)
get_response = requests.get(url=get_check_runs_url, auth=('', token), headers=headers)
if not get_response.status_code == _HTTP_SUCCESS_STATUS:
raise CLIError('Get Check Runs failed. Error: ({err})'.format(err=get_response.reason))
@ -236,11 +238,11 @@ def get_check_runs_for_commit(repo_name,commmit_sha):
return json.loads(get_response.text)
def get_work_flow_check_runID(repo_name,commmit_sha):
check_run_found= False
def get_work_flow_check_runID(repo_name, commmit_sha):
check_run_found = False
count = 0
while(not check_run_found or count>3):
check_runs_list_response = get_check_runs_for_commit(repo_name,commmit_sha)
while(not check_run_found or count > 3):
check_runs_list_response = get_check_runs_for_commit(repo_name, commmit_sha)
if check_runs_list_response and check_runs_list_response['total_count'] > 0:
# fetch the Github actions check run and its check run ID
check_runs_list = check_runs_list_response['check_runs']
@ -250,9 +252,9 @@ def get_work_flow_check_runID(repo_name,commmit_sha):
check_run_found = True
return check_run_id
time.sleep(5)
count = count+1
count = count + 1
raise CLIError("Couldn't find Github Actions check run. Please check 'Actions' tab in your Github repo.")
def get_check_run_status_and_conclusion(repo_name, check_run_id):
"""
@ -260,10 +262,10 @@ def get_check_run_status_and_conclusion(repo_name, check_run_id):
"""
token = get_github_pat_token(repo_name)
headers = get_application_json_header_for_preview()
get_check_run_url = 'https://api.github.com/repos/{repo_id}/check-runs/{checkID}'.format(repo_id=repo_name,checkID=check_run_id)
get_check_run_url = 'https://api.github.com/repos/{repo_id}/check-runs/{checkID}'.format(
repo_id=repo_name, checkID=check_run_id)
get_response = requests.get(url=get_check_run_url, auth=('', token), headers=headers)
if not get_response.status_code == _HTTP_SUCCESS_STATUS:
raise CLIError('Get Check Run failed. Error: ({err})'.format(err=get_response.reason))
import json
return json.loads(get_response.text)['status'], json.loads(get_response.text)['conclusion']

Просмотреть файл

@ -18,7 +18,8 @@ def get_azure_credentials():
azure_creds_user_choice = 1
print('')
print('You need to include the following key value pairs as part of your Secrets in the GitHub Repo Setting.')
print('Please go to your GitHub Repo page -> Settings -> Secrets -> Add a new secret and include the following Name and value pairs.')
print('Please go to your GitHub Repo page -> Settings -> Secrets -> '
'Add a new secret and include the following Name and value pairs.')
while azure_creds_user_choice == 1:
print('Creating AZURE_CREDENTIALS secret...')
auth_details = subprocess.check_output('az ad sp create-for-rbac --sdk-auth -o json', shell=True)
@ -29,18 +30,20 @@ def get_azure_credentials():
print(json.dumps(auth_details_json, indent=2))
print('')
print('Creating REGISTRY_USERNAME and REGISTRY_PASSWORD...')
sp_details = subprocess.check_output('az ad sp create-for-rbac -o json' , shell=True)
sp_details = subprocess.check_output('az ad sp create-for-rbac -o json', shell=True)
sp_details_json = json.loads(sp_details)
print('')
print('Name: REGISTRY_USERNAME')
print('Value: ',sp_details_json['appId'])
print('Value: ', sp_details_json['appId'])
print('')
print('Name: REGISTRY_PASSWORD')
print('Value: ',sp_details_json['password'])
print('Value: ', sp_details_json['password'])
print('')
user_choice_list = []
user_choice_list.append('Yes. Continue')
user_choice_list.append('No. Re-generate the values for AZURE_CREDENTIALS, REGISTRY_USERNAME and REGISTRY_PASSWORD')
user_choice_list.append('No. Re-generate the values for AZURE_CREDENTIALS, '
'REGISTRY_USERNAME and REGISTRY_PASSWORD')
azure_creds_user_choice = prompt_user_friendly_choice_list(
'Have you copied the name and value for AZURE_CREDENTIALS, REGISTRY_USERNAME and REGISTRY_PASSWORD:', user_choice_list)
'Have you copied the name and value for AZURE_CREDENTIALS, REGISTRY_USERNAME and REGISTRY_PASSWORD:',
user_choice_list)
return

Просмотреть файл

@ -15,7 +15,6 @@ AKS_UP_GITHUB_PAT_ENVKEY = "GITHUB_PAT"
logger = get_logger(__name__)
@singleton
class GithubCredentialManager():
""" GithubCredentialManager
@ -90,5 +89,5 @@ class GithubCredentialManager():
print('')
return github_pat
if not self.token:
self._create_token(token_prefix=token_prefix,note=note)
self._create_token(token_prefix=token_prefix, note=note)
return self.token

Просмотреть файл

@ -8,6 +8,7 @@ from knack.util import CLIError
from azext_aks_deploy.dev.common.github_api_helper import get_check_run_status_and_conclusion
def poll_workflow_status(repo_name, check_run_id):
import colorama
import humanfriendly

Просмотреть файл

@ -8,17 +8,18 @@ from azext_aks_deploy.dev.common.utils import which
logger = get_logger(__name__)
def get_deployment_IP_port(release_name, language):
if not which('kubectl'):
raise CLIError('Can not find kubectl executable in PATH')
try:
import subprocess
import json
service_name = release_name + '-' +language.lower()
service_name = release_name + '-' + language.lower()
service_details = subprocess.check_output('kubectl get service {} -o json'.format(service_name), shell=True)
service_obj = json.loads(service_details)
deployment_ip = service_obj['status']['loadBalancer']['ingress'][0]['ip']
port = service_obj['spec']['ports'][0]['port']
return deployment_ip,port
return deployment_ip, port
except subprocess.CalledProcessError as err:
raise CLIError('Could not find app/service: {}'.format(err))

Просмотреть файл

@ -36,8 +36,6 @@ def open_file(filepath):
Opens a file in the default editor for the file type and exits.
"""
import subprocess
import platform
import os
if platform.system() == 'Darwin': # macOS
subprocess.call(('open', filepath))
elif platform.system() == 'Windows': # Windows
@ -63,6 +61,7 @@ def open_url(url):
from webbrowser import open_new
open_new(url=url)
# inspired from aks_preview
def which(binary):
path_var = os.getenv('PATH')
@ -108,7 +107,7 @@ def get_repo_name_from_repo_url(repository_url):
Should be called with a valid github url
returns owner/reponame for github repos, repo_name for azure repo type
"""
from .git import uri_parse
from .git import uri_parse
parsed_url = uri_parse(repository_url)
logger.debug('Parsing GitHub url: %s', parsed_url)
if parsed_url.scheme == 'https' and parsed_url.netloc == 'github.com':
@ -117,4 +116,4 @@ def get_repo_name_from_repo_url(repository_url):
if stripped_path.endswith('.git'):
stripped_path = stripped_path[:-4]
return stripped_path
raise CLIError('Could not parse repository url.')
raise CLIError('Could not parse repository url.')

Просмотреть файл

@ -3,6 +3,7 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
def load_functionapp_arguments(self, _):
with self.argument_context('functionapp up') as context:
context.argument('pat', options_list='--pat')
context.argument('pat', options_list='--pat')

Просмотреть файл

@ -9,6 +9,7 @@ functionappops = CliCommandType(
operations_tmpl='azext_aks_deploy.dev.functionapp.up#{}'
)
def load_functionapp_commands(self, _):
with self.command_group('functionapp', command_type=functionappops) as g:
g.command('up', 'functionapp_deploy')

Просмотреть файл

@ -7,18 +7,18 @@ from knack.prompting import prompt
from knack.log import get_logger
from knack.util import CLIError
from azext_aks_deploy.dev.common.git import get_repository_url_from_local_repo, uri_parse
from azext_aks_deploy.dev.common.github_api_helper import (Files, get_work_flow_check_runID, get_languages_for_repo,
push_files_github, get_check_run_status_and_conclusion,
from azext_aks_deploy.dev.common.git import get_repository_url_from_local_repo
from azext_aks_deploy.dev.common.github_api_helper import (get_work_flow_check_runID,
push_files_github,
get_github_pat_token)
from azext_aks_deploy.dev.common.github_workflow_helper import poll_workflow_status
from azext_aks_deploy.dev.common.github_azure_secrets import get_azure_credentials
from azext_aks_deploy.dev.common.utils import get_repo_name_from_repo_url
from azext_aks_deploy.dev.common.const import ( APP_NAME_DEFAULT, APP_NAME_PLACEHOLDER)
logger = get_logger(__name__)
def functionapp_deploy(repository=None, skip_secrets_generation=False, no_wait=False):
def functionapp_deploy(repository=None, skip_secrets_generation=False, do_not_wait=False):
"""Build and Deploy to Azure FunctionApp via GitHub actions
:param repository: GitHub repository URL e.g. https://github.com/azure/azure-cli.
:type repository: str
@ -29,33 +29,30 @@ def functionapp_deploy(repository=None, skip_secrets_generation=False, no_wait=F
"""
if repository is None:
repository = get_repository_url_from_local_repo()
logger.debug('Github Remote url detected local repo is {}'.format(repository))
logger.debug('Github Remote url detected local repo is %s', repository)
if not repository:
repository = prompt('GitHub Repository url (e.g. https://github.com/atbagga/aks-deploy):')
if not repository:
raise CLIError('The following arguments are required: --repository.')
repo_name = get_repo_name_from_repo_url(repository)
get_github_pat_token(repo_name,display_warning=True)
logger.warning('Setting up your workflow.')
from azext_aks_deploy.dev.common.azure_cli_resources import (get_default_subscription_info,
get_functionapp_details,
configure_aks_credentials)
get_github_pat_token(token_prefix=repo_name, display_warning=True)
logger.warning('Setting up your workflow.')
# create azure service principal and display json on the screen for user to configure it as Github secrets
if not skip_secrets_generation:
get_azure_credentials()
print('')
files = get_yaml_template_for_repo()
files = get_yaml_template_for_repo("appname", repo_name)
# File checkin
for file_name in files:
logger.debug("Checkin file path: {}".format(file_name.path))
logger.debug("Checkin file content: {}".format(file_name.content))
logger.debug("Checkin file path: %s", file_name.path)
logger.debug("Checkin file content: %s", file_name.content)
workflow_commit_sha = push_files_github(files, repo_name, 'master', True, message="Setting up K8s deployment workflow.")
workflow_commit_sha = push_files_github(files, repo_name, 'master', True,
message="Setting up K8s deployment workflow.")
print('Creating workflow...')
check_run_id = get_work_flow_check_runID(repo_name, workflow_commit_sha)
workflow_url = 'https://github.com/{repo_id}/runs/{checkID}'.format(repo_id=repo_name, checkID=check_run_id)
@ -66,21 +63,20 @@ def functionapp_deploy(repository=None, skip_secrets_generation=False, no_wait=F
return
def get_yaml_template_for_repo(language, functionapp_name, repo_name):
files_to_return = []
def get_yaml_template_for_repo(_functionapp_name, _repo_name):
files = []
# Read template file
return None
return files
def choose_supported_language(languages):
# check if top three languages are supported or not
# check if one of top three languages are supported or not
list_languages = list(languages.keys())
first_language = list_languages[0]
if 'JavaScript' == first_language or 'Java' == first_language or 'Python' == first_language:
if first_language in ('JavaScript', 'Java', 'Python'):
return first_language
elif len(list_languages) >= 1 and ( 'JavaScript' == list_languages[1] or 'Java' == list_languages[1] or 'Python' == list_languages[1]):
elif len(list_languages) >= 1 and list_languages[1] in ('JavaScript', 'Java', 'Python'):
return list_languages[1]
elif len(list_languages) >= 2 and ( 'JavaScript' == list_languages[2] or 'Java' == list_languages[2] or 'Python' == list_languages[2]):
elif len(list_languages) >= 2 and list_languages[2] in ('JavaScript', 'Java', 'Python'):
return list_languages[2]
return None

Просмотреть файл

@ -6,24 +6,24 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: Azure/docker-login@v1
with:
login-server: container_registry_name_place_holder.azurecr.io
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- run: |
docker build . -t container_registry_name_place_holder.azurecr.io/app_name_place_holder:${{ github.sha }}
docker push container_registry_name_place_holder.azurecr.io/app_name_place_holder:${{ github.sha }}
- name: Set Context for Azure Kubernetes Cluster using azure/aks-set-context@v1 action
uses: azure/aks-set-context@v1
with:
creds: '${{ secrets.AZURE_CREDENTIALS }}'
cluster-name: cluster_name_place_holder
resource-group: resource_name_place_holder
- uses: Azure/k8s-create-secret@v1
with:
container-registry-url: container_registry_name_place_holder.azurecr.io
@ -45,4 +45,3 @@ jobs:
container_registry_name_place_holder.azurecr.io/app_name_place_holder:${{ github.sha }}
imagepullsecrets: |
demo-k8s-secret"""