Skip to content

Commit

Permalink
Merge pull request Azure#1 from harryli0108/patchList
Browse files Browse the repository at this point in the history
Patch list updates
  • Loading branch information
snehapar9 authored May 4, 2023
2 parents 9f425d8 + 9af1fc8 commit 04f21ed
Show file tree
Hide file tree
Showing 10 changed files with 551 additions and 205 deletions.
142 changes: 141 additions & 1 deletion .github/fabricbot.json
Original file line number Diff line number Diff line change
Expand Up @@ -4200,7 +4200,7 @@
{
"name": "requestReviewer",
"parameters": {
"reviewer": "wangzelin007"
"reviewer": "yanzhudd"
}
}
]
Expand Down Expand Up @@ -12647,6 +12647,146 @@
}
]
}
},
{
"taskType": "trigger",
"capabilityId": "IssueResponder",
"subCapability": "PullRequestResponder",
"version": "1.0",
"config": {
"conditions": {
"operator": "and",
"operands": [
{
"name": "isAction",
"parameters": {
"action": "opened"
}
},
{
"operator": "or",
"operands": [
{
"name": "titleContains",
"parameters": {
"titlePattern": "[Vv]m-repair",
"isRegex": true
}
},
{
"name": "bodyContains",
"parameters": {
"bodyPattern": "[Vv]m-repair",
"isRegex": true
}
}
]
}
]
},
"eventType": "pull_request",
"eventNames": [
"pull_request",
"issues",
"project_card"
],
"taskName": "[vm-repair] Auto assign labels and reviewers based on PR title/description.",
"actions": [
{
"name": "requestReviewer",
"parameters": {
"reviewer": "zhoxing-ms"
}
},
{
"name": "requestReviewer",
"parameters": {
"reviewer": "yanzhudd"
}
},
{
"name": "addLabel",
"parameters": {
"label": "Auto-Assign"
}
},
{
"name": "assignToUser",
"parameters": {
"user": "zhoxing-ms"
}
}
]
}
},
{
"taskType": "trigger",
"capabilityId": "IssueResponder",
"subCapability": "IssuesOnlyResponder",
"version": "1.0",
"config": {
"conditions": {
"operator": "and",
"operands": [
{
"operator": "or",
"operands": [
{
"name": "titleContains",
"parameters": {
"titlePattern": "az azurestackhci",
"isRegex": true
}
},
{
"name": "bodyContains",
"parameters": {
"bodyPattern": "az azurestackhci",
"isRegex": true
}
}
]
},
{
"operator": "or",
"operands": [
{
"name": "isAction",
"parameters": {
"action": "opened"
}
}
]
}
]
},
"eventType": "issue",
"eventNames": [
"issues",
"project_card"
],
"taskName": "[azurestackhci] auto assign labels and users based on issue description.",
"actions": [
{
"name": "addLabel",
"parameters": {
"label": "Auto-Assign"
}
},
{
"name": "addLabel",
"parameters": {
"label": "AzureStackHCI"
}
},
{
"name": "addLabel",
"parameters": {
"label": "CXP Attention"
}
}
]
}
}
],
"userGroups": [
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/AddPRComment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ jobs:
name: Say thanks for the PR
steps:
- name: get message
env:
TITLE: ${{ github.event.pull_request.title }}
run: |
message=$(echo '${{ github.event.pull_request.title }}' | grep -oP '[{\[][^}\]]+[}\]]' | sed 's/{\|}\|\[\|\]//g')
message=$(echo "$TITLE" | grep -oP '[{\[][^}\]]+[}\]]' | sed 's/{\|}\|\[\|\]//g')
echo "message=$message" >> $GITHUB_ENV
if [ -z $message ]; then
echo "message=$(echo 'Thank you for your contribution! We will review the pull request and get back to you soon.')" >> $GITHUB_ENV
Expand Down
23 changes: 23 additions & 0 deletions src/containerapp/azext_containerapp/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,26 @@
"validationMethod": None # str
}
}

# ContainerApp Patch
ImageProperties = {
"imageName": None,
"targetContainerAppName": None
}

ImagePatchableCheck = {
"targetContainerAppName": None,
"oldRunImage": None,
"newRunImage": None,
"id": None,
"reason": None
}

OryxMarinerRunImgTagProperty = {
"fullTag": None,
"framework": None,
"version": None,
"marinerVersion": None,
"architectures": None,
"support": None,
}
75 changes: 75 additions & 0 deletions src/containerapp/azext_containerapp/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import time
import json
import platform
import hashlib
import requests
import packaging.version as SemVer
import re

