Skip to content

Commit

Permalink
[SignalR] az signalr: Add subgroup command start/stop/restart repli…
Browse files Browse the repository at this point in the history
…ca, add/remove ip-rule (Azure#30058)

* Add subgroup command start, stop for azure signalr

* Fix test

* Add subgroup command start, stop, restart for azure signalr replica

* Add subgroup commands for iprules and replica

* Fix linter

---------

Co-authored-by: Duong <dphan@microsoft.com>
  • Loading branch information
phanthaiduong22 and phanthaiduong22 authored Oct 14, 2024
1 parent c16b15b commit 689ffb4
Show file tree
Hide file tree
Showing 14 changed files with 1,757 additions and 909 deletions.
25 changes: 24 additions & 1 deletion src/azure-cli/azure/cli/command_modules/signalr/_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
# pylint: disable=line-too-long

import argparse
from azure.mgmt.signalr.models import UpstreamTemplate
from azure.mgmt.signalr.models import UpstreamTemplate, IPRule
from azure.cli.core.azclierror import InvalidArgumentValueError
from knack.log import get_logger
from knack.util import CLIError

Expand All @@ -24,3 +25,25 @@ def __call__(self, parser, namespace, values, option_string=None):
raise CLIError('usage error: {} KEY=VALUE [KEY=VALUE ...]'.format(option_string))
value = UpstreamTemplate(**kwargs)
super().__call__(parser, namespace, value, option_string)


class IPRuleTemplateUpdateAction(argparse._AppendAction):
# --ip-rule value="" action=""
def __call__(self, parser, namespace, values, option_string=None):
ipRuleValue = None
ipRuleAction = None
for item in values:
try:
key, value = item.split('=', 1)
if key == 'value':
ipRuleValue = value
elif key == 'action':
ipRuleAction = value
else:
raise InvalidArgumentValueError('usage error: {} KEY=VALUE [KEY=VALUE ...]'.format(option_string))
except ValueError:
raise InvalidArgumentValueError('usage error: {} KEY=VALUE [KEY=VALUE ...]'.format(option_string))
if ipRuleValue is None or ipRuleAction is None:
raise InvalidArgumentValueError('usage error: {} KEY=VALUE [KEY=VALUE ...]'.format(option_string))
value = IPRule(value=ipRuleValue, action=ipRuleAction)
super().__call__(parser, namespace, value, option_string)
50 changes: 50 additions & 0 deletions src/azure-cli/azure/cli/command_modules/signalr/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
short-summary: Manage network rules.
"""

helps['signalr network-rule ip-rule'] = """
type: group
short-summary: Manage SignalR Service IP rules.
"""

helps['signalr upstream'] = """
type: group
short-summary: Manage upstream settings.
Expand Down Expand Up @@ -228,6 +233,24 @@
az signalr network-rule update --public-network --connection-name MyPrivateEndpointConnection1 MyPrivateEndpointConnection2 -n MySignalR -g MyResourceGroup --deny ClientConnection
"""

helps['signalr network-rule ip-rule add'] = """
type: command
short-summary: Add IP rule to SignalR Service.
examples:
- name: Add IP rule
text: >
az signalr network-rule ip-rule add -n MySignalR -g MyResourceGroup --ip-rule value="10.0.0.24" action="Allow" --ip-rule value="192.168.0.0/24" action="Deny"
"""

helps['signalr network-rule ip-rule remove'] = """
type: command
short-summary: Remove IP rule from SignalR Service.
examples:
- name: Remove IP rule
text: >
az signalr network-rule ip-rule remove -n MySignalR -g MyResourceGroup --ip-rule value="10.0.0.24" action="Allow" --ip-rule value="192.168.0.0/24" action="Deny"
"""

helps['signalr identity assign'] = """
type: command
short-summary: Assign managed identity for SignalR Service.
Expand Down Expand Up @@ -300,6 +323,33 @@
short-summary: Show the detail of a custom certificate of SignalR Service.
"""

helps['signalr replica start'] = """
type: command
short-summary: Start a replica of SignalR Service.
examples:
- name: Start a replica
text: >
az signalr replica start --replica-name MyReplica --signalr-name MySignalR -g MyResourceGroup
"""

helps['signalr replica stop'] = """
type: command
short-summary: Stop a replica of SignalR Service.
examples:
- name: Stop a replica
text: >
az signalr replica stop --replica-name MyReplica --signalr-name MySignalR -g MyResourceGroup
"""

helps['signalr replica restart'] = """
type: command
short-summary: Restart a replica of SignalR Service.
examples:
- name: Restart a replica
text: >
az signalr replica restart --replica-name MyReplica --signalr-name MySignalR -g MyResourceGroup
"""

helps['signalr replica show'] = """
type: command
short-summary: Show the details of a replica
Expand Down
68 changes: 49 additions & 19 deletions src/azure-cli/azure/cli/command_modules/signalr/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@
get_three_state_flag
)
from ._actions import (
UpstreamTemplateAddAction
UpstreamTemplateAddAction,
IPRuleTemplateUpdateAction
)
from ._constants import (
SIGNALR_RESOURCE_TYPE,
SIGNALR_KEY_TYPE,
SIGNALR_SERVICE_MODE_TYPE
)

