Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add release channels add-accounts remove-accounts commands #1955

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
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'."
)
sfc-gh-gbloom marked this conversation as resolved.
Show resolved Hide resolved

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
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
Loading