Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
27 changes: 27 additions & 0 deletions src/spring-cloud/azext_spring_cloud/_app_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from msrestazure.azure_exceptions import CloudError
from azure.core.exceptions import (ResourceNotFoundError)
from ._client_factory import cf_spring_cloud
from ._resource_quantity import (validate_cpu as validate_cpu_value, validate_memory as validate_memory_value)


# pylint: disable=line-too-long,raise-missing-from
Expand Down Expand Up @@ -76,3 +77,29 @@ def _get_active_deployment(client, resource_group, service, name):
return next(iter(x for x in deployments if x.properties.active), None)
except ResourceNotFoundError:
raise InvalidArgumentValueError('Deployments not found under App {}'.format(name))


def validate_deloy_path(namespace):
arguments = [namespace.artifact_path, namespace.source_path, namespace.container_image]
if all(not x for x in arguments):
raise InvalidArgumentValueError('One of --artifact-path, --source-path, --container-image must be provided.')
_deploy_path_mutual_exclusive(arguments)


def validate_deloyment_create_path(namespace):
arguments = [namespace.artifact_path, namespace.source_path, namespace.container_image]
_deploy_path_mutual_exclusive(arguments)


def _deploy_path_mutual_exclusive(args):
valued_args = [x for x in args if x]
if len(valued_args) > 1:
raise InvalidArgumentValueError('At most one of --artifact-path, --source-path, --container-image must be provided.')


def validate_cpu(namespace):
namespace.cpu = validate_cpu_value(namespace.cpu)


def validate_memory(namespace):
namespace.memory = validate_memory_value(namespace.memory)
46 changes: 27 additions & 19 deletions src/spring-cloud/azext_spring_cloud/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
validate_app_insights_parameters, validate_instance_count, validate_java_agent_parameters,
validate_jar)
from ._app_validator import (fulfill_deployment_param, active_deployment_exist, active_deployment_exist_under_app,
ensure_not_active_deployment)
ensure_not_active_deployment, validate_deloy_path, validate_deloyment_create_path,
validate_cpu, validate_memory)
from ._utils import ApiType

from .vendored_sdks.appplatform.v2020_07_01.models import RuntimeVersion, TestKeyType
Expand All @@ -27,6 +28,12 @@
service_name_type = CLIArgumentType(options_list=['--service', '-s'], help='Name of Azure Spring Cloud, you can configure the default service using az configure --defaults spring-cloud=<name>.', configured_default='spring-cloud')
app_name_type = CLIArgumentType(help='App name, you can configure the default app using az configure --defaults spring-cloud-app=<name>.', validator=validate_app_name, configured_default='spring-cloud-app')
sku_type = CLIArgumentType(arg_type=get_enum_type(['Basic', 'Standard', 'Enterprise']), validator=validate_sku, help='Name of SKU. Enterprise is still in Preview.')
source_path_type = CLIArgumentType(nargs='?', const='.',
help="Deploy the specified source folder. The folder will be packed into tar, uploaded, and built using kpack. Default to the current folder if no value provided.",
arg_group='Source Code deploy')
# app cpu and memory
cpu_type = CLIArgumentType(type=str, help='CPU resource quantity. Should be 500m or number of CPU cores.', validator=validate_cpu)
memort_type = CLIArgumentType(type=str, help='Memory resource quantity. Should be 512Mi or #Gi, e.g., 1Gi, 3Gi.', validator=validate_memory)


# pylint: disable=too-many-statements
Expand Down Expand Up @@ -117,10 +124,8 @@ def load_arguments(self, _):
options_list=['--assign-endpoint', c.deprecate(target='--is-public', redirect='--assign-endpoint', hide=True)])
c.argument('assign_identity', arg_type=get_three_state_flag(),
help='If true, assign managed service identity.')
c.argument('cpu', type=str, default="1",
help='CPU resource quantity. Should be 500m or number of CPU cores.')
c.argument('memory', type=str, default="1Gi",
help='Memory resource quantity. Should be 512Mi or #Gi, e.g., 1Gi, 3Gi.')
c.argument('cpu', arg_type=cpu_type, default="1")
c.argument('memory', arg_type=memort_type, default="1Gi")
c.argument('instance_count', type=int,
default=1, help='Number of instance.', validator=validate_instance_count)
c.argument('persistent_storage', type=str,
Expand Down Expand Up @@ -202,8 +207,8 @@ def prepare_logs_argument(c):
c.argument('disable_probe', arg_type=get_three_state_flag(), help='If true, disable the liveness and readiness probe.')

with self.argument_context('spring-cloud app scale') as c:
c.argument('cpu', type=str, help='CPU resource quantity. Should be 500m or number of CPU cores.')
c.argument('memory', type=str, help='Memory resource quantity. Should be 512Mi or #Gi, e.g., 1Gi, 3Gi.')
c.argument('cpu', arg_type=cpu_type)
c.argument('memory', arg_type=memort_type)
c.argument('instance_count', type=int, help='Number of instance.', validator=validate_instance_count)

for scope in ['spring-cloud app deploy', 'spring-cloud app deployment create']:
Expand All @@ -213,37 +218,40 @@ def prepare_logs_argument(c):
c.deprecate(target='--jar-path', redirect='--artifact-path', hide=True),
c.deprecate(target='-p', redirect='--artifact-path', hide=True)],
help='Deploy the specified pre-built artifact (jar or netcore zip).', validator=validate_jar)
c.argument(
'source_path', nargs='?', const='.',
help="Deploy the specified source folder. The folder will be packed into tar, uploaded, and built using kpack. Default to the current folder if no value provided.")
c.argument(
'disable_validation', arg_type=get_three_state_flag(),
help='If true, disable jar validation.')
c.argument(
'main_entry', options_list=[
'--main-entry', '-m'], help="A string containing the path to the .NET executable relative to zip root.")
c.argument(
'target_module', help='Child module to be deployed, required for multiple jar packages built from source code.')
'target_module', help='Child module to be deployed, required for multiple jar packages built from source code.', arg_group='Source Code deploy')
c.argument(
'version', help='Deployment version, keep unchanged if not set.')
c.argument(
'container_image', help='The container image tag.')
'container_image', help='The container image tag.', arg_group='Custom Container')
c.argument(
'container_registry', default='docker.io', help='The registry of the container image.')
'container_registry', default='docker.io', help='The registry of the container image.', arg_group='Custom Container')
c.argument(
'registry_username', help='The username of the container registry.')
'registry_username', help='The username of the container registry.', arg_group='Custom Container')
c.argument(
'registry_password', help='The password of the container registry.')
'registry_password', help='The password of the container registry.', arg_group='Custom Container')
c.argument(
'container_command', help='The command of the container image.')
'container_command', help='The command of the container image.', arg_group='Custom Container')
c.argument(
'container_args', help='The arguments of the container image.')
'container_args', help='The arguments of the container image.', arg_group='Custom Container')

