Skip to content
5 changes: 4 additions & 1 deletion src/spring/azext_spring/_deployment_deployable_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from ._stream_utils import stream_logs
from ._buildservices_factory import BuildService
from threading import Timer
from ._utils import (_get_file_ext)

logger = get_logger(__name__)

Expand All @@ -38,11 +39,13 @@ def build_deployable_path(self, **_):
def stream_log(self, **_):
pass

def get_source_type(self, runtime_version=None, **_):
def get_source_type(self, runtime_version=None, artifact_path=None, **_):
if self.sku.name == 'E0':
return 'BuildResult'
if runtime_version and runtime_version.lower() == 'netcore_31':
return 'NetCoreZip'
if _get_file_ext(artifact_path).lower() == ".war":
return "War"
return 'Jar'


Expand Down
2 changes: 2 additions & 0 deletions src/spring/azext_spring/_deployment_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ def deployment_source_options_from_resource(original):
options['jvm_options'] = original.properties.source.jvm_options
if hasattr(original.properties.source, 'runtime_version'):
options['runtime_version'] = original.properties.source.runtime_version
if hasattr(original.properties.source, 'server_version'):
options['server_version'] = original.properties.source.server_version
return options


Expand Down
36 changes: 36 additions & 0 deletions src/spring/azext_spring/_deployment_source_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,40 @@ def fulfilled_options_from_original_source_info(self, deployment_resource,
}


class WarSource(BaseSource):
def validate_source(self, **kwargs):
invalid_input = {k: v for k, v in kwargs.items() if k in ['main_entry', 'target_module'] and v is not None}
if any(invalid_input):
invalid_input_str = convert_argument_to_parameter_list(invalid_input.keys())
runtime_version = kwargs.get('runtime_version') or kwargs.get('deployment_resource').properties.source.runtime_version
raise ArgumentUsageError('{} cannot be set when --runtime-version is {}.'
.format(invalid_input_str, runtime_version))

def format_source(self, deployable_path=None, runtime_version=None, server_version=None, version=None, jvm_options=None, **_):
if all(x is None for x in [deployable_path, runtime_version, server_version, version, jvm_options]):
return
return models.WarUploadedUserSourceInfo(
relative_path=deployable_path,
jvm_options=jvm_options,
runtime_version=runtime_version or 'Java_11',
server_version=server_version or 'Tomcat_9',
version=version
)

def fulfilled_options_from_original_source_info(self, deployment_resource,
jvm_options=None, runtime_version=None, server_version=None, **_):
if all(x is None for x in [jvm_options, runtime_version]):
return {}
original_source = deployment_resource.properties.source
return {
'jvm_options': jvm_options if jvm_options is not None else original_source.jvm_options,
Comment thread
yilims marked this conversation as resolved.
'runtime_version': runtime_version or original_source.runtime_version,
'server_version': server_version or original_source.server_version,
'version': original_source.version,
'deployable_path': original_source.relative_path
}


class NetCoreZipSource(BaseSource):
def validate_source(self, **kwargs):
invalid_input = {k: v for k, v in kwargs.items() if k in ['jvm_options'] and v is not None}
Expand Down Expand Up @@ -163,4 +197,6 @@ def source_selector(source_type=None, **_):
return NetCoreZipSource()
if source_type == 'BuildResult':
return BuildResult()
if source_type == 'War':
return WarSource()
return JarSource()
2 changes: 2 additions & 0 deletions src/spring/azext_spring/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@
text: az spring app deploy -n MyApp -s MyCluster -g MyResourceGroup --source-path
- name: Deploy a pre-built jar to an app with jvm options and environment variables.
text: az spring app deploy -n MyApp -s MyCluster -g MyResourceGroup --artifact-path app.jar --jvm-options="-XX:+UseG1GC -XX:+UseStringDeduplication" --env foo=bar
- name: Deploy a pre-built war to an app with server version, jvm options and environment variables (Standard and Basic Tiers Only).
text: az spring app deploy -n MyApp -s MyCluster -g MyResourceGroup --artifact-path app.war --server-version Tomcat_10 --jvm-options="-XX:+UseG1GC -XX:+UseStringDeduplication" --env foo=bar
- name: Deploy source code to a specific deployment of an app.
text: az spring app deploy -n MyApp -s MyCluster -g MyResourceGroup -d green-deployment --source-path
- name: Deploy a container image on Docker Hub to an app.
Expand Down
5 changes: 3 additions & 2 deletions src/spring/azext_spring/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
validate_ingress_timeout, validate_jar, validate_ingress_send_timeout,
validate_ingress_session_max_age, validate_config_server_ssh_or_warn,
validate_remote_debugging_port, validate_ingress_client_auth_certificates,
validate_managed_environment, validate_dataplane_public_endpoint)
validate_managed_environment, validate_dataplane_public_endpoint, validate_server_version)
from ._validators_enterprise import (only_support_enterprise, validate_builder_resource, validate_builder_create,
validate_source_path, validate_artifact_path, validate_build_create,
validate_build_update, validate_container_registry_create,
Expand Down Expand Up @@ -490,7 +490,7 @@ def prepare_logs_argument(c):
'artifact_path', options_list=['--artifact-path',
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)
help='Deploy the specified pre-built artifact (jar, war or netcore zip, war is in public preview).', validator=validate_jar)
c.argument(
'disable_validation', arg_type=get_three_state_flag(),
help='If true, disable jar validation.')
Expand Down Expand Up @@ -528,6 +528,7 @@ def prepare_logs_argument(c):
c.argument('build_certificates', nargs='*',
help='(Enterprise Tier Only) Space-separated certificate names, the certificates are used during build time.',
validator=validate_build_cert_reference)
c.argument('server_version', help='(Standard and Basic Tiers Only) Tomcat server version. This feature is in public preview.', validator=validate_server_version)

