Skip to content

Commit

Permalink
[CU-86b2kxt36] Add add and update storage commands for Azure
Browse files Browse the repository at this point in the history
  • Loading branch information
mpanik committed Dec 9, 2024
1 parent 344edb4 commit ca8d022
Show file tree
Hide file tree
Showing 7 changed files with 467 additions and 13 deletions.
36 changes: 36 additions & 0 deletions dnastack/cli/commands/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from pathlib import Path
from typing import Optional

import click


def handle_value_or_file(value: Optional[str], param_name: str) -> str:
"""
Handle parameter values that can be provided directly or loaded from a file using @ prefix.
Args:
value: The parameter value, either direct value or file path starting with @
param_name: Name of the parameter for error messages
Returns:
str: The value, either direct or loaded from file
Raises:
click.BadParameter: If value is missing or file handling fails
"""
if value is None:
raise click.BadParameter(f'{param_name} value is required')

# Handle file path
if value.startswith('@'):
try:
file_content = Path(value[1:]).read_text().strip()
if not file_content:
raise click.BadParameter(f'{param_name} file is empty')
return file_content
except FileNotFoundError:
raise click.BadParameter(f'File not found: {value[1:]}')
except Exception as e:
raise click.BadParameter(f'Failed to read {param_name} from file: {e}')

return value
120 changes: 116 additions & 4 deletions dnastack/cli/commands/workbench/storage/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@
import click
from click import style

from dnastack.cli.commands.workbench.storage.utils import validate_and_load_service_account_json
from dnastack.cli.commands.workbench.storage.utils import validate_and_load_service_account_json, \
handle_sensitive_azure_params
from dnastack.cli.commands.workbench.utils import get_storage_client, NAMESPACE_ARG
from dnastack.cli.core.command import formatted_command
from dnastack.cli.core.command_spec import ArgumentSpec, ArgumentType, CONTEXT_ARG, SINGLE_ENDPOINT_ID_ARG
from dnastack.cli.core.group import formatted_group
from dnastack.cli.helpers.exporter import to_json, normalize
from dnastack.client.workbench.storage.models import AwsStorageAccountCredentials, StorageAccount, Provider, \
GcpStorageAccountCredentials
GcpStorageAccountCredentials, AzureStorageAccountCredentials, AzureCredentialsType


@formatted_group("add")
def add_storage_command_group():
"""Add storage account"""



