Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e3c8054
Temporarily append '.dev3' to version number
warren-jones Nov 6, 2024
d818972
Calling upload-blob with return_sas_token = True
warren-jones Nov 22, 2024
b6fa1db
Incorporated azure-quantum-python functions
warren-jones Dec 2, 2024
1ca2709
Adding a SAS token to the storage container URL
warren-jones Dec 4, 2024
2bdf773
Moved an import statement from _storage.py to job.py
warren-jones Dec 4, 2024
1d06fd9
Fixed style check violations
warren-jones Dec 5, 2024
1983073
Deleted an extraneous comment
warren-jones Dec 5, 2024
7bb57e3
Added a unit test
warren-jones Dec 5, 2024
1a105cc
Expanded the unit test
warren-jones Dec 5, 2024
042064b
Made changes to test_submit
warren-jones Dec 6, 2024
703f3e9
Made changes to test_submit
warren-jones Dec 6, 2024
b4ef46d
Updated test_provider_sku_list
warren-jones Dec 6, 2024
950a4e0
Added experimental Python SDK code to run a Qiskit input file
warren-jones Dec 13, 2024
dc940c1
Added test input files and simplified the job submit code
warren-jones Dec 13, 2024
50cee37
Rearranged the submit code to use old param validation
warren-jones Dec 14, 2024
b85a23f
Got it working with imported Python SDK methods
warren-jones Dec 19, 2024
5d71981
Un-did changes to _storage.py and _client_factory.py
warren-jones Dec 19, 2024
941ef50
Fixed Pylint rule violations
warren-jones Dec 19, 2024
9101d4e
Experimenting with ways to suppress the expected azure.identity Crede…
warren-jones Dec 20, 2024
be59ec2
Deleted unused import statements
warren-jones Dec 20, 2024
3733adc
Copied azure-quantum-python files to the CLI repo
warren-jones Dec 21, 2024
a282256
Added missing SDK files and fixed another import
warren-jones Dec 21, 2024
c00aa7c
Added the azure.identity files to vendored_sdks
warren-jones Dec 23, 2024
1ef8c76
Fixed another azure.identity import
warren-jones Dec 23, 2024
40af901
Deleted a commented-out line
warren-jones Dec 23, 2024
3fc5f02
Moved the azure-quantum-python files to the vendored_sdks folder
warren-jones Dec 24, 2024
31cca14
Deleted commented-out lines
warren-jones Dec 26, 2024
5556f1b
Removed 'microsoft.qc' from the default test provider/SKU list and re…
warren-jones Dec 27, 2024
0ad93b0
Made test changes like in branch 29126-remove-resource-estimator-refe…
warren-jones Jan 2, 2025
2924b1c
Resolved merge conflict in test_quantum_jobs.py
warren-jones Jan 21, 2025
643eb4a
Add '.dev3' suffix to version number for final testing
warren-jones Mar 6, 2025
cd9535d
Merged changes from main and resolved conflict
warren-jones Mar 6, 2025
3663787
Remove '.dev3' from the version numbers
warren-jones Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 0 additions & 128 deletions src/quantum/azext_quantum/_storage.py

This file was deleted.

23 changes: 12 additions & 11 deletions src/quantum/azext_quantum/operations/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
import uuid
import knack.log

from azure.cli.command_modules.storage.operations.account import show_storage_account_connection_string
from azure.cli.core.azclierror import (FileOperationError, AzureInternalError,
InvalidArgumentValueError, AzureResponseError,
RequiredArgumentMissingError)

from .._storage import create_container, upload_blob

