Skip to content

Commit

Permalink
Add release channels add-accounts remove-accounts commands
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-melnacouzi committed Dec 18, 2024
1 parent 0257a0e commit e441a5e
Show file tree
Hide file tree
Showing 10 changed files with 738 additions and 3 deletions.
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* `snow app version create` now returns version, patch, and label in JSON format.
* Add ability to specify release channel when creating application instance from release directive: `snow app run --from-release-directive --channel=<channel>`
* Add ability to list release channels through `snow app release-channel list` command
* Add ability to add and remove accounts from release channels through `snow app release-channel add-accounts` and snow app release-channel remove-accounts` commands.

## Fixes and improvements
* Fixed crashes with older x86_64 Intel CPUs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,86 @@ def _bundle(self, action_ctx: ActionContext = None):

return bundle_map

def action_release_channel_add_accounts(
self,
action_ctx: ActionContext,
release_channel: str,
target_accounts: list[str],
*args,
**kwargs,
):
"""
Adds target accounts to a release channel.
"""

if not target_accounts:
raise ClickException("No target accounts provided.")

available_channels = get_snowflake_facade().show_release_channels(
self.name, self.role
)

release_channel_names = [c.get("name") for c in available_channels]
if not identifier_in_list(release_channel, release_channel_names):
raise ClickException(
f"Release channel {release_channel} does not exist in application package {self.name}."
)

for account in target_accounts:
if not re.fullmatch(
f"{VALID_IDENTIFIER_REGEX}\\.{VALID_IDENTIFIER_REGEX}", account
):
raise ClickException(
f"Target account {account} is not in a valid format. Make sure you provide the target account in the format 'org.account'."
)

get_snowflake_facade().add_accounts_to_release_channel(
package_name=self.name,
release_channel=release_channel,
target_accounts=target_accounts,
role=self.role,
)

def action_release_channel_remove_accounts(
self,
action_ctx: ActionContext,
release_channel: str,
target_accounts: list[str],
*args,
**kwargs,
):
"""
Removes target accounts from a release channel.
"""

if not target_accounts:
raise ClickException("No target accounts provided.")

available_channels = get_snowflake_facade().show_release_channels(
self.name, self.role
)

release_channel_names = [c.get("name") for c in available_channels]
if not identifier_in_list(release_channel, release_channel_names):
raise ClickException(
f"Release channel {release_channel} does not exist in application package {self.name}."
)

for account in target_accounts:
if not re.fullmatch(
f"{VALID_IDENTIFIER_REGEX}\\.{VALID_IDENTIFIER_REGEX}", account
):
raise ClickException(
f"Target account {account} is not in a valid format. Make sure you provide the target account in the format 'org.account'."
)

get_snowflake_facade().remove_accounts_from_release_channel(
package_name=self.name,
release_channel=release_channel,
target_accounts=target_accounts,
role=self.role,
)

def _bundle_children(self, action_ctx: ActionContext) -> List[str]:
# Create _children directory
children_artifacts_dir = self.children_artifacts_deploy_root
Expand Down
69 changes: 69 additions & 0 deletions src/snowflake/cli/_plugins/nativeapp/release_channel/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from snowflake.cli.api.output.types import (
CollectionResult,
CommandResult,
MessageResult,
)

app = SnowTyperFactory(
Expand Down Expand Up @@ -69,3 +70,71 @@ def release_channel_list(

if cli_context.output_format == OutputFormat.JSON:
return CollectionResult(channels)


@app.command("add-accounts", requires_connection=True)
@with_project_definition()
@force_project_definition_v2()
def release_channel_add_accounts(
channel: str = typer.Argument(
show_default=False,
help="The release channel to add accounts to.",
),
target_accounts: list[str] = typer.Option(
show_default=False,
help="The accounts to add to the release channel. Format has to be `org1.account1,org2.account2`.",
),
**options,
) -> CommandResult:
"""
Adds accounts to a release channel.
"""

cli_context = get_cli_context()
ws = WorkspaceManager(
project_definition=cli_context.project_definition,
project_root=cli_context.project_root,
)
package_id = options["package_entity_id"]
ws.perform_action(
package_id,
EntityActions.RELEASE_CHANNEL_ADD_ACCOUNTS,
release_channel=channel,
target_accounts=target_accounts,
)

return MessageResult("Successfully added accounts to the release channel.")


@app.command("remove-accounts", requires_connection=True)
@with_project_definition()
@force_project_definition_v2()
def release_channel_remove_accounts(
channel: str = typer.Argument(
show_default=False,
help="The release channel to remove accounts from.",
),
target_accounts: list[str] = typer.Option(
show_default=False,
help="The accounts to remove from the release channel. Format has to be `org1.account1,org2.account2`.",
),
**options,
) -> CommandResult:
"""
Removes accounts from a release channel.
"""

cli_context = get_cli_context()
ws = WorkspaceManager(
project_definition=cli_context.project_definition,
project_root=cli_context.project_root,
)
package_id = options["package_entity_id"]
ws.perform_action(
package_id,
EntityActions.RELEASE_CHANNEL_REMOVE_ACCOUNTS,
release_channel=channel,
target_accounts=target_accounts,
)

return MessageResult("Successfully removed accounts from the release channel.")
96 changes: 95 additions & 1 deletion src/snowflake/cli/_plugins/nativeapp/sf_sql_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
APPLICATION_REQUIRES_TELEMETRY_SHARING,
CANNOT_DISABLE_MANDATORY_TELEMETRY,
CANNOT_DISABLE_RELEASE_CHANNELS,
CANNOT_MODIFY_RELEASE_CHANNEL_ACCOUNTS,
DOES_NOT_EXIST_OR_CANNOT_BE_PERFORMED,
DOES_NOT_EXIST_OR_NOT_AUTHORIZED,
INSUFFICIENT_PRIVILEGES,
Expand Down Expand Up @@ -1159,7 +1160,8 @@ def show_release_channels(
) -> list[ReleaseChannel]:
"""
Show release channels in a package.
@param package_name: Name of the package
@param package_name: Name of the application package
@param [Optional] role: Role to switch to while running this script. Current role will be used if no role is passed in.
"""

Expand Down Expand Up @@ -1208,6 +1210,98 @@ def show_release_channels(

return results

def add_accounts_to_release_channel(
self,
package_name: str,
release_channel: str,
target_accounts: List[str],
role: str | None = None,
):
"""
Adds accounts to a release channel.
@param package_name: Name of the application package
@param release_channel: Name of the release channel
@param target_accounts: List of target accounts to add to the release channel
@param [Optional] role: Role to switch to while running this script. Current role will be used if no role is passed in.
"""

package_name = to_identifier(package_name)
release_channel = to_identifier(release_channel)

with self._use_role_optional(role):
try:
self._sql_executor.execute_query(
f"alter application package {package_name} modify release channel {release_channel} add accounts = ({','.join(target_accounts)})"
)
except ProgrammingError as err:
if (
err.errno == ACCOUNT_DOES_NOT_EXIST
or err.errno == ACCOUNT_HAS_TOO_MANY_QUALIFIERS
):
raise UserInputError(
f"Invalid account passed in.\n{str(err.msg)}"
) from err
if err.errno == CANNOT_MODIFY_RELEASE_CHANNEL_ACCOUNTS:
raise UserInputError(
f"Cannot modify accounts for release channel {release_channel} in application package {package_name}."
) from err
handle_unclassified_error(
err,
f"Failed to add accounts to release channel {release_channel} in application package {package_name}.",
)
except Exception as err:
handle_unclassified_error(
err,
f"Failed to add accounts to release channel {release_channel} in application package {package_name}.",
)

def remove_accounts_from_release_channel(
self,
package_name: str,
release_channel: str,
target_accounts: List[str],
role: str | None = None,
):
"""
Removes accounts from a release channel.
@param package_name: Name of the application package
@param release_channel: Name of the release channel
@param target_accounts: List of target accounts to remove from the release channel
@param [Optional] role: Role to switch to while running this script. Current role will be used if no role is passed in.
"""

package_name = to_identifier(package_name)
release_channel = to_identifier(release_channel)

with self._use_role_optional(role):
try:
self._sql_executor.execute_query(
f"alter application package {package_name} modify release channel {release_channel} remove accounts = ({','.join(target_accounts)})"
)
except ProgrammingError as err:
if (
err.errno == ACCOUNT_DOES_NOT_EXIST
or err.errno == ACCOUNT_HAS_TOO_MANY_QUALIFIERS
):
raise UserInputError(
f"Invalid account passed in.\n{str(err.msg)}"
) from err
if err.errno == CANNOT_MODIFY_RELEASE_CHANNEL_ACCOUNTS:
raise UserInputError(
f"Cannot modify accounts for release channel {release_channel} in application package {package_name}."
) from err
handle_unclassified_error(
err,
f"Failed to remove accounts from release channel {release_channel} in application package {package_name}.",
)
except Exception as err:
handle_unclassified_error(
err,
f"Failed to remove accounts from release channel {release_channel} in application package {package_name}.",
)


def _strip_empty_lines(text: str) -> str:
"""
Expand Down
2 changes: 2 additions & 0 deletions src/snowflake/cli/api/entities/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class EntityActions(str, Enum):
RELEASE_DIRECTIVE_LIST = "action_release_directive_list"

RELEASE_CHANNEL_LIST = "action_release_channel_list"
RELEASE_CHANNEL_ADD_ACCOUNTS = "action_release_channel_add_accounts"
RELEASE_CHANNEL_REMOVE_ACCOUNTS = "action_release_channel_remove_accounts"
RELEASE_CHANNEL_ADD_VERSION = "action_release_channel_add_version"
RELEASE_CHANNEL_REMOVE_VERSION = "action_release_channel_remove_version"

Expand Down
1 change: 1 addition & 0 deletions src/snowflake/cli/api/errno.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
VERSION_DOES_NOT_EXIST = 93031
ACCOUNT_DOES_NOT_EXIST = 1999
ACCOUNT_HAS_TOO_MANY_QUALIFIERS = 906
CANNOT_MODIFY_RELEASE_CHANNEL_ACCOUNTS = 512017

ERR_JAVASCRIPT_EXECUTION = 100132

Expand Down
Loading

0 comments on commit e441a5e

Please sign in to comment.