from ._validator import (
validate_ip_rule
)

logger = get_logger(__name__)

Expand All @@ -51,20 +54,27 @@ def load_arguments(self, _):
with self.argument_context('signalr create') as c:
c.argument('sku', help='The sku name of the signalr service. Allowed values: Premium_P1, Standard_S1, Free_F1')
c.argument('unit_count', help='The number of signalr service unit count', type=int)
c.argument('service_mode', help='The service mode which signalr service will be working on', choices=SIGNALR_SERVICE_MODE_TYPE)
c.argument('enable_message_logs', help='The switch for messaging logs which signalr service will generate or not', arg_type=get_three_state_flag())
c.argument('allowed_origins', options_list=['--allowed-origins', '-a'], nargs='*', help='space separated origins that should be allowed to make cross-origin calls (for example: http://example.com:12345). To allow all, use "*"')
c.argument('service_mode', help='The service mode which signalr service will be working on',
choices=SIGNALR_SERVICE_MODE_TYPE)
c.argument('enable_message_logs', help='The switch for messaging logs which signalr service will generate or not',
arg_type=get_three_state_flag())
c.argument('allowed_origins', options_list=['--allowed-origins', '-a'], nargs='*',
help='space separated origins that should be allowed to make cross-origin calls (for example: http://example.com:12345). To allow all, use "*"')

with self.argument_context('signalr update') as c:
c.argument('sku', help='The sku name of the signalr service. E.g. Standard_S1')
c.argument('unit_count', help='The number of signalr service unit count', type=int)
c.argument('service_mode', help='The service mode which signalr service will be working on', choices=SIGNALR_SERVICE_MODE_TYPE)
c.argument('enable_message_logs', help='The switch for messaging logs which signalr service will generate or not', arg_type=get_three_state_flag())
c.argument('allowed_origins', options_list=['--allowed-origins', '-a'], nargs='*', help='space separated origins that should be allowed to make cross-origin calls (for example: http://example.com:12345). To allow all, use "*"')
c.argument('service_mode', help='The service mode which signalr service will be working on',
choices=SIGNALR_SERVICE_MODE_TYPE)
c.argument('enable_message_logs', help='The switch for messaging logs which signalr service will generate or not',
arg_type=get_three_state_flag())
c.argument('allowed_origins', options_list=['--allowed-origins', '-a'], nargs='*',
help='space separated origins that should be allowed to make cross-origin calls (for example: http://example.com:12345). To allow all, use "*"')

for scope in ['signalr create', 'signalr update']:
with self.argument_context(scope, arg_group='Network Rule') as c:
c.argument('default_action', arg_type=get_enum_type(['Allow', 'Deny']), help='Default action to apply when no rule matches.', required=False)
c.argument('default_action', arg_type=get_enum_type(
['Allow', 'Deny']), help='Default action to apply when no rule matches.', required=False)

with self.argument_context('signalr key renew') as c:
c.argument('key_type', help='The name of access key to regenerate', choices=SIGNALR_KEY_TYPE)
Expand All @@ -73,37 +83,51 @@ def load_arguments(self, _):
c.argument('signalr_name', id_part=None)

with self.argument_context('signalr cors add') as c:
c.argument('allowed_origins', options_list=['--allowed-origins', '-a'], nargs='*', help='space separated origins that should be allowed to make cross-origin calls (for example: http://example.com:12345). To allow all, use "*"')
c.argument('allowed_origins', options_list=['--allowed-origins', '-a'], nargs='*',
help='space separated origins that should be allowed to make cross-origin calls (for example: http://example.com:12345). To allow all, use "*"')

with self.argument_context('signalr cors remove') as c:
c.argument('allowed_origins', options_list=['--allowed-origins', '-a'], nargs='*', help='space separated origins that should be allowed to make cross-origin calls (for example: http://example.com:12345). To allow all, use "*"')
c.argument('allowed_origins', options_list=['--allowed-origins', '-a'], nargs='*',
help='space separated origins that should be allowed to make cross-origin calls (for example: http://example.com:12345). To allow all, use "*"')

with self.argument_context('signalr cors update') as c:
c.argument('allowed_origins', options_list=['--allowed-origins', '-a'], nargs='*', help='space separated origins that should be allowed to make cross-origin calls (for example: http://example.com:12345). To allow all, use "*"')
c.argument('allowed_origins', options_list=['--allowed-origins', '-a'], nargs='*',
help='space separated origins that should be allowed to make cross-origin calls (for example: http://example.com:12345). To allow all, use "*"')