from ..vendored_sdks.azure_quantum_python.workspace import Workspace
from ..vendored_sdks.azure_quantum_python.storage import upload_blob
from ..vendored_sdks.azure_storage_blob import ContainerClient
from .._client_factory import cf_jobs
from .._list_helper import repack_response_json
from .workspace import WorkspaceInfo
Expand Down Expand Up @@ -324,15 +324,19 @@ def submit(cmd, resource_group_name, workspace_name, location, target_id, job_in
raise RequiredArgumentMissingError("No storage account specified or linked with workspace.")
storage = ws.properties.storage_account.split('/')[-1]
job_id = str(uuid.uuid4())
container_name = "quantum-job-" + job_id
connection_string_dict = show_storage_account_connection_string(cmd, resource_group_name, storage)
connection_string = connection_string_dict["connectionString"]
container_client = create_container(connection_string, container_name)
blob_name = "inputData"

resource_id = "/subscriptions/" + ws_info.subscription + "/resourceGroups/" + ws_info.resource_group + "/providers/Microsoft.Quantum/Workspaces/" + ws_info.name
workspace = Workspace(resource_id=resource_id, location=location)

knack_logger.warning("Getting Azure credential token...")
container_uri = workspace.get_container_uri(job_id=job_id)
Comment thread
warren-jones marked this conversation as resolved.
container_client = ContainerClient.from_container_url(container_uri)

knack_logger.warning("Uploading input data...")
try:
blob_uri = upload_blob(container_client, blob_name, content_type, content_encoding, blob_data, False)
blob_uri = upload_blob(container_client, blob_name, content_type, content_encoding, blob_data, return_sas_token=False)
logger.debug(" - blob uri: %s", blob_uri)
except Exception as e:
# Unexplained behavior:
# QIR bitcode input and QIO (gzip) input data get UnicodeDecodeError on jobs run in tests using
Expand All @@ -343,9 +347,6 @@ def submit(cmd, resource_group_name, workspace_name, location, target_id, job_in
error_msg += f"\nReason: {e.reason}"
raise AzureResponseError(error_msg) from e

start_of_blob_name = blob_uri.find(blob_name)
container_uri = blob_uri[0:start_of_blob_name - 1]

# Combine separate command-line parameters (like shots, target_capability, and entry_point) with job_params
if job_params is None:
job_params = {}
Expand Down
21 changes: 20 additions & 1 deletion src/quantum/azext_quantum/tests/latest/test_quantum_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,26 @@ def test_submit(self):
self.cmd(f"az quantum workspace create -g {test_resource_group} -w {test_workspace_temp} -l {test_location} -a {test_storage} -r {test_provider_sku_list} --skip-autoadd")
self.cmd(f"az quantum workspace set -g {test_resource_group} -w {test_workspace_temp} -l {test_location}")

# Submit a job to Rigetti and look for SAS tokens in URIs in the output
results = self.cmd("az quantum job submit -t rigetti.sim.qvm --job-input-format rigetti.quil.v1 -t rigetti.sim.qvm --job-input-file src/quantum/azext_quantum/tests/latest/input_data/bell-state.quil --job-output-format rigetti.quil-results.v1 -o json").get_output_in_json()
self.assertIn("?sv=", results["containerUri"])
self.assertIn("&st=", results["containerUri"])
self.assertIn("&se=", results["containerUri"])
self.assertIn("&sp=", results["containerUri"])
self.assertIn("&sig=", results["containerUri"])

self.assertIn("?sv=", results["inputDataUri"])
self.assertIn("&st=", results["inputDataUri"])
self.assertIn("&se=", results["inputDataUri"])
self.assertIn("&sp=", results["inputDataUri"])
self.assertIn("&sig=", results["inputDataUri"])

self.assertIn("?sv=", results["outputDataUri"])
self.assertIn("&st=", results["outputDataUri"])
self.assertIn("&se=", results["outputDataUri"])
self.assertIn("&sp=", results["outputDataUri"])
self.assertIn("&sig=", results["outputDataUri"])

# Run a Quil pass-through job on Rigetti
results = self.cmd("az quantum run -t rigetti.sim.qvm --job-input-format rigetti.quil.v1 -t rigetti.sim.qvm --job-input-file src/quantum/azext_quantum/tests/latest/input_data/bell-state.quil --job-output-format rigetti.quil-results.v1 -o json").get_output_in_json()
self.assertIn("ro", results)
Expand All @@ -233,7 +253,6 @@ def test_submit(self):

results = str(self.cmd("az quantum job list --skip 1 -o json").get_output_in_json())
self.assertIn("ionq", results)
self.assertTrue("rigetti" not in results)

results = str(self.cmd("az quantum job list --orderby Target --skip 1 -o json").get_output_in_json())
self.assertIn("rigetti", results)
Expand Down
66 changes: 66 additions & 0 deletions src/quantum/azext_quantum/vendored_sdks/azure_identity/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# ------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
"""Credentials for Azure SDK clients."""

from ._auth_record import AuthenticationRecord
from ._exceptions import AuthenticationRequiredError, CredentialUnavailableError
from ._constants import AzureAuthorityHosts, KnownAuthorities
from ._credentials import (
AuthorizationCodeCredential,
AzureDeveloperCliCredential,
AzureCliCredential,
AzurePowerShellCredential,
CertificateCredential,
ChainedTokenCredential,
ClientAssertionCredential,
ClientSecretCredential,
DefaultAzureCredential,
DeviceCodeCredential,
EnvironmentCredential,
InteractiveBrowserCredential,
ManagedIdentityCredential,
OnBehalfOfCredential,
SharedTokenCacheCredential,
UsernamePasswordCredential,
VisualStudioCodeCredential,
WorkloadIdentityCredential,
AzurePipelinesCredential,
)
from ._persistent_cache import TokenCachePersistenceOptions
from ._bearer_token_provider import get_bearer_token_provider


__all__ = [
"AuthenticationRecord",
"AuthenticationRequiredError",
"AuthorizationCodeCredential",
"AzureAuthorityHosts",
"AzureCliCredential",
"AzureDeveloperCliCredential",
"AzurePipelinesCredential",
"AzurePowerShellCredential",
"CertificateCredential",
"ChainedTokenCredential",
"ClientAssertionCredential",
"ClientSecretCredential",
"CredentialUnavailableError",
"DefaultAzureCredential",
"DeviceCodeCredential",
"EnvironmentCredential",
"InteractiveBrowserCredential",
"KnownAuthorities",
"OnBehalfOfCredential",
"ManagedIdentityCredential",
"SharedTokenCacheCredential",
"TokenCachePersistenceOptions",
"UsernamePasswordCredential",
"VisualStudioCodeCredential",
"WorkloadIdentityCredential",
"get_bearer_token_provider",
]

from ._version import VERSION

__version__ = VERSION
114 changes: 114 additions & 0 deletions src/quantum/azext_quantum/vendored_sdks/azure_identity/_auth_record.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# ------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
import json


SUPPORTED_VERSIONS = {"1.0"}


class AuthenticationRecord:
"""Non-secret account information for an authenticated user

This class enables :class:`DeviceCodeCredential` and :class:`InteractiveBrowserCredential` to access
previously cached authentication data. Applications shouldn't construct instances of this class. They should
instead acquire one from a credential's **authenticate** method, such as
:func:`InteractiveBrowserCredential.authenticate`. See the user_authentication sample for more details.

:param str tenant_id: The tenant the account should authenticate in.
:param str client_id: The client ID of the application which performed the original authentication.
:param str authority: The authority host used to authenticate the account.
:param str home_account_id: A unique identifier of the account.
:param str username: The user principal or service principal name of the account.
"""

def __init__(self, tenant_id: str, client_id: str, authority: str, home_account_id: str, username: str) -> None:
self._authority = authority
self._client_id = client_id
self._home_account_id = home_account_id
self._tenant_id = tenant_id
self._username = username

@property
def authority(self) -> str:
"""The authority host used to authenticate the account.

:rtype: str
"""
return self._authority

@property
def client_id(self) -> str:
"""The client ID of the application which performed the original authentication.

:rtype: str
"""
return self._client_id

@property
def home_account_id(self) -> str:
"""A unique identifier of the account.

:rtype: str
"""
return self._home_account_id

@property
def tenant_id(self) -> str:
"""The tenant the account should authenticate in.

:rtype: str
"""
return self._tenant_id

@property
def username(self) -> str:
"""The user principal or service principal name of the account.

:rtype: str
"""
return self._username

@classmethod
def deserialize(cls, data: str) -> "AuthenticationRecord":
"""Deserialize a record.

:param str data: A serialized record.
:return: The deserialized record.
:rtype: ~azure.identity.AuthenticationRecord
"""

deserialized = json.loads(data)

version = deserialized.get("version")
if version not in SUPPORTED_VERSIONS:
raise ValueError(
'Unexpected version "{}". This package supports these versions: {}'.format(version, SUPPORTED_VERSIONS)
)

return cls(
authority=deserialized["authority"],
client_id=deserialized["clientId"],
home_account_id=deserialized["homeAccountId"],
tenant_id=deserialized["tenantId"],
username=deserialized["username"],
)

def serialize(self) -> str:
"""Serialize the record.

:return: The serialized record.
:rtype: str
"""

record = {
"authority": self._authority,
"clientId": self._client_id,
"homeAccountId": self._home_account_id,
"tenantId": self._tenant_id,
"username": self._username,
"version": "1.0",
}

return json.dumps(record)
Loading