@formatted_command(
group=add_storage_command_group,
name='aws',
Expand Down Expand Up @@ -118,7 +118,8 @@ def add_aws_storage_account(context: Optional[str],
ArgumentSpec(
name='service_account_json',
arg_names=['--service-account'],
help='The json file for the storage account to use when authenticating with GCP. Use @<path> to load from a file.',
help='The json file for the storage account to use when authenticating with GCP. '
'Use @<path> to load from a file.',
required=True,
),
ArgumentSpec(
Expand Down Expand Up @@ -171,3 +172,114 @@ def add_gcp_storage_account(context: Optional[str],
client = get_storage_client(context, endpoint_id, namespace)
response = client.add_storage_account(storage_account)
click.echo(to_json(normalize(response)))


@formatted_command(
group=add_storage_command_group,
name='azure',
specs=[
ArgumentSpec(
name='storage_id',
arg_type=ArgumentType.POSITIONAL,
help='The storage account id',
required=False,
),
NAMESPACE_ARG,
ArgumentSpec(
name='name',
arg_names=['--name'],
help='A human readable name for the storage account',
required=True,
),
ArgumentSpec(
name='container',
arg_names=['--container'],
help='The name of the blob container',
required=True,
),
ArgumentSpec(
name='storage_account_name',
arg_names=['--storage-account-name'],
help='The Azure storage account\'s name',
required=True,
),
ArgumentSpec(
name='sas',
arg_names=['--sas'],
help='The Shared Access Signature (SAS) for secure access. Use @<path> to load from file.',
required=False,
),
ArgumentSpec(
name='access_key',
arg_names=['--access-key'],
help='The access key for the storage account. Use @<path> to load from file.',
required=False,
),
ArgumentSpec(
name='tenant_id',
arg_names=['--tenant-id'],
help='Refers to the Azure Active Directory (AAD) tenant associated with the storage account. '
'The tenant ID uniquely identifies your organization’s AAD instance.',
required=False,
),
ArgumentSpec(
name='client_id',
arg_names=['--client-id'],
help='Refers to the application ID of the Azure AD service principal. '
'The client ID identifies the application or service trying to access the storage.',
required=False,
),
ArgumentSpec(
name='client_secret',
arg_names=['--client-secret'],
help='Refers to the password or secret key for the service principal. Use @<path> to load from file.',
required=False,
),
CONTEXT_ARG,
SINGLE_ENDPOINT_ID_ARG,
]
)
@handle_sensitive_azure_params()
def add_azure_storage_account(context: Optional[str],
endpoint_id: Optional[str],
namespace: Optional[str],
storage_id: Optional[str],
name: str,
container: str,
storage_account_name: str,
sas: Optional[str],
access_key: Optional[str],
tenant_id: Optional[str],
client_id: Optional[str],
client_secret: Optional[str]):
"""Create a new azure storage account"""
credentials_type = None
if sas:
credentials_type = AzureCredentialsType.SAS_URL
elif access_key:
credentials_type = AzureCredentialsType.ACCESS_KEY
elif tenant_id and client_id and client_secret:
credentials_type = AzureCredentialsType.CLIENT_CREDENTIALS
else:
raise click.BadParameter('Invalid Azure credentials provided. Please provide either SAS, access key, or service principal credentials.')

credentials = AzureStorageAccountCredentials(
sas_url=sas,
access_key=access_key,
tenant_id=tenant_id,
client_id=client_id,
client_secret=client_secret,
storage_account_name=storage_account_name,
azure_credentials_type=credentials_type
)
storage_account = StorageAccount(
id=storage_id,
name=name,
provider=Provider.azure,
bucket=container,
credentials=credentials
)

client = get_storage_client(context, endpoint_id, namespace)
response = client.add_storage_account(storage_account)
click.echo(to_json(normalize(response)))
121 changes: 117 additions & 4 deletions dnastack/cli/commands/workbench/storage/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import click
from click import style

from dnastack.cli.commands.workbench.storage.utils import validate_and_load_service_account_json
from dnastack.cli.commands.workbench.storage.utils import validate_and_load_service_account_json, \
handle_sensitive_azure_params
from dnastack.cli.commands.workbench.utils import get_storage_client, NAMESPACE_ARG
from dnastack.cli.core.command import formatted_command
from dnastack.cli.core.command_spec import ArgumentSpec, ArgumentType, CONTEXT_ARG, SINGLE_ENDPOINT_ID_ARG
from dnastack.cli.core.group import formatted_group
from dnastack.cli.helpers.exporter import to_json, normalize
from dnastack.client.workbench.storage.models import AwsStorageAccountCredentials, StorageAccount, Provider, \
GcpStorageAccountCredentials
GcpStorageAccountCredentials, AzureCredentialsType, AzureStorageAccountCredentials


@formatted_group("update")
Expand All @@ -27,7 +28,7 @@ def update_storage_command_group():
name='storage_id',
arg_type=ArgumentType.POSITIONAL,
help='The storage account id',
required=False,
required=True,
),
NAMESPACE_ARG,
ArgumentSpec(
Expand Down Expand Up @@ -106,7 +107,7 @@ def update_aws_storage_account(context: Optional[str],
name='storage_id',
arg_type=ArgumentType.POSITIONAL,
help='The storage account id',
required=False,
required=True,
),
NAMESPACE_ARG,
ArgumentSpec(
Expand Down Expand Up @@ -172,3 +173,115 @@ def update_gcp_storage_account(context: Optional[str],
existing_storage_account = client.get_storage_account(storage_account_id=storage_id)
response = client.update_storage_account(storage_id, storage_account, existing_storage_account.etag)
click.echo(to_json(normalize(response)))


@formatted_command(
group=update_storage_command_group,
name='azure',
specs=[
ArgumentSpec(
name='storage_id',
arg_type=ArgumentType.POSITIONAL,
help='The storage account id',
required=True,
),
NAMESPACE_ARG,
ArgumentSpec(
name='name',
arg_names=['--name'],
help='A human readable name for the storage account',
required=True,
),
ArgumentSpec(
name='container',
arg_names=['--container'],
help='The name of the blob container',
required=True,
),
ArgumentSpec(
name='storage_account_name',
arg_names=['--storage-account-name'],
help='The Azure storage account\'s name',
required=True,
),
ArgumentSpec(
name='sas',
arg_names=['--sas'],
help='The Shared Access Signature (SAS) for secure access. Use @<path> to load from file.',
required=False,
),
ArgumentSpec(
name='access_key',
arg_names=['--access-key'],
help='The access key for the storage account. Use @<path> to load from file.',
required=False,
),
ArgumentSpec(
name='tenant_id',
arg_names=['--tenant-id'],
help='Refers to the Azure Active Directory (AAD) tenant associated with the storage account. '
'The tenant ID uniquely identifies your organization’s AAD instance.',
required=False,
),
ArgumentSpec(
name='client_id',
arg_names=['--client-id'],
help='Refers to the application ID of the Azure AD service principal. '
'The client ID identifies the application or service trying to access the storage.',
required=False,
),
ArgumentSpec(
name='client_secret',
arg_names=['--client-secret'],
help='Refers to the password or secret key for the service principal. Use @<path> to load from file.',
required=False,
),
CONTEXT_ARG,
SINGLE_ENDPOINT_ID_ARG,
]
)
@handle_sensitive_azure_params()
def add_azure_storage_account(context: Optional[str],
endpoint_id: Optional[str],
namespace: Optional[str],
storage_id: str,
name: str,
container: str,
storage_account_name: str,
sas: Optional[str],
access_key: Optional[str],
tenant_id: Optional[str],
client_id: Optional[str],
client_secret: Optional[str]):
"""Create a new azure storage account"""
credentials_type = None
if sas:
credentials_type = AzureCredentialsType.SAS_URL
elif access_key:
credentials_type = AzureCredentialsType.ACCESS_KEY
elif tenant_id and client_id and client_secret:
credentials_type = AzureCredentialsType.CLIENT_CREDENTIALS
else:
raise click.BadParameter('Invalid Azure credentials provided. Please provide either SAS, access key, or service principal credentials.')

credentials = AzureStorageAccountCredentials(
sas_url=sas,
access_key=access_key,
tenant_id=tenant_id,
client_id=client_id,
client_secret=client_secret,
storage_account_name=storage_account_name,
azure_credentials_type=credentials_type
)
storage_account = StorageAccount(
id=storage_id,
name=name,
provider=Provider.azure,
bucket=container,
credentials=credentials
)

client = get_storage_client(context, endpoint_id, namespace)
existing_storage_account = client.get_storage_account(storage_account_id=storage_id)
response = client.update_storage_account(storage_id, storage_account, existing_storage_account.etag)
click.echo(to_json(normalize(response)))
Loading

0 comments on commit ca8d022

Please sign in to comment.