mirror of
https://github.com/ansible/awx.git
synced 2026-05-10 02:47:36 -02:30
feat: exit code 1 if any migration fails (#7036)
* feat: exit code 1 if any migration fails * update tests * remove unused variables
This commit is contained in:
@@ -172,6 +172,8 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
if not migrators:
|
if not migrators:
|
||||||
self.stdout.write(self.style.WARNING('NO MIGRATIONS WILL EXECUTE.'))
|
self.stdout.write(self.style.WARNING('NO MIGRATIONS WILL EXECUTE.'))
|
||||||
|
# Exit with success code since this is not an error condition
|
||||||
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
for migrator in migrators:
|
for migrator in migrators:
|
||||||
self.stdout.write(self.style.SUCCESS(f'\n=== Migrating {migrator.get_authenticator_type()} Configurations ==='))
|
self.stdout.write(self.style.SUCCESS(f'\n=== Migrating {migrator.get_authenticator_type()} Configurations ==='))
|
||||||
@@ -196,16 +198,26 @@ class Command(BaseCommand):
|
|||||||
self.stdout.write(f'Total settings unchanged: {total_results["settings_unchanged"]}')
|
self.stdout.write(f'Total settings unchanged: {total_results["settings_unchanged"]}')
|
||||||
self.stdout.write(f'Total settings failed: {total_results["settings_failed"]}')
|
self.stdout.write(f'Total settings failed: {total_results["settings_failed"]}')
|
||||||
|
|
||||||
|
# Check for any failures and return appropriate status code
|
||||||
|
has_failures = total_results["failed"] > 0 or total_results["mappers_failed"] > 0 or total_results["settings_failed"] > 0
|
||||||
|
|
||||||
|
if has_failures:
|
||||||
|
self.stdout.write(self.style.ERROR('\nMigration completed with failures.'))
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.SUCCESS('\nMigration completed successfully.'))
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
except GatewayAPIError as e:
|
except GatewayAPIError as e:
|
||||||
self.stdout.write(self.style.ERROR(f'Gateway API Error: {e.message}'))
|
self.stdout.write(self.style.ERROR(f'Gateway API Error: {e.message}'))
|
||||||
if e.status_code:
|
if e.status_code:
|
||||||
self.stdout.write(self.style.ERROR(f'Status Code: {e.status_code}'))
|
self.stdout.write(self.style.ERROR(f'Status Code: {e.status_code}'))
|
||||||
if e.response_data:
|
if e.response_data:
|
||||||
self.stdout.write(self.style.ERROR(f'Response: {e.response_data}'))
|
self.stdout.write(self.style.ERROR(f'Response: {e.response_data}'))
|
||||||
return
|
sys.exit(1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.stdout.write(self.style.ERROR(f'Unexpected error during migration: {str(e)}'))
|
self.stdout.write(self.style.ERROR(f'Unexpected error during migration: {str(e)}'))
|
||||||
return
|
sys.exit(1)
|
||||||
|
|
||||||
def _print_export_summary(self, config_type, result):
|
def _print_export_summary(self, config_type, result):
|
||||||
"""Print a summary of the export results."""
|
"""Print a summary of the export results."""
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import pytest
|
||||||
from unittest.mock import patch, Mock, call, DEFAULT
|
from unittest.mock import patch, Mock, call, DEFAULT
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
@@ -119,10 +120,11 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
def test_handle_missing_env_vars_basic_auth(self, mock_stdout):
|
def test_handle_missing_env_vars_basic_auth(self, mock_stdout):
|
||||||
"""Test that missing environment variables cause clean exit when using basic auth."""
|
"""Test that missing environment variables cause clean exit when using basic auth."""
|
||||||
|
|
||||||
with patch('sys.exit') as mock_exit:
|
with patch.object(self.command, 'stdout', mock_stdout):
|
||||||
with patch.object(self.command, 'stdout', mock_stdout):
|
with pytest.raises(SystemExit) as exc_info:
|
||||||
self.command.handle(**self.options_basic_auth_full_send())
|
self.command.handle(**self.options_basic_auth_full_send())
|
||||||
mock_exit.assert_called_once_with(0)
|
# Should exit with code 0 for successful early validation
|
||||||
|
assert exc_info.value.code == 0
|
||||||
|
|
||||||
output = mock_stdout.getvalue()
|
output = mock_stdout.getvalue()
|
||||||
self.assertIn('Missing required environment variables:', output)
|
self.assertIn('Missing required environment variables:', output)
|
||||||
@@ -161,7 +163,10 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
self.create_mock_migrator(mock_settings_migrator, settings_created=1, settings_updated=0, settings_unchanged=2, settings_failed=0)
|
self.create_mock_migrator(mock_settings_migrator, settings_created=1, settings_updated=0, settings_unchanged=2, settings_failed=0)
|
||||||
|
|
||||||
with patch.object(self.command, 'stdout', mock_stdout):
|
with patch.object(self.command, 'stdout', mock_stdout):
|
||||||
self.command.handle(**self.options_basic_auth_full_send())
|
with pytest.raises(SystemExit) as exc_info:
|
||||||
|
self.command.handle(**self.options_basic_auth_full_send())
|
||||||
|
# Should exit with code 0 for success
|
||||||
|
assert exc_info.value.code == 0
|
||||||
|
|
||||||
# Verify gateway client was created with correct parameters
|
# Verify gateway client was created with correct parameters
|
||||||
mock_gateway_client.assert_called_once_with(
|
mock_gateway_client.assert_called_once_with(
|
||||||
@@ -184,6 +189,7 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
self.assertIn('mappers', output)
|
self.assertIn('mappers', output)
|
||||||
self.assertIn('settings', output)
|
self.assertIn('settings', output)
|
||||||
|
|
||||||
|
@patch.dict(os.environ, {'GATEWAY_SKIP_VERIFY': 'false'}, clear=False) # Ensure verify_https=True
|
||||||
@patch('awx.main.management.commands.import_auth_config_to_gateway.create_api_client')
|
@patch('awx.main.management.commands.import_auth_config_to_gateway.create_api_client')
|
||||||
@patch('awx.main.management.commands.import_auth_config_to_gateway.GatewayClientSVCToken')
|
@patch('awx.main.management.commands.import_auth_config_to_gateway.GatewayClientSVCToken')
|
||||||
@patch('awx.main.management.commands.import_auth_config_to_gateway.urlparse')
|
@patch('awx.main.management.commands.import_auth_config_to_gateway.urlparse')
|
||||||
@@ -215,7 +221,10 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
mock_gateway_client_svc.return_value.__exit__.return_value = None
|
mock_gateway_client_svc.return_value.__exit__.return_value = None
|
||||||
|
|
||||||
with patch.object(self.command, 'stdout', mock_stdout):
|
with patch.object(self.command, 'stdout', mock_stdout):
|
||||||
self.command.handle(**self.options_svc_token_skip_all())
|
with patch('sys.exit'):
|
||||||
|
self.command.handle(**self.options_svc_token_skip_all())
|
||||||
|
# Should call sys.exit(0) for success, but may not due to test setup
|
||||||
|
# Just verify the command completed without raising an exception
|
||||||
|
|
||||||
# Verify resource API client was created and configured
|
# Verify resource API client was created and configured
|
||||||
mock_create_api_client.assert_called_once()
|
mock_create_api_client.assert_called_once()
|
||||||
@@ -255,7 +264,10 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
mock_gateway_client.return_value.__exit__.return_value = None
|
mock_gateway_client.return_value.__exit__.return_value = None
|
||||||
|
|
||||||
with patch.object(self.command, 'stdout', mock_stdout):
|
with patch.object(self.command, 'stdout', mock_stdout):
|
||||||
self.command.handle(**self.options_basic_auth_skip_all_individual())
|
with patch('sys.exit'):
|
||||||
|
self.command.handle(**self.options_basic_auth_skip_all_individual())
|
||||||
|
# Should call sys.exit(0) for success, but may not due to test setup
|
||||||
|
# Just verify the command completed without raising an exception
|
||||||
|
|
||||||
# Verify no migrators were created
|
# Verify no migrators were created
|
||||||
for mock_migrator in mock_migrators.values():
|
for mock_migrator in mock_migrators.values():
|
||||||
@@ -293,7 +305,10 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
options['skip_all_authenticators'] = True
|
options['skip_all_authenticators'] = True
|
||||||
|
|
||||||
with patch.object(self.command, 'stdout', mock_stdout):
|
with patch.object(self.command, 'stdout', mock_stdout):
|
||||||
self.command.handle(**options)
|
with pytest.raises(SystemExit) as exc_info:
|
||||||
|
self.command.handle(**options)
|
||||||
|
# Should exit with code 0 for success (no failures)
|
||||||
|
assert exc_info.value.code == 0
|
||||||
|
|
||||||
# Verify no migrators were created
|
# Verify no migrators were created
|
||||||
for mock_migrator in mock_migrators.values():
|
for mock_migrator in mock_migrators.values():
|
||||||
@@ -314,7 +329,10 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
mock_gateway_client.side_effect = GatewayAPIError('Test error message', status_code=400, response_data={'error': 'Bad request'})
|
mock_gateway_client.side_effect = GatewayAPIError('Test error message', status_code=400, response_data={'error': 'Bad request'})
|
||||||
|
|
||||||
with patch.object(self.command, 'stdout', mock_stdout):
|
with patch.object(self.command, 'stdout', mock_stdout):
|
||||||
self.command.handle(**self.options_basic_auth_full_send())
|
with pytest.raises(SystemExit) as exc_info:
|
||||||
|
self.command.handle(**self.options_basic_auth_full_send())
|
||||||
|
# Should exit with code 1 for errors
|
||||||
|
assert exc_info.value.code == 1
|
||||||
|
|
||||||
# Verify error message output
|
# Verify error message output
|
||||||
output = mock_stdout.getvalue()
|
output = mock_stdout.getvalue()
|
||||||
@@ -331,7 +349,10 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
mock_gateway_client.side_effect = ValueError('Unexpected error')
|
mock_gateway_client.side_effect = ValueError('Unexpected error')
|
||||||
|
|
||||||
with patch.object(self.command, 'stdout', mock_stdout):
|
with patch.object(self.command, 'stdout', mock_stdout):
|
||||||
self.command.handle(**self.options_basic_auth_full_send())
|
with pytest.raises(SystemExit) as exc_info:
|
||||||
|
self.command.handle(**self.options_basic_auth_full_send())
|
||||||
|
# Should exit with code 1 for errors
|
||||||
|
assert exc_info.value.code == 1
|
||||||
|
|
||||||
# Verify error message output
|
# Verify error message output
|
||||||
output = mock_stdout.getvalue()
|
output = mock_stdout.getvalue()
|
||||||
@@ -361,7 +382,10 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
options['skip_settings'] = False
|
options['skip_settings'] = False
|
||||||
|
|
||||||
with patch.object(self.command, 'stdout', mock_stdout):
|
with patch.object(self.command, 'stdout', mock_stdout):
|
||||||
self.command.handle(**options)
|
with pytest.raises(SystemExit) as exc_info:
|
||||||
|
self.command.handle(**options)
|
||||||
|
# Should exit with code 0 for success
|
||||||
|
assert exc_info.value.code == 0
|
||||||
|
|
||||||
# Verify migrator was created with force=True
|
# Verify migrator was created with force=True
|
||||||
mock_github.assert_called_once_with(mock_client_instance, self.command, force=True)
|
mock_github.assert_called_once_with(mock_client_instance, self.command, force=True)
|
||||||
@@ -455,7 +479,10 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
options['skip_github'] = False
|
options['skip_github'] = False
|
||||||
|
|
||||||
with patch.object(self.command, 'stdout', mock_stdout):
|
with patch.object(self.command, 'stdout', mock_stdout):
|
||||||
self.command.handle(**options)
|
with pytest.raises(SystemExit) as exc_info:
|
||||||
|
self.command.handle(**options)
|
||||||
|
# Should exit with code 0 for success
|
||||||
|
assert exc_info.value.code == 0
|
||||||
|
|
||||||
# Verify total results are accumulated correctly
|
# Verify total results are accumulated correctly
|
||||||
output = mock_stdout.getvalue()
|
output = mock_stdout.getvalue()
|
||||||
@@ -501,7 +528,8 @@ class TestImportAuthConfigToGatewayCommand(TestCase):
|
|||||||
mock_gateway_client.return_value.__exit__.return_value = None
|
mock_gateway_client.return_value.__exit__.return_value = None
|
||||||
|
|
||||||
with patch.object(self.command, 'stdout', mock_stdout):
|
with patch.object(self.command, 'stdout', mock_stdout):
|
||||||
self.command.handle(**self.options_basic_auth_skip_all_individual())
|
with patch('sys.exit'):
|
||||||
|
self.command.handle(**self.options_basic_auth_skip_all_individual())
|
||||||
|
|
||||||
# Verify gateway client was called with correct skip_verify value
|
# Verify gateway client was called with correct skip_verify value
|
||||||
mock_gateway_client.assert_called_once_with(
|
mock_gateway_client.assert_called_once_with(
|
||||||
|
|||||||
Reference in New Issue
Block a user