with self.argument_context('signalr cors list') as c:
c.argument('signalr_name', id_part=None)

# Network Rule
with self.argument_context('signalr network-rule update') as c:
c.argument('connection_name', nargs='*', help='Space-separeted list of private endpoint connection name.', required=False, arg_group='Private Endpoint Connection')
c.argument('public_network', arg_type=get_three_state_flag(), help='Set rules for public network.', required=False, arg_group='Public Network')
c.argument('allow', nargs='*', help='The allowed virtual network rule. Space-separeted list of scope to assign. Allowed values: ClientConnection, ServerConnection, RESTAPI', type=SignalRRequestType, required=False)
c.argument('deny', nargs='*', help='The denied virtual network rule. Space-separeted list of scope to assign. Allowed values: ClientConnection, ServerConnection, RESTAPI', type=SignalRRequestType, required=False)
c.argument('connection_name', nargs='*', help='Space-separeted list of private endpoint connection name.',
required=False, arg_group='Private Endpoint Connection')
c.argument('public_network', arg_type=get_three_state_flag(),
help='Set rules for public network.', required=False, arg_group='Public Network')
c.argument('allow', nargs='*', help='The allowed virtual network rule. Space-separeted list of scope to assign. Allowed values: ClientConnection, ServerConnection, RESTAPI',
type=SignalRRequestType, required=False)
c.argument('deny', nargs='*', help='The denied virtual network rule. Space-separeted list of scope to assign. Allowed values: ClientConnection, ServerConnection, RESTAPI',
type=SignalRRequestType, required=False)

for scope in ['signalr network-rule ip-rule add', 'signalr network-rule ip-rule remove']:
with self.argument_context(scope, validator=validate_ip_rule) as c:
c.argument('ip_rule', action=IPRuleTemplateUpdateAction, nargs='*',
help='The IP rule for the hub.', required=True)

with self.argument_context('signalr network-rule list') as c:
c.argument('signalr_name', id_part=None)

# Upstream Settings
with self.argument_context('signalr upstream update') as c:
c.argument('template', action=UpstreamTemplateAddAction, nargs='+', help='Template item for upstream settings. Use key=value pattern to set properties. Supported keys are "url-template", "hub-pattern", "event-pattern", "category-pattern".')
c.argument('template', action=UpstreamTemplateAddAction, nargs='+',
help='Template item for upstream settings. Use key=value pattern to set properties. Supported keys are "url-template", "hub-pattern", "event-pattern", "category-pattern".')

with self.argument_context('signalr upstream list') as c:
c.argument('signalr_name', id_part=None)

# Managed Identity
with self.argument_context('signalr identity assign') as c:
c.argument('identity', help="Assigns managed identities to the service. Use '[system]' to refer to the system-assigned identity or a resource ID to refer to a user-assigned identity. You can only assign either on of them.")
c.argument(
'identity', help="Assigns managed identities to the service. Use '[system]' to refer to the system-assigned identity or a resource ID to refer to a user-assigned identity. You can only assign either on of them.")

# Custom Domain
for scope in ['signalr custom-domain update',
Expand Down Expand Up @@ -143,7 +167,10 @@ def load_arguments(self, _):
for scope in ['signalr replica create',
'signalr replica list',
'signalr replica delete',
'signalr replica show']:
'signalr replica show',
'signalr replica start',
'signalr replica stop',
'signalr replica restart']:
with self.argument_context(scope) as c:
c.argument('sku', help='The sku name of the replica. Currently allowed values: Premium_P1')
c.argument('unit_count', help='The number of signalr service unit count', type=int)
Expand All @@ -155,6 +182,9 @@ def load_arguments(self, _):
c.argument('signalr_name', signalr_name_type, id_part=None)

for scope in ['signalr replica show',
'signalr replica start',
'signalr replica stop',
'signalr replica restart',
'signalr replica delete']:
with self.argument_context(scope) as c:
c.argument('signalr_name', signalr_name_type)
11 changes: 11 additions & 0 deletions src/azure-cli/azure/cli/command_modules/signalr/_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
# pylint: disable=line-too-long
from azure.cli.core.azclierror import RequiredArgumentMissingError


def validate_ip_rule(namespace):
if not namespace.ip_rule:
raise RequiredArgumentMissingError('IP rule should be set.')
13 changes: 11 additions & 2 deletions src/azure-cli/azure/cli/command_modules/signalr/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ def load_command_table(self, _):
g.command('list', 'list_network_rules')
g.command('update', 'update_network_rules')

with self.command_group('signalr network-rule ip-rule', signalr_network_utils) as g:
g.command('add', 'add_ip_rule')
g.command('remove', 'remove_ip_rule')

