Skip to content

Commit

Permalink
Merge pull request Azure#97 from StrawnSC/cache-gh-creds-2
Browse files Browse the repository at this point in the history
Cache GitHub credentials for 'az containerapp up' with --repo
  • Loading branch information
StrawnSC committed May 10, 2022
2 parents f92ce6c + 083ed7b commit 302bbb5
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 4 deletions.
43 changes: 43 additions & 0 deletions src/containerapp/azext_containerapp/_github_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
# --------------------------------------------------------------------------------------------
# pylint: disable=consider-using-f-string

import os
import json
from datetime import datetime

from azure.cli.core.util import open_page_in_browser
from azure.cli.core.auth.persistence import SecretStore, build_persistence
from azure.cli.core.azclierror import (ValidationError, CLIInternalError, UnclassifiedUserFault)
from ._utils import repo_url_to_name
from knack.log import get_logger

logger = get_logger(__name__)
Expand All @@ -25,6 +31,43 @@
]


def _get_github_token_secret_store(cmd):
location = os.path.join(cmd.cli_ctx.config.config_dir, "github_token_cache")
file_persistence = build_persistence(location, encrypt=True)
return SecretStore(file_persistence)


def cache_github_token(cmd, token, repo):
repo = repo_url_to_name(repo)
secret_store = _get_github_token_secret_store(cmd)
cache = secret_store.load()

for entry in cache:
if isinstance(entry, dict) and entry.get("value") == token:
if repo not in entry.get("repos", []):
entry["repos"] = [*entry.get("repos", []), repo]
entry["last_modified_timestamp"] = datetime.utcnow().timestamp()
break
else:
cache_entry = {"last_modified_timestamp": datetime.utcnow().timestamp(), "value": token, "repos": [repo]}
cache = [cache_entry, *cache]

secret_store.save(cache)


def load_github_token_from_cache(cmd, repo):
repo = repo_url_to_name(repo)
secret_store = _get_github_token_secret_store(cmd)
cache = secret_store.load()

if isinstance(cache, list):
for entry in cache:
if isinstance(entry, dict) and repo in entry.get("repos", []):
return entry.get("value")

return None


def get_github_access_token(cmd, scope_list=None, token=None): # pylint: disable=unused-argument
if token:
return token
Expand Down
13 changes: 13 additions & 0 deletions src/containerapp/azext_containerapp/_up_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
create_or_update_github_action,
)

from ._github_oauth import load_github_token_from_cache, get_github_access_token

logger = get_logger(__name__)


Expand Down Expand Up @@ -867,3 +869,14 @@ def check_env_name_on_rg(cmd, managed_env, resource_group_name, location):
if env_def:
if location != env_def["location"]:
raise ValidationError("Environment {} already exists in resource group {} on location {}, cannot change location of existing environment to {}.".format(parse_resource_id(managed_env)["name"], resource_group_name, env_def["location"], location))


def get_token(cmd, repo, token):
if not repo:
return None
if token:
return token
token = load_github_token_from_cache(cmd, repo)
if not token:
token = get_github_access_token(cmd, ["admin:repo_hook", "repo", "workflow"], token)
return token
4 changes: 2 additions & 2 deletions src/containerapp/azext_containerapp/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def get_workflow(github_repo, name): # pylint: disable=inconsistent-return-stat
workflows = list(github_repo.get_workflows())
workflows.sort(key=lambda r: r.created_at, reverse=True) # sort by latest first
for wf in workflows:
if wf.path.startswith(f".github/workflows/{name}") and "Trigger auto deployment for containerapp" in wf.name:
if wf.path.startswith(f".github/workflows/{name}") and f"Trigger auto deployment for" in wf.name:
return wf


Expand Down Expand Up @@ -244,7 +244,7 @@ def await_github_action(cmd, token, repo, branch, name, resource_group_name, tim

def repo_url_to_name(repo_url):
repo = None
repo = repo_url.split('/')
repo = [s for s in repo_url.split('/') if s]
if len(repo) >= 2:
repo = '/'.join(repo[-2:])
return repo
Expand Down
6 changes: 4 additions & 2 deletions src/containerapp/azext_containerapp/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -2015,7 +2015,8 @@ def containerapp_up(cmd,
from ._up_utils import (_validate_up_args, _reformat_image, _get_dockerfile_content, _get_ingress_and_target_port,
ResourceGroup, ContainerAppEnvironment, ContainerApp, _get_registry_from_app,
_get_registry_details, _create_github_action, _set_up_defaults, up_output, AzureContainerRegistry,
check_env_name_on_rg)
check_env_name_on_rg, get_token)
from ._github_oauth import cache_github_token, load_github_token_from_cache
HELLOWORLD = "mcr.microsoft.com/azuredocs/containerapps-helloworld"
dockerfile = "Dockerfile" # for now the dockerfile name must be "Dockerfile" (until GH actions API is updated)

Expand All @@ -2024,7 +2025,7 @@ def containerapp_up(cmd,
check_env_name_on_rg(cmd, managed_env, resource_group_name, location)

image = _reformat_image(source, repo, image)
token = None if not repo else get_github_access_token(cmd, ["admin:repo_hook", "repo", "workflow"], token)
token = get_token(cmd, repo, token)

if image and HELLOWORLD in image.lower():
ingress = "external" if not ingress else ingress
Expand Down Expand Up @@ -2064,6 +2065,7 @@ def containerapp_up(cmd,
if repo:
_create_github_action(app, env, service_principal_client_id, service_principal_client_secret,
service_principal_tenant_id, branch, token, repo, context_path)
cache_github_token(cmd, token, repo)

if browse:
open_containerapp_in_browser(cmd, app.name, app.resource_group.name)
Expand Down

0 comments on commit 302bbb5

Please sign in to comment.