from urllib.parse import urlparse
from datetime import datetime
Expand All @@ -31,6 +35,7 @@
LOG_ANALYTICS_RP, CONTAINER_APPS_RP, CHECK_CERTIFICATE_NAME_AVAILABILITY_TYPE, ACR_IMAGE_SUFFIX,
LOGS_STRING, PENDING_STATUS, SUCCEEDED_STATUS, UPDATING_STATUS)
from ._models import (ContainerAppCustomDomainEnvelope as ContainerAppCustomDomainEnvelopeModel, ManagedCertificateEnvelop as ManagedCertificateEnvelopModel)
from ._models import ImagePatchableCheck, OryxMarinerRunImgTagProperty

logger = get_logger(__name__)

Expand Down Expand Up @@ -1714,3 +1719,73 @@ def format_location(location=None):
if location:
return location.lower().replace(" ", "").replace("(", "").replace(")", "")
return location

def patchableCheck(repoTagSplit: str, oryxBuilderRunImgTags, bom):
tagProp = parseOryxMarinerTag(repoTagSplit)
repoTagSplit = repoTagSplit.split("-")
if repoTagSplit[1] == "dotnet":
matchingVersionInfo = oryxBuilderRunImgTags[repoTagSplit[2]][str(tagProp["version"].major) + "." + str(tagProp["version"].minor)][tagProp["support"]][tagProp["marinerVersion"]]

# Check if the image minor version is four less than the latest minor version
if tagProp["version"] < matchingVersionInfo[0]["version"]:
result = ImagePatchableCheck
result["targetContainerAppName"] = bom["targetContainerAppName"]
result["oldRunImage"] = tagProp["fullTag"]
if (tagProp["version"].minor == matchingVersionInfo[0]["version"].minor) and (tagProp["version"].micro < matchingVersionInfo[0]["version"].micro):
# Patchable
result["newRunImage"] = "mcr.microsoft.com/oryx/builder:" + matchingVersionInfo[0]["fullTag"]
result["id"] = hashlib.md5(str(result["oldRunImage"] + result["targetContainerAppName"] + result["newRunImage"]).encode()).hexdigest()
result["reason"] = "New security patch released for your current run image."
else:
# Not patchable
result["newRunImage"] = "mcr.microsoft.com/oryx/builder:" + matchingVersionInfo[0]["fullTag"]
result["reason"] = "The image is not pachable Please check for major or minor version upgrade."
else:
result = ImagePatchableCheck
result["targetContainerAppName"] = bom["targetContainerAppName"]
result["oldRunImage"] = tagProp["fullTag"]
result["reason"] = "You're already up to date!"
return result

def getCurrentMarinerTags() -> list(OryxMarinerRunImgTagProperty):
r = requests.get("https://mcr.microsoft.com/v2/oryx/builder/tags/list")
tags = r.json()
# tags = dict(tags=["run-dotnet-aspnet-7.0.1-cbl-mariner2.0", "run-dotnet-aspnet-7.0.1-cbl-mariner1.0", "run-dotnet-aspnet-7.1.0-cbl-mariner2.0"])
tagList = {}
# only keep entries that container keyword "mariner"
tags = [tag for tag in tags["tags"] if "mariner" in tag]
for tag in tags:
tagObj = parseOryxMarinerTag(tag)
if tagObj:
majorMinorVer = str(tagObj["version"].major) + "." + str(tagObj["version"].minor)
support = tagObj["support"]
framework = tagObj["framework"]
marinerVer = tagObj["marinerVersion"]
if framework in tagList.keys():
if majorMinorVer in tagList[framework].keys():
if support in tagList[framework][majorMinorVer].keys():
if marinerVer in tagList[framework][majorMinorVer][support].keys():
tagList[framework][majorMinorVer][support][marinerVer].append(tagObj)
tagList[framework][majorMinorVer][support][marinerVer].sort(reverse=True, key=lambda x: x["version"])
else:
tagList[framework][majorMinorVer][support][marinerVer] = [tagObj]
else:
tagList[framework][majorMinorVer][support] = {marinerVer: [tagObj]}
else:
tagList[framework][majorMinorVer] = {support: {marinerVer: [tagObj]}}
else:
tagList[framework] = {majorMinorVer: {support: {marinerVer: [tagObj]}}}
return tagList

def parseOryxMarinerTag(tag: str) -> OryxMarinerRunImgTagProperty:
tagSplit = tag.split("-")
if tagSplit[0] == "run" and tagSplit[1] == "dotnet":
versionRE = r"(\d+\.\d+(\.\d+)?).*?(cbl-mariner(\d+\.\d+))"
REmatches = re.findall(versionRE, tag)
if REmatches.count == 0:
tagObj = None
else:
tagObj = dict(fullTag=tag, version=SemVer.parse(REmatches[0][0]), framework=tagSplit[2], marinerVersion=REmatches[0][2], architectures=None, support="lts")
else:
tagObj = None
return tagObj
4 changes: 2 additions & 2 deletions src/containerapp/azext_containerapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,5 +200,5 @@ def load_command_table(self, _):
g.custom_command('set', 'set_workload_profile')
g.custom_command('delete', 'delete_workload_profile')

