Files
2026-07-13 13:17:40 +08:00

138 lines
4.7 KiB
Python

import json
import os
import random
import shutil
import string
import tempfile
import time
from typing import Optional, Tuple
from urllib.parse import urlparse
import boto3
from azure.identity import CertificateCredential
from azure.storage.blob import BlobServiceClient
from ray_release.logger import logger
_AZURE_ACCOUNT_SECRET_ID = "azure-service-principal-oss-release"
_AZURE_CERTIFICATE_SECRET_ID = "azure-service-principal-certificate"
_AZURE_CREDENTIAL = [None]
def get_azure_credential() -> CertificateCredential:
if _AZURE_CREDENTIAL[0] is None:
secret_manager = boto3.client("secretsmanager", region_name="us-west-2")
azure_account = secret_manager.get_secret_value(
SecretId=_AZURE_ACCOUNT_SECRET_ID
)
azure_account = json.loads(azure_account["SecretString"])
client_id = azure_account["client_id"]
tenant_id = azure_account["tenant_id"]
certificate_secret = secret_manager.get_secret_value(
SecretId=_AZURE_CERTIFICATE_SECRET_ID
)
certificate = certificate_secret["SecretString"]
with tempfile.TemporaryDirectory() as tmp_dir:
certificate_path = os.path.join(tmp_dir, "azure_cert.pem")
with open(certificate_path, "w") as f:
f.write(certificate)
_AZURE_CREDENTIAL[0] = CertificateCredential(
tenant_id=tenant_id,
client_id=client_id,
certificate_path=certificate_path,
)
return _AZURE_CREDENTIAL[0]
def generate_tmp_cloud_storage_path() -> str:
return "".join(random.choice(string.ascii_lowercase) for i in range(10))
def _upload_file_to_azure(
local_file_path: str,
azure_file_path: str,
blob_service_client: Optional[BlobServiceClient] = None,
) -> None:
"""Upload a file to Azure Blob Storage.
Args:
local_file_path: Path to local file to upload.
azure_file_path: Path to file in Azure blob storage.
"""
account, container, path = _parse_abfss_uri(azure_file_path)
account_url = f"https://{account}.blob.core.windows.net"
if blob_service_client is None:
credential = get_azure_credential()
blob_service_client = BlobServiceClient(account_url, credential)
blob_client = blob_service_client.get_blob_client(container=container, blob=path)
try:
with open(local_file_path, "rb") as f:
blob_client.upload_blob(data=f, overwrite=True)
except Exception as e:
logger.exception(f"Failed to upload file to Azure Blob Storage: {e}")
raise
def archive_directory(directory_path: str) -> str:
timestamp = str(int(time.time()))
archived_filename = f"ray_release_{timestamp}.zip"
output_path = os.path.abspath(archived_filename)
shutil.make_archive(output_path[:-4], "zip", directory_path)
return output_path
def upload_working_dir_to_azure(working_dir: str, azure_directory_uri: str) -> str:
"""Upload archived working directory to Azure blob storage.
Args:
working_dir: Path to directory to upload.
azure_directory_uri: Path to directory in Azure blob storage.
Returns:
Azure blob storage path where archived directory was uploaded.
"""
archived_file_path = archive_directory(working_dir)
archived_filename = os.path.basename(archived_file_path)
azure_file_path = f"{azure_directory_uri}/{archived_filename}"
_upload_file_to_azure(
local_file_path=archived_file_path, azure_file_path=azure_file_path
)
return azure_file_path
def _parse_abfss_uri(uri: str) -> Tuple[str, str, str]:
"""Parse ABFSS URI to extract account, container, and path.
ABFSS URI format: abfss://container@account.dfs.core.windows.net/path
Returns: (account_name, container_name, path)
"""
parsed = urlparse(uri)
if "@" not in parsed.netloc:
raise ValueError(
f"Invalid ABFSS URI format: {uri}. "
"Expected format: abfss://container@account.dfs.core.windows.net/path"
)
# Split netloc into container@account.dfs.core.windows.net
container, account_part = parsed.netloc.split("@", 1)
# Extract account name from account.dfs.core.windows.net
account = account_part.split(".")[0]
# Path starts with / which we keep for the blob path
path = parsed.path.lstrip("/")
return account, container, path
def convert_abfss_uri_to_https(uri: str) -> str:
"""Convert ABFSS URI to HTTPS URI.
ABFSS URI format: abfss://container@account.dfs.core.windows.net/path
Returns: HTTPS URI format: https://account.dfs.core.windows.net/container/path
"""
account, container, path = _parse_abfss_uri(uri)
return f"https://{account}.dfs.core.windows.net/{container}/{path}"