with self.argument_context('spring app deploy') as c:
c.argument('source_path', arg_type=source_path_type, validator=validate_deloy_path)
Expand Down
12 changes: 11 additions & 1 deletion src/spring/azext_spring/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,18 @@ def _get_upload_local_file(runtime_version, artifact_path=None, source_path=None
return file_type, file_path


def _get_file_ext(artifact_path):
return os.path.splitext(artifact_path)[-1].lower() if artifact_path else ""


def _get_file_type(runtime_version, artifact_path=None):
file_type = "Jar" if _is_java(runtime_version) else "Others"
file_type = "Others"
if _is_java(runtime_version):
file_ext = _get_file_ext(artifact_path)
if file_ext.lower() == ".jar":
file_type = "Jar"
elif file_ext.lower() == ".war":
file_type = "War"
if artifact_path is None:
file_type = "Source"
return file_type
Expand Down
5 changes: 5 additions & 0 deletions src/spring/azext_spring/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,3 +755,8 @@ def validate_managed_environment(namespace):
managed_environment = parse_resource_id(managed_environment_id)
if managed_environment['namespace'].lower() != 'microsoft.app' or managed_environment['type'].lower() != 'managedenvironments':
raise InvalidArgumentValueError('--managed-environment {0} is not a valid Container App Environment resource ID'.format(managed_environment_id))


def validate_server_version(cmd, namespace):
if namespace.server_version and is_enterprise_tier(cmd, namespace.resource_group, namespace.service):
raise ArgumentUsageError("'--server-version' only supports for Standard/Basic tier Spring instance.")
4 changes: 4 additions & 0 deletions src/spring/azext_spring/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ def app_deploy(cmd, client, resource_group, service, name,
source_path=None,
target_module=None,
runtime_version=None,
server_version=None,
jvm_options=None,
main_entry=None,
container_image=None,
Expand Down Expand Up @@ -362,6 +363,7 @@ def app_deploy(cmd, client, resource_group, service, name,
'apms': apms,
'build_certificates': build_certificates,
'runtime_version': runtime_version,
'server_version': server_version,
'jvm_options': jvm_options,
'main_entry': main_entry,
'version': version,
Expand Down Expand Up @@ -523,6 +525,7 @@ def deployment_create(cmd, client, resource_group, service, app, name,
source_path=None,
target_module=None,
runtime_version=None,
server_version=None,
jvm_options=None,
main_entry=None,
container_image=None,
Expand Down Expand Up @@ -586,6 +589,7 @@ def deployment_create(cmd, client, resource_group, service, app, name,
'apms': apms,
'build_certificates': build_certificates,
'runtime_version': runtime_version,
'server_version': server_version,
'jvm_options': jvm_options,
'main_entry': main_entry,
'version': version,
Expand Down
31 changes: 31 additions & 0 deletions src/spring/azext_spring/tests/latest/test_asa_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ def test_app_deploy_with_runtime_version(self, file_mock):
self.assertIsNone(resource.properties.source.version)
self.assertEqual('Java_8', resource.properties.source.runtime_version)

@mock.patch('azext_spring._deployment_uploadable_factory.FileUpload.upload_and_build')
def test_app_deploy_war(self, file_mock):
file_mock.return_value = mock.MagicMock()
self._execute('rg', 'asc', 'app', deployment=self._get_deployment(), artifact_path='my-path/test.war', runtime_version='Java_8', server_version='Tomcat_10')
resource = self.patch_deployment_resource
self.assertEqual('War', resource.properties.source.type)
self.assertEqual('my-relative-path', resource.properties.source.relative_path)
self.assertIsNone(resource.properties.source.version)
self.assertEqual('Java_8', resource.properties.source.runtime_version)
self.assertEqual('Tomcat_10', resource.properties.source.server_version)

@mock.patch('azext_spring._deployment_uploadable_factory.FileUpload.upload_and_build')
def test_app_deploy_net(self, file_mock):
file_mock.return_value = mock.MagicMock()
Expand Down Expand Up @@ -400,6 +411,26 @@ def test_app_deploy_container_from_jar(self):
self.assertEqual('2Gi', resource.properties.deployment_settings.resource_requests.memory)
self.assertEqual(2, resource.sku.capacity)

@mock.patch('azext_spring._deployment_uploadable_factory.FileUpload.upload_and_build')
def test_app_deploy_war_from_container(self, file_mock):
file_mock.return_value = mock.MagicMock()
deployment = self._get_deployment()
deployment.properties.source.type = 'Container'
deployment.properties.source.custom_container = mock.MagicMock()
deployment.properties.source.relative_path = None
deployment.properties.source.runtime_version = None
deployment.properties.source.version = '123'
self._execute('rg', 'asc', 'app', deployment=deployment, artifact_path='my-path/test.war', server_version='Tomcat_9')
resource = self.put_deployment_resource
self.assertEqual('War', resource.properties.source.type)
self.assertEqual('my-relative-path', resource.properties.source.relative_path)
self.assertIsNone(resource.properties.source.version)
self.assertEqual('Java_11', resource.properties.source.runtime_version)
self.assertEqual('Tomcat_9', resource.properties.source.server_version)
self.assertEqual('2', resource.properties.deployment_settings.resource_requests.cpu)
self.assertEqual('2Gi', resource.properties.deployment_settings.resource_requests.memory)
self.assertEqual(2, resource.sku.capacity)

@mock.patch('azext_spring._deployment_uploadable_factory.FileUpload.upload_and_build')
def test_app_deploy_jar_from_container(self, file_mock):
file_mock.return_value = mock.MagicMock()
Expand Down