with self.command_group('containerapp patch') as g:
g.custom_command('list','list_dummy_values',exception_handler=ex_handler_factory())
with self.command_group('containerapp patch', is_preview=True) as g:
g.custom_command('list', 'patch_list')
73 changes: 72 additions & 1 deletion src/containerapp/azext_containerapp/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from urllib.parse import urlparse
import requests
import json
import subprocess

from azure.cli.core.azclierror import (
RequiredArgumentMissingError,
Expand Down Expand Up @@ -58,6 +59,7 @@
ScaleRule as ScaleRuleModel,
Volume as VolumeModel,
VolumeMount as VolumeMountModel,)
from ._models import DotnetTagProperty, ImagePatchableCheck, ImageProperties

from ._utils import (_validate_subscription_registered, _ensure_location_allowed,
parse_secret_flags, store_as_secret_and_return_secret_ref, parse_env_var_flags,
Expand All @@ -74,7 +76,8 @@
validate_environment_location, safe_set, parse_metadata_flags, parse_auth_flags, _azure_monitor_quickstart,
set_ip_restrictions, certificate_location_matches, certificate_matches, generate_randomized_managed_cert_name,
check_managed_cert_name_availability, prepare_managed_certificate_envelop,
get_default_workload_profile_name_from_env, get_default_workload_profiles, ensure_workload_profile_supported, _generate_secret_volume_name)
get_default_workload_profile_name_from_env, get_default_workload_profiles, ensure_workload_profile_supported, _generate_secret_volume_name,
getCurrentMarinerTags, patchableCheck)
from ._validators import validate_create, validate_revision_suffix
from ._ssh_utils import (SSH_DEFAULT_ENCODING, WebSocketConnection, read_ssh, get_stdin_writer, SSH_CTRL_C_MSG,
SSH_BACKUP_ENCODING)
Expand Down Expand Up @@ -4322,3 +4325,71 @@ def delete_workload_profile(cmd, resource_group_name, env_name, workload_profile
return r
except Exception as e:
handle_raw_exception(e)

def patch_list(cmd, resource_group_name=None, managed_env=None):
caList = list_containerapp(cmd, resource_group_name, managed_env)
imgs = []
if caList:
for ca in caList:
containers = ca["properties"]["template"]["containers"]
for container in containers:
result = dict(imageName=container["image"], targetContainerAppName=container["name"])
imgs.append(result)

# Get the BOM of the images
results = []
boms = []

## For production
#
for img in imgs:
subprocess.run("pack inspect-image " + img["imageName"] + " --output json > /tmp/bom.json 2>&1", shell=True)
with open("/tmp/bom.json", "rb") as f:
bom = json.load(f)
bom.update('{ "targetContainerAppName": img["targetContainerAppName"] }')
boms.append(bom)

## For testing
#
# with open("./bom.json", "rb") as f:
# lines = f.read()
# # if lines.find(b"status code 401 Unauthorized") == -1 or lines.find(b"unable to find image") == -1:
# # bom = dict(remote_info=401)
# # else:
# bom = json.loads(lines)
# bom.update({ "targetContainerAppName": "test-containerapp-1" })
# boms.append(bom)

# Get the current tags of Dotnet Mariners
oryxRunImgTags = getCurrentMarinerTags()

# Start checking if the images are based on Mariner
for bom in boms:
if bom["remote_info"] == 401:
result = ImagePatchableCheck
result["targetContainerAppName"] = bom["targetContainerAppName"]
result["reason"] = "Failed to get BOM of the image. Please check if the image exists or you have the permission to access the image."
results.append(result)
else:
# devide run-images into different parts by "/"
runImagesProps = bom["remote_info"]["run_images"]
for runImagesProp in runImagesProps:
if (runImagesProp["name"].find("mcr.microsoft.com/oryx/builder") != -1):
runImagesProp = runImagesProp["name"].split(":")
runImagesTag = runImagesProp[1]
# Based on Mariners
if runImagesTag.find('mariner') != -1:
result = patchableCheck(runImagesTag, oryxRunImgTags, bom=bom)
else:
result = ImagePatchableCheck
result["targetContainerAppName"] = bom["targetContainerAppName"]
result["oldRunImage"] = bom["remote_info"]["run_images"]
result["reason"] = "Image not based on Mariners"
else:
# Not based on image from mcr.microsoft.com/dotnet
result = ImagePatchableCheck
result["targetContainerAppName"] = bom["targetContainerAppName"]
result["oldRunImage"] = bom["remote_info"]["run_images"]
result["reason"] = "Image not from mcr.microsoft.com/oryx/builder"
results.append(result)
return results
Loading

0 comments on commit 04f21ed

Please sign in to comment.