with self.command_group('signalr upstream', signalr_upstream_utils) as g:
g.command('list', 'signalr_upstream_list')
g.command('update', 'signalr_upstream_update')
Expand All @@ -101,18 +105,23 @@ def load_command_table(self, _):
g.show_command('show', 'custom_domain_show', exception_handler=empty_on_404)
g.command('create', 'custom_domain_create')
g.command('delete', 'custom_domain_delete')
g.generic_update_command('update', getter_name='get_custom_domain', setter_name='set_custom_domain', custom_func_name='update', custom_func_type=signalr_custom_domain_utils)
g.generic_update_command('update', getter_name='get_custom_domain', setter_name='set_custom_domain',
custom_func_name='update', custom_func_type=signalr_custom_domain_utils)
g.command('list', 'custom_domain_list')

with self.command_group('signalr custom-certificate', signalr_custom_certificate_utils) as g:
g.show_command('show', 'custom_certificate_show', exception_handler=empty_on_404)
g.command('create', 'custom_certificate_create')
g.command('delete', 'custom_certificate_delete')
g.generic_update_command('update', getter_name='get_custom_certificate', setter_name='set_custom_certificate', custom_func_name='update', custom_func_type=signalr_custom_certificate_utils)
g.generic_update_command('update', getter_name='get_custom_certificate', setter_name='set_custom_certificate',
custom_func_name='update', custom_func_type=signalr_custom_certificate_utils)
g.command('list', 'custom_certificate_list')

with self.command_group('signalr replica', signalr_replica_utils) as g:
g.command('create', 'signalr_replica_create')
g.command('list', 'signalr_replica_list')
g.show_command('show', 'signalr_replica_show', exception_handler=empty_on_404)
g.command('start', 'signalr_replica_start', exception_handler=empty_on_404)
g.command('stop', 'signalr_replica_stop', exception_handler=empty_on_404)
g.command('restart', 'signalr_replica_restart', exception_handler=empty_on_404)
g.show_command('delete', 'signalr_replica_delete')
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,13 @@ signalr custom-domain update:
certificate_resource_id:
rule_exclusions:
- option_length_too_long
signalr replica start:
rule_exclusions:
- missing_command_test_coverage
signalr replica stop:
rule_exclusions:
- missing_command_test_coverage
signalr replica restart:
rule_exclusions:
- missing_command_test_coverage
...
14 changes: 14 additions & 0 deletions src/azure-cli/azure/cli/command_modules/signalr/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,17 @@ def update_network_rules(client, signalr_name, resource_group_name, public_netwo
def list_network_rules(client, signalr_name, resource_group_name):
resource = client.get(resource_group_name, signalr_name)
return resource.network_ac_ls


def add_ip_rule(client, signalr_name, resource_group_name, ip_rule):
resource = client.get(resource_group_name, signalr_name)
network_acl = resource.network_ac_ls
network_acl.ip_rules.extend(ip_rule)
return client.begin_update(resource_group_name, signalr_name, SignalRResource(location=resource.location, network_ac_ls=network_acl))


def remove_ip_rule(client, signalr_name, resource_group_name, ip_rule):
resource = client.get(resource_group_name, signalr_name)
network_acl = resource.network_ac_ls
network_acl.ip_rules = [rule for rule in network_acl.ip_rules if rule not in ip_rule]
return client.begin_update(resource_group_name, signalr_name, SignalRResource(location=resource.location, network_ac_ls=network_acl))
14 changes: 14 additions & 0 deletions src/azure-cli/azure/cli/command_modules/signalr/replica.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ def signalr_replica_show(client: SignalRReplicasOperations, signalr_name, replic
return client.get(resource_group_name=resource_group_name, resource_name=signalr_name, replica_name=replica_name)


def signalr_replica_start(client: SignalRReplicasOperations, signalr_name, replica_name, resource_group_name):
parameter = Replica(location=None, resource_stopped=False)
return client.begin_update(resource_group_name=resource_group_name, resource_name=signalr_name, replica_name=replica_name, parameters=parameter)


def signalr_replica_stop(client: SignalRReplicasOperations, signalr_name, replica_name, resource_group_name):
parameter = Replica(location=None, resource_stopped=True)
return client.begin_update(resource_group_name=resource_group_name, resource_name=signalr_name, replica_name=replica_name, parameters=parameter)


def signalr_replica_restart(client: SignalRReplicasOperations, signalr_name, replica_name, resource_group_name):
return client.begin_restart(resource_group_name=resource_group_name, resource_name=signalr_name, replica_name=replica_name)


def signalr_replica_delete(client: SignalRReplicasOperations, signalr_name, replica_name, resource_group_name):
return client.delete(resource_group_name=resource_group_name, resource_name=signalr_name, replica_name=replica_name)

Expand Down
Loading

0 comments on commit 689ffb4

Please sign in to comment.