Skip to content

Commit

Permalink
[Spring] Add new params for ACS settings (#7187)
Browse files Browse the repository at this point in the history
  • Loading branch information
moarychan authored Feb 5, 2024
1 parent ecf778c commit b68cff4
Show file tree
Hide file tree
Showing 8 changed files with 2,144 additions and 850 deletions.
3 changes: 3 additions & 0 deletions src/spring/HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
Release History
===============
1.19.3
---
* Add arguments `--refresh-interval` in `spring application-configuration-service create` and `spring application-configuration-service update`.

1.19.2
---
Expand Down
7 changes: 6 additions & 1 deletion src/spring/azext_spring/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
validate_buildpack_binding_exist, validate_buildpack_binding_not_exist,
validate_buildpack_binding_properties, validate_buildpack_binding_secrets,
validate_build_env, validate_target_module, validate_runtime_version,
validate_acs_ssh_or_warn, validate_apm_properties, validate_apm_secrets,
validate_acs_ssh_or_warn, validate_refresh_interval,
validate_apm_properties, validate_apm_secrets,
validate_apm_not_exist, validate_apm_update, validate_apm_reference,
validate_apm_reference_and_enterprise_tier, validate_cert_reference,
validate_build_cert_reference, validate_acs_create, not_support_enterprise,
Expand Down Expand Up @@ -864,6 +865,10 @@ def prepare_logs_argument(c):
for scope in ['create', 'update']:
with self.argument_context('spring application-configuration-service {}'.format(scope)) as c:
c.argument('generation', arg_type=get_enum_type(ConfigurationServiceGeneration), help='Generation of Application Configuration Service.')
c.argument('refresh_interval', type=int,
validator=validate_refresh_interval,
help='Specify the interval (in seconds) for refreshing the repository. '
'Use 0 to turn off automatic refresh. An interval of at least 60 seconds is recommended.')

for scope in ['add', 'update']:
with self.argument_context('spring application-configuration-service git repo {}'.format(scope)) as c:
Expand Down
9 changes: 9 additions & 0 deletions src/spring/azext_spring/_validators_enterprise.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,15 @@ def validate_acs_create(namespace):
raise ArgumentUsageError("--application-configuration-service-generation can only be set when enable application configuration service.")


def validate_refresh_interval(namespace):
if namespace.refresh_interval:
if not isinstance(namespace.refresh_interval, int):
raise InvalidArgumentValueError("--refresh-interval should be a number.")

if namespace.refresh_interval < 0:
raise ArgumentUsageError("--refresh-interval must be greater than or equal to 0.")


def validate_gateway_update(cmd, namespace):
_validate_gateway_response_cache(namespace)
_validate_sso(namespace)
Expand Down
9 changes: 7 additions & 2 deletions src/spring/azext_spring/application_configuration_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,20 @@


def application_configuration_service_create(cmd, client, service, resource_group,
generation=None):
generation=None, refresh_interval=None):
if generation is None:
generation = ConfigurationServiceGeneration.GEN1

properties = models.ConfigurationServiceProperties(generation=generation)
if refresh_interval is not None:
properties.settings = models.ConfigurationServiceSettings(refresh_interval_in_seconds=refresh_interval)
acs_resource = models.ConfigurationServiceResource(properties=properties)
logger.warning("Create with generation {}".format(acs_resource.properties.generation))
return client.configuration_services.begin_create_or_update(resource_group, service, DEFAULT_NAME, acs_resource)


