зеркало из https://github.com/microsoft/hi-ml.git
MNT: Upgrade to password-free authentication (#925)
Remove the use of Service Principal authentication. To make this work, I also had to add to modify the Service Principal. In the "Clients & Secrets" section, add "Federated Credentials". Choose "Scenario: GitHub Action". Fill in repo details, "Entity Type: Pull Request". Name does not matter.
This commit is contained in:
Родитель
61a2c4d330
Коммит
9ec8fd4426
|
@ -0,0 +1,45 @@
|
|||
name: 'Log into Azure using OpenID Connect'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
|
||||
# Github agents runners sometimes are out of sync with the NTP server, and this can cause issues with the token expiry.
|
||||
# Hence, sync time before logging into Azure.
|
||||
- name: Synchronize time with NTP server
|
||||
run: sudo timedatectl set-ntp true
|
||||
shell: bash
|
||||
|
||||
- name: Login to Azure via OpenID Connect
|
||||
uses: azure/login@v1
|
||||
with:
|
||||
client-id: ${{ env.HIML_SERVICE_PRINCIPAL_ID }}
|
||||
tenant-id: ${{ env.HIML_TENANT_ID }}
|
||||
subscription-id: ${{ env.HIML_SUBSCRIPTION_ID }}
|
||||
|
||||
# The step above only acquires an ID token and an access token for the Azure Resource Manager scope.
|
||||
# This ID token has an expiry of 10min. During the tests, we will acquire access tokens for further
|
||||
# scopes, but at that time, the ID token will be expired. Hence, acquire those access tokens now,
|
||||
# because they will have an expiry of 60min.
|
||||
# https://github.com/Azure/azure-cli/issues/28708#issuecomment-2047256166
|
||||
- name: Get access tokens
|
||||
run: |
|
||||
az account get-access-token --scope https://management.azure.com/.default --output none
|
||||
az account get-access-token --scope https://storage.azure.com/.default --output none
|
||||
az account get-access-token --scope https://ml.azure.com/.default --output none
|
||||
az account get-access-token --scope https://management.core.windows.net/.default --output none
|
||||
shell: bash
|
||||
|
||||
# Workaround for bug in MSAL taken from
|
||||
# https://github.com/Azure/azure-cli/issues/28708#issuecomment-2049718869
|
||||
- name: Fetch OID token every 4 mins in the background
|
||||
shell: bash
|
||||
run: |
|
||||
while true; do
|
||||
token_request=$ACTIONS_ID_TOKEN_REQUEST_TOKEN
|
||||
token_uri=$ACTIONS_ID_TOKEN_REQUEST_URL
|
||||
token=$(curl -H "Authorization: bearer $token_request" "${token_uri}&audience=api://AzureADTokenExchange" | jq .value -r)
|
||||
az login --service-principal -u ${{ env.HIML_SERVICE_PRINCIPAL_ID }} -t ${{ env.HIML_TENANT_ID }} --federated-token $token --output none
|
||||
# Sleep for 4 minutes
|
||||
sleep 240
|
||||
done &
|
|
@ -2,7 +2,8 @@ name: AzureML_SDK
|
|||
channels:
|
||||
- defaults
|
||||
dependencies:
|
||||
- pip=23.3
|
||||
- python=3.9.18
|
||||
- pip=23.0.1
|
||||
- python=3.10
|
||||
- pip:
|
||||
- azureml-sdk==1.53.0
|
||||
- azureml-sdk==1.54.0
|
||||
- azure-cli
|
||||
|
|
|
@ -7,16 +7,12 @@ import re
|
|||
|
||||
from azureml._restclient.constants import RunStatus
|
||||
from azureml.core import Experiment, Run, Workspace
|
||||
from azureml.core.authentication import ServicePrincipalAuthentication
|
||||
from azureml.core.authentication import AzureCliAuthentication
|
||||
|
||||
|
||||
def cancel_running_and_queued_jobs() -> None:
|
||||
print("Authenticating")
|
||||
auth = ServicePrincipalAuthentication(
|
||||
tenant_id='72f988bf-86f1-41af-91ab-2d7cd011db47',
|
||||
service_principal_id=os.environ["HIML_SERVICE_PRINCIPAL_ID"],
|
||||
service_principal_password=os.environ["HIML_SERVICE_PRINCIPAL_PASSWORD"],
|
||||
)
|
||||
auth = AzureCliAuthentication()
|
||||
print("Getting AML workspace")
|
||||
workspace = Workspace.get(
|
||||
name="hi-ml",
|
||||
|
|
|
@ -18,6 +18,12 @@ concurrency:
|
|||
group: ${{ github.ref }}/cpath-pr
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
permissions:
|
||||
# This is required for requesting the Azure login token
|
||||
id-token: write
|
||||
# This is required for actions/checkout
|
||||
contents: read
|
||||
|
||||
env:
|
||||
pythonVersion: 3.9
|
||||
folder: hi-ml-cpath
|
||||
|
@ -26,7 +32,6 @@ env:
|
|||
HIML_SUBSCRIPTION_ID: ${{ secrets.HIML_SUBSCRIPTION_ID }}
|
||||
HIML_WORKSPACE_NAME: ${{ secrets.HIML_WORKSPACE_NAME }}
|
||||
HIML_SERVICE_PRINCIPAL_ID: ${{ secrets.HIML_SERVICE_PRINCIPAL_ID }}
|
||||
HIML_SERVICE_PRINCIPAL_PASSWORD: ${{ secrets.HIML_SERVICE_PRINCIPAL_PASSWORD }}
|
||||
# Set the AML experiment name for all AML jobs submitted during tests. Github.ref looks like
|
||||
# "refs/pull/123/merge" for PR builds.
|
||||
HIML_EXPERIMENT_NAME: ${{ github.ref }}
|
||||
|
@ -38,6 +43,9 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Cancel previous AzureML runs
|
||||
uses: ./.github/actions/cancel_azureml_jobs
|
||||
|
||||
|
@ -87,6 +95,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -114,6 +125,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Run GPU tests
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -138,6 +152,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -154,6 +171,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -170,6 +190,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -186,6 +209,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -202,6 +228,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -218,6 +247,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -234,6 +266,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -250,6 +285,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -266,6 +304,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -282,6 +323,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -298,6 +342,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
@ -314,6 +361,9 @@ jobs:
|
|||
- name: Prepare Conda environment
|
||||
uses: ./.github/actions/prepare_cpath_environment
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: smoke test
|
||||
run: |
|
||||
cd ${{ env.folder }}
|
||||
|
|
|
@ -16,6 +16,12 @@ concurrency:
|
|||
group: ${{ github.ref }}/hi-ml-pr
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
permissions:
|
||||
# This is required for requesting the Azure login token
|
||||
id-token: write
|
||||
# This is required for actions/checkout
|
||||
contents: read
|
||||
|
||||
env:
|
||||
pythonVersion: 3.9
|
||||
HIML_TENANT_ID: ${{ secrets.HIML_TENANT_ID }}
|
||||
|
@ -23,7 +29,6 @@ env:
|
|||
HIML_SUBSCRIPTION_ID: ${{ secrets.HIML_SUBSCRIPTION_ID }}
|
||||
HIML_WORKSPACE_NAME: ${{ secrets.HIML_WORKSPACE_NAME }}
|
||||
HIML_SERVICE_PRINCIPAL_ID: ${{ secrets.HIML_SERVICE_PRINCIPAL_ID }}
|
||||
HIML_SERVICE_PRINCIPAL_PASSWORD: ${{ secrets.HIML_SERVICE_PRINCIPAL_PASSWORD }}
|
||||
HIML_DIST_ARTIFACT_SUFFIX: '-dist'
|
||||
HIML_PACKAGE_NAME_ARTIFACT_SUFFIX: '-package_name'
|
||||
HIML_VERSION_ARTIFACT_SUFFIX: '-latest_version'
|
||||
|
@ -40,6 +45,9 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Cancel previous AzureML runs
|
||||
uses: ./.github/actions/cancel_azureml_jobs
|
||||
|
||||
|
@ -126,6 +134,9 @@ jobs:
|
|||
# Install local package in editable mode
|
||||
make pip_local
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Test with pytest, fast only
|
||||
run: |
|
||||
cd ${{ matrix.folder }}
|
||||
|
@ -202,6 +213,9 @@ jobs:
|
|||
with:
|
||||
folder: ${{ matrix.folder }}
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Install artifact and test
|
||||
run: |
|
||||
cd ${{ matrix.folder }}
|
||||
|
@ -237,6 +251,9 @@ jobs:
|
|||
with:
|
||||
python-version: ${{ env.pythonVersion }}
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Run smoke_helloworld_v1
|
||||
run: |
|
||||
cd hi-ml
|
||||
|
@ -255,6 +272,9 @@ jobs:
|
|||
with:
|
||||
python-version: ${{ env.pythonVersion }}
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Run smoke_helloworld_v2
|
||||
run: |
|
||||
cd hi-ml
|
||||
|
@ -273,6 +293,9 @@ jobs:
|
|||
with:
|
||||
python-version: ${{ env.pythonVersion }}
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Run smoke_helloworld_v1_2nodes
|
||||
run: |
|
||||
cd hi-ml
|
||||
|
@ -291,6 +314,9 @@ jobs:
|
|||
with:
|
||||
python-version: ${{ env.pythonVersion }}
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Run smoke_helloworld_v2_2nodes
|
||||
run: |
|
||||
cd hi-ml
|
||||
|
@ -398,6 +424,9 @@ jobs:
|
|||
# Install local package in editable mode
|
||||
make pip_local
|
||||
|
||||
- name: Azure login using OpenID Connect
|
||||
uses: ./.github/actions/azure-login
|
||||
|
||||
- name: Install PyPI package and test
|
||||
run: |
|
||||
cd ${{ matrix.folder }}
|
||||
|
|
|
@ -20,7 +20,6 @@ env:
|
|||
HIML_SUBSCRIPTION_ID: ${{ secrets.HIML_SUBSCRIPTION_ID }}
|
||||
HIML_WORKSPACE_NAME: ${{ secrets.HIML_WORKSPACE_NAME }}
|
||||
HIML_SERVICE_PRINCIPAL_ID: ${{ secrets.HIML_SERVICE_PRINCIPAL_ID }}
|
||||
HIML_SERVICE_PRINCIPAL_PASSWORD: ${{ secrets.HIML_SERVICE_PRINCIPAL_PASSWORD }}
|
||||
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
|
||||
jobs:
|
||||
|
|
|
@ -25,6 +25,12 @@ ENV_SERVICE_PRINCIPAL_ID = "HIML_SERVICE_PRINCIPAL_ID"
|
|||
ENV_SERVICE_PRINCIPAL_PASSWORD = "HIML_SERVICE_PRINCIPAL_PASSWORD"
|
||||
ENV_TENANT_ID = "HIML_TENANT_ID"
|
||||
|
||||
# This is an environment variable that is set by GitHub Actions, for checking if the code is running in GitHub
|
||||
ENV_GITHUB_ACTIONS = "GITHUB_ACTIONS"
|
||||
|
||||
# The scope for the access tokens that are requested from Azure
|
||||
ACCESS_TOKEN_SCOPE = "https://management.azure.com/.default"
|
||||
|
||||
|
||||
def get_secret_from_environment(name: str, allow_missing: bool = False) -> Optional[str]:
|
||||
"""
|
||||
|
@ -69,11 +75,17 @@ def get_authentication() -> (
|
|||
try:
|
||||
logger.debug("Trying to authenticate using Azure CLI")
|
||||
auth = AzureCliAuthentication()
|
||||
_ = auth.get_token()
|
||||
_ = auth.get_token(ACCESS_TOKEN_SCOPE)
|
||||
logger.info("Successfully started AzureCLI authentication.")
|
||||
return auth
|
||||
except AuthenticationException:
|
||||
pass
|
||||
except AuthenticationException as ex:
|
||||
# If the code is running in GitHub, there is no point in even trying to authenticate interactively.
|
||||
# Raise the exception to get some information about the authentication problem.
|
||||
# Otherwise, try to authenticate interactively.
|
||||
# The GITHUB_ACTIONS environment variable is meant to be used exactly for this check
|
||||
# https://docs.github.com/en/actions/learn-github-actions/variables
|
||||
if os.getenv(ENV_GITHUB_ACTIONS, "") == "true":
|
||||
raise AuthenticationException("AzureCLI authentication must be set up when running in GitHub") from ex
|
||||
|
||||
logger.info(
|
||||
"Using interactive login to Azure. To use Service Principal authentication, set the environment "
|
||||
|
@ -90,7 +102,7 @@ def _validate_credential(credential: TokenCredential) -> None:
|
|||
|
||||
:param credential: The credential object to validate.
|
||||
"""
|
||||
credential.get_token("https://management.azure.com/.default")
|
||||
credential.get_token(ACCESS_TOKEN_SCOPE)
|
||||
|
||||
|
||||
def _get_legitimate_service_principal_credential(
|
||||
|
|
|
@ -146,15 +146,19 @@ def test_get_legitimate_device_code_credential() -> None:
|
|||
|
||||
|
||||
@pytest.mark.fast
|
||||
def test_get_legitimate_default_credential() -> None:
|
||||
@pytest.mark.skip(reason="Default azure credential are now the default in CI, and test hence fails")
|
||||
def test_get_legitimate_default_credential_fails() -> None:
|
||||
def _mock_credential_fast_timeout(timeout: int) -> DefaultAzureCredential:
|
||||
return DefaultAzureCredential(timeout=1)
|
||||
|
||||
with patch("health_azure.auth.DefaultAzureCredential", new=_mock_credential_fast_timeout):
|
||||
exception_message = r"DefaultAzureCredential failed to retrieve a token from the included credentials."
|
||||
with pytest.raises(ClientAuthenticationError, match=exception_message):
|
||||
cred = _get_legitimate_default_credential()
|
||||
_get_legitimate_default_credential()
|
||||
|
||||
|
||||
@pytest.mark.fast
|
||||
def test_get_legitimate_default_credential() -> None:
|
||||
with patch("health_azure.auth._validate_credential"):
|
||||
cred = _get_legitimate_default_credential()
|
||||
assert isinstance(cred, DefaultAzureCredential)
|
||||
|
|
|
@ -10,7 +10,6 @@ from pathlib import Path
|
|||
from uuid import uuid4
|
||||
|
||||
from azureml.core.authentication import (
|
||||
InteractiveLoginAuthentication,
|
||||
ServicePrincipalAuthentication,
|
||||
AzureCliAuthentication,
|
||||
)
|
||||
|
@ -25,6 +24,7 @@ from unittest.mock import MagicMock, patch
|
|||
from health_azure.auth import (
|
||||
get_secret_from_environment,
|
||||
get_authentication,
|
||||
ENV_GITHUB_ACTIONS,
|
||||
ENV_SERVICE_PRINCIPAL_ID,
|
||||
ENV_SERVICE_PRINCIPAL_PASSWORD,
|
||||
ENV_TENANT_ID,
|
||||
|
@ -104,10 +104,12 @@ def test_find_file_in_parent_folders(caplog: LogCaptureFixture) -> None:
|
|||
|
||||
@pytest.mark.fast
|
||||
@patch("health_azure.auth.InteractiveLoginAuthentication")
|
||||
def test_get_authentication(mock_interactive_authentication: MagicMock) -> None:
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
get_authentication()
|
||||
assert mock_interactive_authentication.called
|
||||
def test_get_authentication(mock_auth: MagicMock) -> None:
|
||||
# Disable Azure CLI authentication and SP via mocks
|
||||
with patch("health_azure.auth.AzureCliAuthentication", side_effect=AuthenticationException("")):
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
get_authentication()
|
||||
assert mock_auth.called, "Expected InteractiveLoginAuthentication to be called"
|
||||
service_principal_id = "1"
|
||||
tenant_id = "2"
|
||||
service_principal_password = "3"
|
||||
|
@ -228,6 +230,7 @@ def test_auth_azure_cli() -> None:
|
|||
ENV_SERVICE_PRINCIPAL_ID: "foo",
|
||||
ENV_TENANT_ID: "bar",
|
||||
ENV_SERVICE_PRINCIPAL_PASSWORD: "",
|
||||
ENV_GITHUB_ACTIONS: "",
|
||||
}
|
||||
|
||||
# Patch environment variables to have no service principal
|
||||
|
|
|
@ -7,6 +7,7 @@ from __future__ import annotations
|
|||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from torch.hub import load_state_dict_from_url
|
||||
from torchvision.datasets.utils import download_url
|
||||
|
@ -75,7 +76,7 @@ def get_biovil_image_encoder(pretrained: bool = True) -> ImageModel:
|
|||
return image_model
|
||||
|
||||
|
||||
def get_biovil_t_image_encoder(**kwargs) -> ImageModel:
|
||||
def get_biovil_t_image_encoder(**kwargs: Any) -> ImageModel:
|
||||
"""Download weights from Hugging Face and instantiate the image model."""
|
||||
|
||||
biovilt_checkpoint_path = _download_biovil_t_image_model_weights()
|
||||
|
|
Загрузка…
Ссылка в новой задаче