with self.argument_context('spring-cloud app deploy') as c:
c.argument('source_path', arg_type=source_path_type, validator=validate_deloy_path)

with self.argument_context('spring-cloud app deployment create') as c:
c.argument('source_path', arg_type=source_path_type, validator=validate_deloyment_create_path)

with self.argument_context('spring-cloud app deployment create') as c:
c.argument('skip_clone_settings', help='Create staging deployment will automatically copy settings from production deployment.',
action='store_true')
c.argument('cpu', type=str, help='CPU resource quantity. Should be 500m or number of CPU cores.')
c.argument('memory', type=str, help='Memory resource quantity. Should be 512Mi or #Gi, e.g., 1Gi, 3Gi.')
c.argument('cpu', arg_type=cpu_type)
c.argument('memory', arg_type=memort_type)
c.argument('instance_count', type=int, help='Number of instance.', validator=validate_instance_count)

with self.argument_context('spring-cloud app deployment') as c:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import unittest
import copy
from argparse import Namespace
from azure.cli.core.azclierror import InvalidArgumentValueError
from msrestazure.azure_exceptions import CloudError
from azure.core.exceptions import ResourceNotFoundError
from ..._app_validator import (fulfill_deployment_param, active_deployment_exist, active_deployment_exist_under_app)
from ..._app_validator import (fulfill_deployment_param, active_deployment_exist, active_deployment_exist_under_app,
validate_cpu, validate_memory, validate_deloyment_create_path, validate_deloy_path)


try:
Expand Down Expand Up @@ -39,6 +39,73 @@ def _get_deployment(resource_group, service, app, deployment, active):
return resource


class TestCpuAndMemoryValidator(unittest.TestCase):
def test_none_input(self):
ns = Namespace(cpu=None, memory=None)
validate_memory(ns)
validate_cpu(ns)
self.assertIsNone(ns.cpu)
self.assertIsNone(ns.memory)

def test_int_input(self):
ns = Namespace(cpu='1', memory='1')
validate_memory(ns)
validate_cpu(ns)
self.assertEqual('1', ns.cpu)
self.assertEqual('1Gi', ns.memory)

def test_str_input(self):
ns = Namespace(cpu='1', memory='1Gi')
validate_memory(ns)
validate_cpu(ns)
self.assertEqual('1', ns.cpu)
self.assertEqual('1Gi', ns.memory)

def test_invalid_memory(self):
ns = Namespace(memory='invalid')
with self.assertRaises(InvalidArgumentValueError) as context:
validate_memory(ns)
self.assertEqual('Memory quantity should be integer followed by unit (Mi/Gi)', str(context.exception))

def test_invalid_cpu(self):
ns = Namespace(cpu='invalid')
with self.assertRaises(InvalidArgumentValueError) as context:
validate_cpu(ns)
self.assertEqual('CPU quantity should be millis (500m) or integer (1, 2, ...)', str(context.exception))


class TestDeployPath(unittest.TestCase):
def test_no_deploy_path_provided_when_create(self):
ns = Namespace(source_path=None, artifact_path=None, container_image=None)
validate_deloyment_create_path(ns)

def test_no_deploy_path_when_deploy(self):
ns = Namespace(source_path=None, artifact_path=None, container_image=None)
with self.assertRaises(InvalidArgumentValueError):
validate_deloy_path(ns)

def test_more_than_one_path(self):
ns = Namespace(source_path='test', artifact_path='test', container_image=None)
with self.assertRaises(InvalidArgumentValueError):
validate_deloy_path(ns)
with self.assertRaises(InvalidArgumentValueError):
validate_deloyment_create_path(ns)

def test_more_than_one_path_1(self):
ns = Namespace(source_path='test', artifact_path='test', container_image='test')
with self.assertRaises(InvalidArgumentValueError):
validate_deloy_path(ns)
with self.assertRaises(InvalidArgumentValueError):
validate_deloyment_create_path(ns)

def test_more_than_one_path_2(self):
ns = Namespace(source_path='test', artifact_path=None, container_image='test')
with self.assertRaises(InvalidArgumentValueError):
validate_deloy_path(ns)
with self.assertRaises(InvalidArgumentValueError):
validate_deloyment_create_path(ns)


class TestActiveDeploymentExist(unittest.TestCase):
@mock.patch('azext_spring_cloud._app_validator.cf_spring_cloud', autospec=True)
def test_deployment_found(self, client_factory_mock):
Expand Down