def application_configuration_service_update(cmd, client, service, resource_group,
generation=None):
generation=None, refresh_interval=None):
acs_resource = client.configuration_services.get(resource_group, service, DEFAULT_NAME)
if generation is not None:
acs_resource.properties.generation = generation
Expand All @@ -44,6 +46,9 @@ def application_configuration_service_update(cmd, client, service, resource_grou
acs_resource.properties.generation = ConfigurationServiceGeneration.GEN1
logger.warning("Default generation will be Gen1")
logger.warning(acs_resource.properties.generation)
if refresh_interval is not None:
acs_resource.properties.settings = acs_resource.properties.settings or models.ConfigurationServiceSettings()
acs_resource.properties.settings.refresh_interval_in_seconds = refresh_interval
return client.configuration_services.begin_create_or_update(resource_group, service, DEFAULT_NAME, acs_resource)


Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,94 +2,89 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.testsdk import (ScenarioTest)
from azure.cli.testsdk.reverse_dependency import (
get_dummy_cli,
)
from .custom_preparers import (SpringPreparer, SpringResourceGroupPreparer, SpringAppNamePreparer,
SpringSubResourceWrapper)
from .custom_dev_setting_constant import SpringTestEnvironmentEnum


# pylint: disable=line-too-long
# pylint: disable=too-many-lines

class TearDown(SpringSubResourceWrapper):
def __init__(self,
resource_group_parameter_name='resource_group',
spring_parameter_name='spring'):
super(TearDown, self).__init__()
self.cli_ctx = get_dummy_cli()
self.resource_group_parameter_name = resource_group_parameter_name
self.spring_parameter_name = spring_parameter_name

def create_resource(self, *_, **kwargs):
self.resource_group = self._get_resource_group(**kwargs)
self.spring = self._get_spring(**kwargs)

def remove_resource(self, *_, **__):
self.live_only_execute(self.cli_ctx,
'spring application-configuration-service delete -g {} -s {} --yes'.format(
self.resource_group, self.spring))
self.live_only_execute(self.cli_ctx, 'spring application-configuration-service create -g {} -s {}'.format(
self.resource_group, self.spring))


class ApplicationConfigurationServiceTest(ScenarioTest):

@SpringResourceGroupPreparer(
dev_setting_name=SpringTestEnvironmentEnum.ENTERPRISE_WITH_TANZU['resource_group_name'])
@SpringPreparer(**SpringTestEnvironmentEnum.ENTERPRISE_WITH_TANZU['spring'])
@SpringAppNamePreparer()
@TearDown()
def test_application_configuration_service(self, resource_group, spring, app):
self.kwargs.update({
'serviceName': spring,
'rg': resource_group,
'repo': "repo1",
"label": "main",
"patterns": "api-gateway,customers-service",
"uri": "https://github.com/spring-petclinic/spring-petclinic-microservices-config",
"app": app
})

self.cmd('spring app create -g {rg} -s {serviceName} -n {app}')

self.cmd('spring application-configuration-service show -g {rg} -s {serviceName}', checks=[
self.check('properties.provisioningState', "Succeeded")
])

self.cmd('spring application-configuration-service git repo add -g {rg} -s {serviceName} '
'-n {repo} --label {label} --patterns {patterns} --uri {uri}',
checks=[self.check('properties.provisioningState', "Succeeded")])

self.cmd('spring application-configuration-service git repo update -g {rg} -s {serviceName} '
'-n {repo} --label {label}',
checks=[self.check('properties.provisioningState', "Succeeded")])

result = self.cmd(
'spring application-configuration-service git repo list -g {rg} -s {serviceName}').get_output_in_json()
self.assertTrue(len(result) > 0)

self.cmd('spring application-configuration-service git repo remove --name {repo} -g {rg} -s {serviceName}')
result = self.cmd(
'spring application-configuration-service git repo list -g {rg} -s {serviceName}').get_output_in_json()
self.assertTrue(len(result) == 0)

self.cmd('spring application-configuration-service bind --app {app} -g {rg} -s {serviceName}', checks=[
self.check('properties.addonConfigs.applicationConfigurationService.resourceId',
"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.AppPlatform/Spring/{}/configurationServices/default".format(
self.get_subscription_id(), resource_group, spring))
])

self.cmd('spring app show -n {app} -g {rg} -s {serviceName}')

self.cmd('spring application-configuration-service unbind --app {app} -g {rg} -s {serviceName}')

self.cmd('spring application-configuration-service clear -g {rg} -s {serviceName}', checks=[
self.check('properties.provisioningState', "Succeeded")
])

self.cmd('spring application-configuration-service update -g {rg} -s {serviceName} --generation Gen2',
checks=[self.check('properties.provisioningState', "Succeeded")])
from ...application_configuration_service import (application_configuration_service_create,
application_configuration_service_update)

import unittest
from argparse import Namespace
from azure.cli.core.azclierror import InvalidArgumentValueError, ArgumentUsageError
from ..._validators_enterprise import validate_refresh_interval
try:
import unittest.mock as mock
except ImportError:
from unittest import mock

from azure.cli.core.mock import DummyCli
from azure.cli.core import AzCommandsLoader
from azure.cli.core.commands import AzCliCommand

from knack.log import get_logger

logger = get_logger(__name__)
free_mock_client = mock.MagicMock()


def _get_test_cmd():
cli_ctx = DummyCli()
cli_ctx.data['subscription_id'] = '00000000-0000-0000-0000-000000000000'
loader = AzCommandsLoader(cli_ctx, resource_type='Microsoft.AppPlatform')
cmd = AzCliCommand(loader, 'test', None)
cmd.command_kwargs = {'resource_type': 'Microsoft.AppPlatform'}
cmd.cli_ctx = cli_ctx
return cmd


def _cf_resource_group(cli_ctx, subscription_id=None):
client = mock.MagicMock()
rg = mock.MagicMock()
rg.location = 'east us'
client.resource_groups.get.return_value = rg
return client


def _get_basic_mock_client(*_):
return mock.MagicMock()


class BasicTest(unittest.TestCase):
def __init__(self, methodName: str = ...):
super().__init__(methodName=methodName)
self.created_resource = None

def setUp(self):
resp = super().setUp()
free_mock_client.reset_mock()
return resp

@mock.patch('azext_spring._utils.cf_resource_groups', _cf_resource_group)
def _execute(self, resource_group, generation, refresh_interval, **kwargs):
client = kwargs.pop('client', None) or _get_basic_mock_client()
application_configuration_service_create(_get_test_cmd(), client, 'myasa',
resource_group, generation, refresh_interval)
call_args = client.configuration_services.begin_create_or_update.call_args_list
self.assertEqual(1, len(call_args))
self.assertEqual(4, len(call_args[0][0]))
self.assertEqual((resource_group, generation),
(call_args[0][0][0], call_args[0][0][3].properties.generation))
self.created_resource = call_args[0][0][3]


class TestApplicationConfigurationService(BasicTest):
def test_acs_create(self):
self._execute('rg', 'Gen1', 120)
resource = self.created_resource
self.assertIsNotNone(resource.properties)
self.assertEqual(120, resource.properties.settings.refresh_interval_in_seconds)


class TestApplicationConfigurationServiceValidator(unittest.TestCase):
def test_validate_refresh_interval_parameter(self):
ns = Namespace(refresh_interval="a")
with self.assertRaises(InvalidArgumentValueError) as context:
validate_refresh_interval(ns)
self.assertEqual("--refresh-interval should be a number.", str(context.exception))

ns = Namespace(refresh_interval=-1)
with self.assertRaises(ArgumentUsageError) as context:
validate_refresh_interval(ns)
self.assertEqual("--refresh-interval must be greater than or equal to 0.", str(context.exception))
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.testsdk import (ScenarioTest)
from azure.cli.testsdk.reverse_dependency import (
get_dummy_cli,
)
from .custom_preparers import (SpringPreparer, SpringResourceGroupPreparer, SpringAppNamePreparer,
SpringSubResourceWrapper)
from .custom_dev_setting_constant import SpringTestEnvironmentEnum


# pylint: disable=line-too-long
# pylint: disable=too-many-lines

class TearDown(SpringSubResourceWrapper):
def __init__(self,
resource_group_parameter_name='resource_group',
spring_parameter_name='spring'):
super(TearDown, self).__init__()
self.cli_ctx = get_dummy_cli()
self.resource_group_parameter_name = resource_group_parameter_name
self.spring_parameter_name = spring_parameter_name

def create_resource(self, *_, **kwargs):
self.resource_group = self._get_resource_group(**kwargs)
self.spring = self._get_spring(**kwargs)

def remove_resource(self, *_, **__):
self.live_only_execute(self.cli_ctx,
'spring application-configuration-service delete -g {} -s {} --yes'.format(
self.resource_group, self.spring))
self.live_only_execute(self.cli_ctx, 'spring application-configuration-service create -g {} -s {}'.format(
self.resource_group, self.spring))


class ApplicationConfigurationServiceTest(ScenarioTest):

@SpringResourceGroupPreparer(
dev_setting_name=SpringTestEnvironmentEnum.ENTERPRISE_WITH_TANZU['resource_group_name'])
@SpringPreparer(**SpringTestEnvironmentEnum.ENTERPRISE_WITH_TANZU['spring'])
@SpringAppNamePreparer()
@TearDown()
def test_application_configuration_service(self, resource_group, spring, app):
self.kwargs.update({
'serviceName': spring,
'rg': resource_group,
'repo': "repo1",
"label": "main",
"patterns": "api-gateway,customers-service",
"uri": "https://github.com/spring-petclinic/spring-petclinic-microservices-config",
"app": app
})

self.cmd('spring app create -g {rg} -s {serviceName} -n {app}')

self.cmd('spring application-configuration-service show -g {rg} -s {serviceName}', checks=[
self.check('properties.provisioningState', "Succeeded")
])

self.cmd('spring application-configuration-service git repo add -g {rg} -s {serviceName} '
'-n {repo} --label {label} --patterns {patterns} --uri {uri}',
checks=[self.check('properties.provisioningState', "Succeeded")])

self.cmd('spring application-configuration-service git repo update -g {rg} -s {serviceName} '
'-n {repo} --label {label}',
checks=[self.check('properties.provisioningState', "Succeeded")])

result = self.cmd(
'spring application-configuration-service git repo list -g {rg} -s {serviceName}').get_output_in_json()
self.assertTrue(len(result) > 0)

self.cmd('spring application-configuration-service git repo remove --name {repo} -g {rg} -s {serviceName}')
result = self.cmd(
'spring application-configuration-service git repo list -g {rg} -s {serviceName}').get_output_in_json()
self.assertTrue(len(result) == 0)

self.cmd('spring application-configuration-service bind --app {app} -g {rg} -s {serviceName}', checks=[
self.check('properties.addonConfigs.applicationConfigurationService.resourceId',
"/subscriptions/{}/resourceGroups/{}/providers/Microsoft.AppPlatform/Spring/{}/configurationServices/default".format(
self.get_subscription_id(), resource_group, spring))
])

self.cmd('spring app show -n {app} -g {rg} -s {serviceName}')

self.cmd('spring application-configuration-service unbind --app {app} -g {rg} -s {serviceName}')

self.cmd('spring application-configuration-service clear -g {rg} -s {serviceName}', checks=[
self.check('properties.provisioningState', "Succeeded")
])

self.cmd('spring application-configuration-service update -g {rg} -s {serviceName} '
'--generation Gen2 --refresh-interval 10',
checks=[
self.check('properties.provisioningState', "Succeeded"),
self.check('properties.generation', "Gen2"),
self.check('properties.settings.refreshIntervalInSeconds', 10)])

self.cmd('spring application-configuration-service delete -g {rg} -s {serviceName} --yes')
self.cmd('spring application-configuration-service create -g {rg} -s {serviceName} '
'--generation Gen1 --refresh-interval 20',
checks=[
self.check('properties.provisioningState', "Succeeded"),
self.check('properties.generation', "Gen1"),
self.check('properties.settings.refreshIntervalInSeconds', 20)])
2 changes: 1 addition & 1 deletion src/spring/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

# TODO: Confirm this is the right version number you want and it matches your
# HISTORY.rst entry.
VERSION = '1.19.2'
VERSION = '1.19.3'

# The full list of classifiers is available at
# https://pypi.python.org/pypi?%3Aaction=list_classifiers
Expand Down

0 comments on commit b68cff4

Please sign in to comment.