Example #1
0
    def test_failed_to_get_locale_encoding_warning_is_printed(self, mock_logger):
        shell = Shell()
        shell.run(argv=['trigger', 'list'])

        call_args = mock_logger.warn.call_args[0][0]
        self.assertTrue('Locale unknown with encoding unknown which is not UTF-8 is used.' in
                        call_args)
Example #2
0
    def test_non_unicode_encoding_locale_warning_is_printed(self, mock_logger):
        shell = Shell()
        shell.run(argv=['trigger', 'list'])

        call_args = mock_logger.warn.call_args[0][0]
        self.assertIn(
            'Locale en_US with encoding iso which is not UTF-8 is used.',
            call_args)
Example #3
0
    def test_get_cached_auth_token_no_token_cache_file(self):
        client = Client()
        shell = Shell()
        username = '******'
        password = '******'

        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)
        self.assertEqual(result, None)
Example #4
0
    def test_automatic_auth_skipped_on_auth_command(self):
        self._write_mock_config()

        shell = Shell()
        shell._get_auth_token = mock.Mock()

        argv = ['auth', 'testu', '-p', 'testp']
        args = shell.parser.parse_args(args=argv)
        shell.get_client(args=args)
        self.assertEqual(shell._get_auth_token.call_count, 0)
Example #5
0
    def test_automatic_auth_skipped_on_auth_command(self):
        self._write_mock_config()

        shell = Shell()
        shell._get_auth_token = mock.Mock()

        argv = ['auth', 'testu', '-p', 'testp']
        args = shell.parser.parse_args(args=argv)
        shell.get_client(args=args)
        self.assertEqual(shell._get_auth_token.call_count, 0)
Example #6
0
    def test_get_version_package_metadata_file_exists_stable_version(self):
        # stable version, package metadata file exists on disk - no git revision should be included
        package_metadata_path = self._write_mock_package_metadata_file()
        st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path

        shell = Shell()
        shell.run(argv=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn('v2.8.0, on Python', stderr)
Example #7
0
    def test_automatic_auth_skipped_if_token_provided_as_env_variable(self):
        self._write_mock_config()

        shell = Shell()
        shell._get_auth_token = mock.Mock()

        os.environ['ST2_AUTH_TOKEN'] = 'fooo'
        argv = ['action', 'list']
        args = shell.parser.parse_args(args=argv)
        shell.get_client(args=args)
        self.assertEqual(shell._get_auth_token.call_count, 0)
Example #8
0
    def test_automatic_auth_skipped_if_token_provided_as_env_variable(self):
        self._write_mock_config()

        shell = Shell()
        shell._get_auth_token = mock.Mock()

        os.environ['ST2_AUTH_TOKEN'] = 'fooo'
        argv = ['action', 'list']
        args = shell.parser.parse_args(args=argv)
        shell.get_client(args=args)
        self.assertEqual(shell._get_auth_token.call_count, 0)
Example #9
0
    def test_get_version_package_metadata_file_exists_stable_version(self):
        # stable version, package metadata file exists on disk - no git revision should be included
        package_metadata_path = self._write_mock_package_metadata_file()
        st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path

        shell = Shell()
        shell.run(argv=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertTrue('v2.8.0, on Python' in stderr)
Example #10
0
    def test_get_one_unicode_character_in_name(self):
        self._write_mock_config()

        shell = Shell()
        shell._get_auth_token = mock.Mock()

        os.environ["ST2_AUTH_TOKEN"] = "fooo"
        argv = ["action", "get", "examples.test_rule_utf8_náme"]
        args = shell.parser.parse_args(args=argv)
        shell.get_client(args=args)
        self.assertEqual(args.ref_or_id, ["examples.test_rule_utf8_náme"])
Example #11
0
    def test_get_cached_auth_token_invalid_permissions(self):
        shell = Shell()
        client = Client()
        username = '******'
        password = '******'

        cached_token_path = shell._get_cached_token_path_for_user(username=username)
        data = {
            'token': 'yayvalid',
            'expire_timestamp': (int(time.time()) + 20)
        }
        with open(cached_token_path, 'w') as fp:
            fp.write(json.dumps(data))

        # 1. Current user doesn't have read access to the config directory
        os.chmod(self._mock_config_directory_path, 0o000)

        shell.LOG = mock.Mock()
        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)

        self.assertEqual(result, None)
        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = ('Unable to retrieve cached token from .*? read access to the parent '
                        'directory')
        self.assertRegexpMatches(log_message, expected_msg)

        # 2. Read access on the directory, but not on the cached token file
        os.chmod(self._mock_config_directory_path, 0o777)  # nosec
        os.chmod(cached_token_path, 0o000)

        shell.LOG = mock.Mock()
        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)
        self.assertEqual(result, None)

        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = ('Unable to retrieve cached token from .*? read access to this file')
        self.assertRegexpMatches(log_message, expected_msg)

        # 3. Other users also have read access to the file
        os.chmod(self._mock_config_directory_path, 0o777)  # nosec
        os.chmod(cached_token_path, 0o444)

        shell.LOG = mock.Mock()
        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)
        self.assertEqual(result, 'yayvalid')

        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = ('Permissions .*? for cached token file .*? are too permissive.*')
        self.assertRegexpMatches(log_message, expected_msg)
Example #12
0
    def test_get_cached_auth_token_corrupted_token_cache_file(self):
        client = Client()
        shell = Shell()
        username = '******'
        password = '******'

        cached_token_path = shell._get_cached_token_path_for_user(username=username)
        with open(cached_token_path, 'w') as fp:
            fp.write('CORRRRRUPTED!')

        expected_msg = 'File (.+) with cached token is corrupted or invalid'
        self.assertRaisesRegexp(ValueError, expected_msg, shell._get_cached_auth_token,
                                client=client, username=username, password=password)
Example #13
0
    def test_get_cached_auth_token_valid_token_in_cache_file(self):
        client = Client()
        shell = Shell()
        username = "******"
        password = "******"

        cached_token_path = shell._get_cached_token_path_for_user(username=username)
        data = {"token": "yayvalid", "expire_timestamp": (int(time.time()) + 20)}
        with open(cached_token_path, "w") as fp:
            fp.write(json.dumps(data))

        result = shell._get_cached_auth_token(
            client=client, username=username, password=password
        )
        self.assertEqual(result, "yayvalid")
Example #14
0
    def test_get_cached_auth_token_invalid_permissions(self):
        shell = Shell()
        client = Client()
        username = '******'
        password = '******'

        cached_token_path = shell._get_cached_token_path_for_user(username=username)
        data = {
            'token': 'yayvalid',
            'expire_timestamp': (int(time.time()) + 20)
        }
        with open(cached_token_path, 'w') as fp:
            fp.write(json.dumps(data))

        # 1. Current user doesn't have read access to the config directory
        os.chmod(self._mock_config_directory_path, 0000)

        shell.LOG = mock.Mock()
        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)

        self.assertEqual(result, None)
        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = ('Unable to retrieve cached token from .*? read access to the parent '
                        'directory')
        self.assertRegexpMatches(log_message, expected_msg)

        # 2. Read access on the directory, but not on the cached token file
        os.chmod(self._mock_config_directory_path, 0777)  # nosec
        os.chmod(cached_token_path, 0000)

        shell.LOG = mock.Mock()
        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)
        self.assertEqual(result, None)

        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = ('Unable to retrieve cached token from .*? read access to this file')
        self.assertRegexpMatches(log_message, expected_msg)

        # 3. Other users also have read access to the file
        os.chmod(self._mock_config_directory_path, 0777)  # nosec
        os.chmod(cached_token_path, 0444)

        shell.LOG = mock.Mock()
        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)
        self.assertEqual(result, 'yayvalid')

        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = ('Permissions .*? for cached token file .*? are to permissive')
        self.assertRegexpMatches(log_message, expected_msg)
Example #15
0
    def test_cache_auth_token_invalid_permissions(self):
        shell = Shell()
        username = '******'

        cached_token_path = shell._get_cached_token_path_for_user(
            username=username)
        expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=30)

        token_db = TokenDB(user=username, token='fyeah', expiry=expiry)

        cached_token_path = shell._get_cached_token_path_for_user(
            username=username)
        data = {
            'token': 'yayvalid',
            'expire_timestamp': (int(time.time()) + 20)
        }
        with open(cached_token_path, 'w') as fp:
            fp.write(json.dumps(data))

        # 1. Current user has no write access to the parent directory
        os.chmod(self._mock_config_directory_path, 0o000)

        shell.LOG = mock.Mock()
        shell._cache_auth_token(token_obj=token_db)

        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = (
            'Unable to write token to .*? doesn\'t have write access to the parent '
            'directory')
        self.assertRegexpMatches(log_message, expected_msg)

        # 2. Current user has no write access to the cached token file
        os.chmod(self._mock_config_directory_path, 0o777)  # nosec
        os.chmod(cached_token_path, 0o000)

        shell.LOG = mock.Mock()
        shell._cache_auth_token(token_obj=token_db)

        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = (
            'Unable to write token to .*? doesn\'t have write access to this file'
        )
        self.assertRegexpMatches(log_message, expected_msg)
Example #16
0
    def test_help_command_line_arg_works_for_supported_commands(self):
        shell = Shell()

        for command in self.COMMANDS:
            # First test longhang notation
            argv = command + ['--help']

            try:
                result = shell.run(argv)
            except SystemExit as e:
                self.assertEqual(e.code, 0)
            else:
                self.assertEqual(result, 0)

            stdout = self.stdout.getvalue()

            self.assertTrue('usage:' in stdout)
            self.assertTrue(' '.join(command) in stdout)
            # self.assertTrue('positional arguments:' in stdout)
            self.assertTrue('optional arguments:' in stdout)

            # Reset stdout and stderr after each iteration
            self._reset_output_streams()

            # Then shorthand notation
            argv = command + ['-h']

            try:
                result = shell.run(argv)
            except SystemExit as e:
                self.assertEqual(e.code, 0)
            else:
                self.assertEqual(result, 0)

            stdout = self.stdout.getvalue()

            self.assertTrue('usage:' in stdout)
            self.assertTrue(' '.join(command) in stdout)
            # self.assertTrue('positional arguments:' in stdout)
            self.assertTrue('optional arguments:' in stdout)

            # Verify that the actual help usage string was triggered and not the invalid
            # "too few arguments" which would indicate command doesn't actually correctly handle
            # --help flag
            self.assertTrue('too few arguments' not in stdout)

            self._reset_output_streams()
Example #17
0
    def test_help_command_line_arg_works_for_supported_commands(self):
        shell = Shell()

        for command in self.COMMANDS:
            # First test longhang notation
            argv = command + ['--help']

            try:
                result = shell.run(argv)
            except SystemExit as e:
                self.assertEqual(e.code, 0)
            else:
                self.assertEqual(result, 0)

            stdout = self.stdout.getvalue()

            self.assertTrue('usage:' in stdout)
            self.assertTrue(' '.join(command) in stdout)
            # self.assertTrue('positional arguments:' in stdout)
            self.assertTrue('optional arguments:' in stdout)

            # Reset stdout and stderr after each iteration
            self._reset_output_streams()

            # Then shorthand notation
            argv = command + ['-h']

            try:
                result = shell.run(argv)
            except SystemExit as e:
                self.assertEqual(e.code, 0)
            else:
                self.assertEqual(result, 0)

            stdout = self.stdout.getvalue()

            self.assertTrue('usage:' in stdout)
            self.assertTrue(' '.join(command) in stdout)
            # self.assertTrue('positional arguments:' in stdout)
            self.assertTrue('optional arguments:' in stdout)

            # Verify that the actual help usage string was triggered and not the invalid
            # "too few arguments" which would indicate command doesn't actually correctly handle
            # --help flag
            self.assertTrue('too few arguments' not in stdout)

            self._reset_output_streams()
Example #18
0
    def test_get_cached_auth_token_valid_token_in_cache_file(self):
        client = Client()
        shell = Shell()
        username = '******'
        password = '******'

        cached_token_path = shell._get_cached_token_path_for_user(username=username)
        data = {
            'token': 'yayvalid',
            'expire_timestamp': (int(time.time()) + 20)
        }
        with open(cached_token_path, 'w') as fp:
            fp.write(json.dumps(data))

        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)
        self.assertEqual(result, 'yayvalid')
Example #19
0
    def test_get_version_no_package_metadata_file_stable_version(self):
        # stable version, package metadata file doesn't exist on disk - no git revision should be
        # included
        shell = Shell()
        shell.parser.parse_args(args=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertTrue('v2.8.0, on Python' in stderr)
Example #20
0
    def test_get_version_no_package_metadata_file_dev_version(self):
        # dev version, package metadata file doesn't exist on disk - no git revision should be
        # included since package metadata file doesn't exist on disk
        shell = Shell()
        shell.parser.parse_args(args=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn('v2.9dev, on Python', stderr)
Example #21
0
    def test_get_cached_auth_token_corrupted_token_cache_file(self):
        client = Client()
        shell = Shell()
        username = "******"
        password = "******"

        cached_token_path = shell._get_cached_token_path_for_user(username=username)
        with open(cached_token_path, "w") as fp:
            fp.write("CORRRRRUPTED!")

        expected_msg = "File (.+) with cached token is corrupted or invalid"
        self.assertRaisesRegexp(
            ValueError,
            expected_msg,
            shell._get_cached_auth_token,
            client=client,
            username=username,
            password=password,
        )
Example #22
0
    def test_dont_warn_multiple_times(self):
        mock_temp_dir_path = tempfile.mkdtemp()
        mock_config_dir_path = os.path.join(mock_temp_dir_path, "testconfig")
        mock_config_path = os.path.join(mock_config_dir_path, "config")

        # Make the temporary config directory
        os.makedirs(mock_config_dir_path)

        old_perms = os.stat(mock_config_dir_path).st_mode
        new_perms = old_perms | 0o7
        os.chmod(mock_config_dir_path, new_perms)

        # Make the temporary config file
        shutil.copyfile(CONFIG_FILE_PATH_FULL, mock_config_path)
        os.chmod(mock_config_path, 0o777)  # nosec

        shell = Shell()
        shell.LOG = mock.Mock()

        # Test without token.
        shell.run(["--config-file", mock_config_path, "action", "list"])

        self.assertEqual(shell.LOG.warn.call_count, 2)
        self.assertEqual(
            shell.LOG.warn.call_args_list[0][0][0][:63],
            "The StackStorm configuration directory permissions are insecure",
        )
        self.assertEqual(
            shell.LOG.warn.call_args_list[1][0][0][:58],
            "The StackStorm configuration file permissions are insecure",
        )

        self.assertEqual(shell.LOG.info.call_count, 2)
        self.assertEqual(
            shell.LOG.info.call_args_list[0][0][0],
            "The SGID bit is not "
            "set on the StackStorm configuration directory.",
        )

        self.assertEqual(shell.LOG.info.call_args_list[1][0][0],
                         "Skipping parsing CLI config")
Example #23
0
    def test_get_version_package_metadata_file_exists_dev_version(self):
        # dev version, package metadata file exists on disk - git revision should be included
        # since package metadata file exists on disk and contains server.git_sha attribute
        package_metadata_path = self._write_mock_package_metadata_file()
        st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path

        shell = Shell()
        shell.parser.parse_args(args=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn('v2.9dev (abcdefg), on Python', stderr)
Example #24
0
    def test_dont_warn_multiple_times(self):
        mock_temp_dir_path = tempfile.mkdtemp()
        mock_config_dir_path = os.path.join(mock_temp_dir_path, 'testconfig')
        mock_config_path = os.path.join(mock_config_dir_path, 'config')

        # Make the temporary config directory
        os.makedirs(mock_config_dir_path)

        old_perms = os.stat(mock_config_dir_path).st_mode
        new_perms = old_perms | 0o7
        os.chmod(mock_config_dir_path, new_perms)

        # Make the temporary config file
        shutil.copyfile(CONFIG_FILE_PATH_FULL, mock_config_path)
        os.chmod(mock_config_path, 0o777)  # nosec

        shell = Shell()
        shell.LOG = mock.Mock()

        # Test without token.
        shell.run(['--config-file', mock_config_path, 'action', 'list'])

        self.assertEqual(shell.LOG.warn.call_count, 2)
        self.assertEqual(
            shell.LOG.warn.call_args_list[0][0][0][:63],
            'The StackStorm configuration directory permissions are insecure')
        self.assertEqual(
            shell.LOG.warn.call_args_list[1][0][0][:58],
            'The StackStorm configuration file permissions are insecure')

        self.assertEqual(shell.LOG.info.call_count, 2)
        self.assertEqual(
            shell.LOG.info.call_args_list[0][0][0], "The SGID bit is not "
            "set on the StackStorm configuration directory.")

        self.assertEqual(
            shell.LOG.info.call_args_list[1][0][0], 'Skipping parsing CLI config')
Example #25
0
    def test_cache_auth_token_invalid_permissions(self):
        shell = Shell()
        username = '******'

        cached_token_path = shell._get_cached_token_path_for_user(username=username)
        expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=30)

        token_db = TokenDB(user=username, token='fyeah', expiry=expiry)

        cached_token_path = shell._get_cached_token_path_for_user(username=username)
        data = {
            'token': 'yayvalid',
            'expire_timestamp': (int(time.time()) + 20)
        }
        with open(cached_token_path, 'w') as fp:
            fp.write(json.dumps(data))

        # 1. Current user has no write access to the parent directory
        os.chmod(self._mock_config_directory_path, 0000)

        shell.LOG = mock.Mock()
        shell._cache_auth_token(token_obj=token_db)

        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = ('Unable to write token to .*? doesn\'t have write access to the parent '
                        'directory')
        self.assertRegexpMatches(log_message, expected_msg)

        # 2. Current user has no write access to the cached token file
        os.chmod(self._mock_config_directory_path, 0777)  # nosec
        os.chmod(cached_token_path, 0000)

        shell.LOG = mock.Mock()
        shell._cache_auth_token(token_obj=token_db)

        self.assertEqual(shell.LOG.warn.call_count, 1)
        log_message = shell.LOG.warn.call_args[0][0]

        expected_msg = ('Unable to write token to .*? doesn\'t have write access to this file')
        self.assertRegexpMatches(log_message, expected_msg)
Example #26
0
    def test_automatic_auth_skipped_if_token_provided_as_cli_argument(self):
        self._write_mock_config()

        shell = Shell()
        shell._get_auth_token = mock.Mock()

        argv = ['action', 'list', '--token=bar']
        args = shell.parser.parse_args(args=argv)
        shell.get_client(args=args)
        self.assertEqual(shell._get_auth_token.call_count, 0)

        argv = ['action', 'list', '-t', 'bar']
        args = shell.parser.parse_args(args=argv)
        shell.get_client(args=args)
        self.assertEqual(shell._get_auth_token.call_count, 0)
Example #27
0
    def test_automatic_auth_skipped_if_token_provided_as_cli_argument(self):
        self._write_mock_config()

        shell = Shell()
        shell._get_auth_token = mock.Mock()

        argv = ['action', 'list', '--token=bar']
        args = shell.parser.parse_args(args=argv)
        shell.get_client(args=args)
        self.assertEqual(shell._get_auth_token.call_count, 0)

        argv = ['action', 'list', '-t', 'bar']
        args = shell.parser.parse_args(args=argv)
        shell.get_client(args=args)
        self.assertEqual(shell._get_auth_token.call_count, 0)
Example #28
0
    def test_cache_auth_token_success(self):
        client = Client()
        shell = Shell()
        username = '******'
        password = '******'
        expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=30)

        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)
        self.assertEqual(result, None)

        token_db = TokenDB(user=username, token='fyeah', expiry=expiry)
        shell._cache_auth_token(token_obj=token_db)

        result = shell._get_cached_auth_token(client=client, username=username,
                                              password=password)
        self.assertEqual(result, 'fyeah')
Example #29
0
class ShellTestCase(base.BaseCLITestCase):
    capture_output = True

    def __init__(self, *args, **kwargs):
        super(ShellTestCase, self).__init__(*args, **kwargs)
        self.shell = Shell()

    def setUp(self):
        super(ShellTestCase, self).setUp()

        if six.PY3:
            # In python --version outputs to stdout and in 2.x to stderr
            self.version_output = self.stdout
        else:
            self.version_output = self.stderr

    def test_commands_usage_and_help_strings(self):
        # No command, should print out user friendly usage / help string
        self.assertEqual(self.shell.run([]), 2)

        self.stderr.seek(0)
        stderr = self.stderr.read()
        self.assertTrue('Usage: ' in stderr)
        self.assertTrue('For example:' in stderr)
        self.assertTrue('CLI for StackStorm' in stderr)
        self.assertTrue('positional arguments:' in stderr)

        self.stdout.truncate()
        self.stderr.truncate()

        # --help should result in the same output
        try:
            self.assertEqual(self.shell.run(['--help']), 0)
        except SystemExit as e:
            self.assertEqual(e.code, 0)

        self.stdout.seek(0)
        stdout = self.stdout.read()
        self.assertTrue('Usage: ' in stdout)
        self.assertTrue('For example:' in stdout)
        self.assertTrue('CLI for StackStorm' in stdout)
        self.assertTrue('positional arguments:' in stdout)

        self.stdout.truncate()
        self.stderr.truncate()

        # Sub command with no args
        try:
            self.assertEqual(self.shell.run(['action']), 2)
        except SystemExit as e:
            self.assertEqual(e.code, 2)

        self.stderr.seek(0)
        stderr = self.stderr.read()

        self.assertTrue('usage' in stderr)

        if six.PY2:
            self.assertTrue('{list,get,create,update' in stderr)
            self.assertTrue('error: too few arguments' in stderr)

    def test_endpoints_default(self):
        base_url = 'http://127.0.0.1'
        auth_url = 'http://127.0.0.1:9100'
        api_url = 'http://127.0.0.1:9101/v1'
        stream_url = 'http://127.0.0.1:9102/v1'
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)
        self.assertEqual(client.endpoints['stream'], stream_url)

    def test_endpoints_base_url_from_cli(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:9100'
        api_url = 'http://www.st2.com:9101/v1'
        stream_url = 'http://www.st2.com:9102/v1'
        args = ['--url', base_url, 'trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)
        self.assertEqual(client.endpoints['stream'], stream_url)

    def test_endpoints_base_url_from_env(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:9100'
        api_url = 'http://www.st2.com:9101/v1'
        stream_url = 'http://www.st2.com:9102/v1'
        os.environ['ST2_BASE_URL'] = base_url
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)
        self.assertEqual(client.endpoints['stream'], stream_url)

    def test_endpoints_override_from_cli(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:8888'
        api_url = 'http://www.stackstorm1.com:9101/v1'
        stream_url = 'http://www.stackstorm1.com:9102/v1'
        args = ['--url', base_url,
                '--auth-url', auth_url,
                '--api-url', api_url,
                '--stream-url', stream_url,
                'trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)
        self.assertEqual(client.endpoints['stream'], stream_url)

    def test_endpoints_override_from_env(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:8888'
        api_url = 'http://www.stackstorm1.com:9101/v1'
        stream_url = 'http://www.stackstorm1.com:9102/v1'
        os.environ['ST2_BASE_URL'] = base_url
        os.environ['ST2_AUTH_URL'] = auth_url
        os.environ['ST2_API_URL'] = api_url
        os.environ['ST2_STREAM_URL'] = stream_url
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)
        self.assertEqual(client.endpoints['stream'], stream_url)

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(base.RESOURCES), 200, 'OK')))
    def test_exit_code_on_success(self):
        argv = ['trigger', 'list']
        self.assertEqual(self.shell.run(argv), 0)

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(None, 500, 'INTERNAL SERVER ERROR')))
    def test_exit_code_on_error(self):
        argv = ['trigger', 'list']
        self.assertEqual(self.shell.run(argv), 1)

    def _validate_parser(self, args_list, is_subcommand=True):
        for args in args_list:
            ns = self.shell.parser.parse_args(args)
            func = (self.shell.commands[args[0]].run_and_print
                    if not is_subcommand
                    else self.shell.commands[args[0]].commands[args[1]].run_and_print)
            self.assertEqual(ns.func, func)

    def test_action(self):
        args_list = [
            ['action', 'list'],
            ['action', 'get', 'abc'],
            ['action', 'create', '/tmp/action.json'],
            ['action', 'update', '123', '/tmp/action.json'],
            ['action', 'delete', 'abc'],
            ['action', 'execute', '-h'],
            ['action', 'execute', 'remote', '-h'],
            ['action', 'execute', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"'],
            ['action', 'execute', 'remote-fib', 'hosts=192.168.1.1', '3', '8']
        ]
        self._validate_parser(args_list)

    def test_action_execution(self):
        args_list = [
            ['execution', 'list'],
            ['execution', 'list', '-a', 'all'],
            ['execution', 'list', '--attr=all'],
            ['execution', 'get', '123'],
            ['execution', 'get', '123', '-d'],
            ['execution', 'get', '123', '-k', 'localhost.stdout'],
            ['execution', 're-run', '123'],
            ['execution', 're-run', '123', '--tasks', 'x', 'y', 'z'],
            ['execution', 're-run', '123', '--tasks', 'x', 'y', 'z', '--no-reset', 'x'],
            ['execution', 're-run', '123', 'a=1', 'b=x', 'c=True'],
            ['execution', 'cancel', '123'],
            ['execution', 'cancel', '123', '456'],
            ['execution', 'pause', '123'],
            ['execution', 'pause', '123', '456'],
            ['execution', 'resume', '123'],
            ['execution', 'resume', '123', '456']
        ]
        self._validate_parser(args_list)

        # Test mutually exclusive argument groups
        self.assertRaises(SystemExit, self._validate_parser,
                          [['execution', 'get', '123', '-d', '-k', 'localhost.stdout']])

    def test_key(self):
        args_list = [
            ['key', 'list'],
            ['key', 'list', '-n', '2'],
            ['key', 'get', 'abc'],
            ['key', 'set', 'abc', '123'],
            ['key', 'delete', 'abc'],
            ['key', 'load', '/tmp/keys.json']
        ]
        self._validate_parser(args_list)

    def test_policy(self):
        args_list = [
            ['policy', 'list'],
            ['policy', 'list', '-p', 'core'],
            ['policy', 'list', '--pack', 'core'],
            ['policy', 'list', '-r', 'core.local'],
            ['policy', 'list', '--resource-ref', 'core.local'],
            ['policy', 'list', '-pt', 'action.type1'],
            ['policy', 'list', '--policy-type', 'action.type1'],
            ['policy', 'list', '-r', 'core.local', '-pt', 'action.type1'],
            ['policy', 'list', '--resource-ref', 'core.local', '--policy-type', 'action.type1'],
            ['policy', 'get', 'abc'],
            ['policy', 'create', '/tmp/policy.json'],
            ['policy', 'update', '123', '/tmp/policy.json'],
            ['policy', 'delete', 'abc']
        ]
        self._validate_parser(args_list)

    def test_policy_type(self):
        args_list = [
            ['policy-type', 'list'],
            ['policy-type', 'list', '-r', 'action'],
            ['policy-type', 'list', '--resource-type', 'action'],
            ['policy-type', 'get', 'abc']
        ]
        self._validate_parser(args_list)

    def test_pack(self):
        args_list = [
            ['pack', 'list'],
            ['pack', 'get', 'abc'],
            ['pack', 'search', 'abc'],
            ['pack', 'show', 'abc'],
            ['pack', 'remove', 'abc'],
            ['pack', 'remove', 'abc', '--detail'],
            ['pack', 'install', 'abc'],
            ['pack', 'install', 'abc', '--force'],
            ['pack', 'install', 'abc', '--detail'],
            ['pack', 'config', 'abc']
        ]
        self._validate_parser(args_list)

    @mock.patch('st2client.base.ST2_CONFIG_PATH', '/home/does/not/exist')
    def test_print_config_default_config_no_config(self):
        os.environ['ST2_CONFIG_FILE'] = '/home/does/not/exist'
        argv = ['--print-config']
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertTrue('username = None' in stdout)
        self.assertTrue('cache_token = True' in stdout)

    def test_print_config_custom_config_as_env_variable(self):
        os.environ['ST2_CONFIG_FILE'] = CONFIG_FILE_PATH_FULL
        argv = ['--print-config']
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertTrue('username = test1' in stdout)
        self.assertTrue('cache_token = False' in stdout)

    def test_print_config_custom_config_as_command_line_argument(self):
        argv = ['--print-config', '--config-file=%s' % (CONFIG_FILE_PATH_FULL)]
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertTrue('username = test1' in stdout)
        self.assertTrue('cache_token = False' in stdout)

    def test_run(self):
        args_list = [
            ['run', '-h'],
            ['run', 'abc', '-h'],
            ['run', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"'],
            ['run', 'remote-fib', 'hosts=192.168.1.1', '3', '8']
        ]
        self._validate_parser(args_list, is_subcommand=False)

    def test_runner(self):
        args_list = [
            ['runner', 'list'],
            ['runner', 'get', 'abc']
        ]
        self._validate_parser(args_list)

    def test_rule(self):
        args_list = [
            ['rule', 'list'],
            ['rule', 'list', '-n', '1'],
            ['rule', 'get', 'abc'],
            ['rule', 'create', '/tmp/rule.json'],
            ['rule', 'update', '123', '/tmp/rule.json'],
            ['rule', 'delete', 'abc']
        ]
        self._validate_parser(args_list)

    def test_trigger(self):
        args_list = [
            ['trigger', 'list'],
            ['trigger', 'get', 'abc'],
            ['trigger', 'create', '/tmp/trigger.json'],
            ['trigger', 'update', '123', '/tmp/trigger.json'],
            ['trigger', 'delete', 'abc']
        ]
        self._validate_parser(args_list)

    def test_workflow(self):
        args_list = [
            ['workflow', 'inspect', '--file', '/path/to/workflow/definition'],
            ['workflow', 'inspect', '--action', 'mock.foobar']
        ]

        self._validate_parser(args_list)

    @mock.patch('sys.exit', mock.Mock())
    @mock.patch('st2client.shell.__version__', 'v2.8.0')
    def test_get_version_no_package_metadata_file_stable_version(self):
        # stable version, package metadata file doesn't exist on disk - no git revision should be
        # included
        shell = Shell()
        shell.parser.parse_args(args=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertTrue('v2.8.0, on Python' in stderr)

    @mock.patch('sys.exit', mock.Mock())
    @mock.patch('st2client.shell.__version__', 'v2.8.0')
    def test_get_version_package_metadata_file_exists_stable_version(self):
        # stable version, package metadata file exists on disk - no git revision should be included
        package_metadata_path = self._write_mock_package_metadata_file()
        st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path

        shell = Shell()
        shell.run(argv=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertTrue('v2.8.0, on Python' in stderr)

    @mock.patch('sys.exit', mock.Mock())
    @mock.patch('st2client.shell.__version__', 'v2.9dev')
    @mock.patch('st2client.shell.PACKAGE_METADATA_FILE_PATH', '/tmp/doesnt/exist.1')
    def test_get_version_no_package_metadata_file_dev_version(self):
        # dev version, package metadata file doesn't exist on disk - no git revision should be
        # included since package metadata file doesn't exist on disk
        shell = Shell()
        shell.parser.parse_args(args=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertTrue('v2.9dev, on Python' in stderr)

    @mock.patch('sys.exit', mock.Mock())
    @mock.patch('st2client.shell.__version__', 'v2.9dev')
    def test_get_version_package_metadata_file_exists_dev_version(self):
        # dev version, package metadata file exists on disk - git revision should be included
        # since package metadata file exists on disk and contains server.git_sha attribute
        package_metadata_path = self._write_mock_package_metadata_file()
        st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path

        shell = Shell()
        shell.parser.parse_args(args=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertTrue('v2.9dev (abcdefg), on Python' in stderr)

    @mock.patch('locale.getdefaultlocale', mock.Mock(return_value=['en_US']))
    @mock.patch('locale.getpreferredencoding', mock.Mock(return_value='iso'))
    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(base.RESOURCES), 200, 'OK')))
    @mock.patch('st2client.shell.LOGGER')
    def test_non_unicode_encoding_locale_warning_is_printed(self, mock_logger):
        shell = Shell()
        shell.run(argv=['trigger', 'list'])

        call_args = mock_logger.warn.call_args[0][0]
        self.assertTrue('Locale en_US with encoding iso which is not UTF-8 is used.' in call_args)

    @mock.patch('locale.getdefaultlocale', mock.Mock(side_effect=ValueError('bar')))
    @mock.patch('locale.getpreferredencoding', mock.Mock(side_effect=ValueError('bar')))
    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(base.RESOURCES), 200, 'OK')))
    @mock.patch('st2client.shell.LOGGER')
    def test_failed_to_get_locale_encoding_warning_is_printed(self, mock_logger):
        shell = Shell()
        shell.run(argv=['trigger', 'list'])

        call_args = mock_logger.warn.call_args[0][0]
        self.assertTrue('Locale unknown with encoding unknown which is not UTF-8 is used.' in
                        call_args)

    def _write_mock_package_metadata_file(self):
        _, package_metadata_path = tempfile.mkstemp()

        with open(package_metadata_path, 'w') as fp:
            fp.write(MOCK_PACKAGE_METADATA)

        return package_metadata_path

    @unittest2.skipIf(True, 'skipping until checks are re-enabled')
    @mock.patch.object(
        requests, 'get',
        mock.MagicMock(return_value=base.FakeResponse("{}", 200, 'OK')))
    def test_dont_warn_multiple_times(self):
        mock_temp_dir_path = tempfile.mkdtemp()
        mock_config_dir_path = os.path.join(mock_temp_dir_path, 'testconfig')
        mock_config_path = os.path.join(mock_config_dir_path, 'config')

        # Make the temporary config directory
        os.makedirs(mock_config_dir_path)

        old_perms = os.stat(mock_config_dir_path).st_mode
        new_perms = old_perms | 0o7
        os.chmod(mock_config_dir_path, new_perms)

        # Make the temporary config file
        shutil.copyfile(CONFIG_FILE_PATH_FULL, mock_config_path)
        os.chmod(mock_config_path, 0o777)  # nosec

        shell = Shell()
        shell.LOG = mock.Mock()

        # Test without token.
        shell.run(['--config-file', mock_config_path, 'action', 'list'])

        self.assertEqual(shell.LOG.warn.call_count, 2)
        self.assertEqual(
            shell.LOG.warn.call_args_list[0][0][0][:63],
            'The StackStorm configuration directory permissions are insecure')
        self.assertEqual(
            shell.LOG.warn.call_args_list[1][0][0][:58],
            'The StackStorm configuration file permissions are insecure')

        self.assertEqual(shell.LOG.info.call_count, 2)
        self.assertEqual(
            shell.LOG.info.call_args_list[0][0][0], "The SGID bit is not "
            "set on the StackStorm configuration directory.")

        self.assertEqual(
            shell.LOG.info.call_args_list[1][0][0], 'Skipping parsing CLI config')
Example #30
0
class ShellTestCase(base.BaseCLITestCase):
    capture_output = True

    def __init__(self, *args, **kwargs):
        super(ShellTestCase, self).__init__(*args, **kwargs)
        self.shell = Shell()

    def setUp(self):
        super(ShellTestCase, self).setUp()

        if six.PY3:
            # In python --version outputs to stdout and in 2.x to stderr
            self.version_output = self.stdout
        else:
            self.version_output = self.stderr

    def test_commands_usage_and_help_strings(self):
        # No command, should print out user friendly usage / help string
        self.assertEqual(self.shell.run([]), 2)

        self.stderr.seek(0)
        stderr = self.stderr.read()
        self.assertIn("Usage: ", stderr)
        self.assertIn("For example:", stderr)
        self.assertIn("CLI for StackStorm", stderr)
        self.assertIn("positional arguments:", stderr)

        self.stdout.truncate()
        self.stderr.truncate()

        # --help should result in the same output
        try:
            self.assertEqual(self.shell.run(["--help"]), 0)
        except SystemExit as e:
            self.assertEqual(e.code, 0)

        self.stdout.seek(0)
        stdout = self.stdout.read()
        self.assertIn("Usage: ", stdout)
        self.assertIn("For example:", stdout)
        self.assertIn("CLI for StackStorm", stdout)
        self.assertIn("positional arguments:", stdout)

        self.stdout.truncate()
        self.stderr.truncate()

        # Sub command with no args
        try:
            self.assertEqual(self.shell.run(["action"]), 2)
        except SystemExit as e:
            self.assertEqual(e.code, 2)

        self.stderr.seek(0)
        stderr = self.stderr.read()

        self.assertIn("usage", stderr)

        if six.PY2:
            self.assertIn("{list,get,create,update", stderr)
            self.assertIn("error: too few arguments", stderr)

    def test_endpoints_default(self):
        base_url = "http://127.0.0.1"
        auth_url = "http://127.0.0.1:9100"
        api_url = "http://127.0.0.1:9101/v1"
        stream_url = "http://127.0.0.1:9102/v1"
        args = ["trigger", "list"]
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints["base"], base_url)
        self.assertEqual(client.endpoints["auth"], auth_url)
        self.assertEqual(client.endpoints["api"], api_url)
        self.assertEqual(client.endpoints["stream"], stream_url)

    def test_endpoints_base_url_from_cli(self):
        base_url = "http://www.st2.com"
        auth_url = "http://www.st2.com:9100"
        api_url = "http://www.st2.com:9101/v1"
        stream_url = "http://www.st2.com:9102/v1"
        args = ["--url", base_url, "trigger", "list"]
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints["base"], base_url)
        self.assertEqual(client.endpoints["auth"], auth_url)
        self.assertEqual(client.endpoints["api"], api_url)
        self.assertEqual(client.endpoints["stream"], stream_url)

    def test_endpoints_base_url_from_env(self):
        base_url = "http://www.st2.com"
        auth_url = "http://www.st2.com:9100"
        api_url = "http://www.st2.com:9101/v1"
        stream_url = "http://www.st2.com:9102/v1"
        os.environ["ST2_BASE_URL"] = base_url
        args = ["trigger", "list"]
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints["base"], base_url)
        self.assertEqual(client.endpoints["auth"], auth_url)
        self.assertEqual(client.endpoints["api"], api_url)
        self.assertEqual(client.endpoints["stream"], stream_url)

    def test_endpoints_override_from_cli(self):
        base_url = "http://www.st2.com"
        auth_url = "http://www.st2.com:8888"
        api_url = "http://www.stackstorm1.com:9101/v1"
        stream_url = "http://www.stackstorm1.com:9102/v1"
        args = [
            "--url",
            base_url,
            "--auth-url",
            auth_url,
            "--api-url",
            api_url,
            "--stream-url",
            stream_url,
            "trigger",
            "list",
        ]
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints["base"], base_url)
        self.assertEqual(client.endpoints["auth"], auth_url)
        self.assertEqual(client.endpoints["api"], api_url)
        self.assertEqual(client.endpoints["stream"], stream_url)

    def test_endpoints_override_from_env(self):
        base_url = "http://www.st2.com"
        auth_url = "http://www.st2.com:8888"
        api_url = "http://www.stackstorm1.com:9101/v1"
        stream_url = "http://www.stackstorm1.com:9102/v1"
        os.environ["ST2_BASE_URL"] = base_url
        os.environ["ST2_AUTH_URL"] = auth_url
        os.environ["ST2_API_URL"] = api_url
        os.environ["ST2_STREAM_URL"] = stream_url
        args = ["trigger", "list"]
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints["base"], base_url)
        self.assertEqual(client.endpoints["auth"], auth_url)
        self.assertEqual(client.endpoints["api"], api_url)
        self.assertEqual(client.endpoints["stream"], stream_url)

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(return_value=base.FakeResponse(
            json.dumps(base.RESOURCES), 200, "OK")),
    )
    def test_exit_code_on_success(self):
        argv = ["trigger", "list"]
        self.assertEqual(self.shell.run(argv), 0)

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(return_value=base.FakeResponse(
            None, 500, "INTERNAL SERVER ERROR")),
    )
    def test_exit_code_on_error(self):
        argv = ["trigger", "list"]
        self.assertEqual(self.shell.run(argv), 1)

    def _validate_parser(self, args_list, is_subcommand=True):
        for args in args_list:
            ns = self.shell.parser.parse_args(args)
            func = (
                self.shell.commands[args[0]].run_and_print
                if not is_subcommand else
                self.shell.commands[args[0]].commands[args[1]].run_and_print)
            self.assertEqual(ns.func, func)

    def test_action(self):
        args_list = [
            ["action", "list"],
            ["action", "get", "abc"],
            ["action", "create", "/tmp/action.json"],
            ["action", "update", "123", "/tmp/action.json"],
            ["action", "delete", "abc"],
            ["action", "execute", "-h"],
            ["action", "execute", "remote", "-h"],
            [
                "action",
                "execute",
                "remote",
                "hosts=192.168.1.1",
                "user=st2",
                'cmd="ls -l"',
            ],
            ["action", "execute", "remote-fib", "hosts=192.168.1.1", "3", "8"],
        ]
        self._validate_parser(args_list)

    def test_action_execution(self):
        args_list = [
            ["execution", "list"],
            ["execution", "list", "-a", "all"],
            ["execution", "list", "--attr=all"],
            ["execution", "get", "123"],
            ["execution", "get", "123", "-d"],
            ["execution", "get", "123", "-k", "localhost.stdout"],
            ["execution", "re-run", "123"],
            ["execution", "re-run", "123", "--tasks", "x", "y", "z"],
            [
                "execution", "re-run", "123", "--tasks", "x", "y", "z",
                "--no-reset", "x"
            ],
            ["execution", "re-run", "123", "a=1", "b=x", "c=True"],
            ["execution", "cancel", "123"],
            ["execution", "cancel", "123", "456"],
            ["execution", "pause", "123"],
            ["execution", "pause", "123", "456"],
            ["execution", "resume", "123"],
            ["execution", "resume", "123", "456"],
        ]
        self._validate_parser(args_list)

        # Test mutually exclusive argument groups
        self.assertRaises(
            SystemExit,
            self._validate_parser,
            [["execution", "get", "123", "-d", "-k", "localhost.stdout"]],
        )

    def test_key(self):
        args_list = [
            ["key", "list"],
            ["key", "list", "-n", "2"],
            ["key", "get", "abc"],
            ["key", "set", "abc", "123"],
            ["key", "delete", "abc"],
            ["key", "load", "/tmp/keys.json"],
        ]
        self._validate_parser(args_list)

    def test_policy(self):
        args_list = [
            ["policy", "list"],
            ["policy", "list", "-p", "core"],
            ["policy", "list", "--pack", "core"],
            ["policy", "list", "-r", "core.local"],
            ["policy", "list", "--resource-ref", "core.local"],
            ["policy", "list", "-pt", "action.type1"],
            ["policy", "list", "--policy-type", "action.type1"],
            ["policy", "list", "-r", "core.local", "-pt", "action.type1"],
            [
                "policy",
                "list",
                "--resource-ref",
                "core.local",
                "--policy-type",
                "action.type1",
            ],
            ["policy", "get", "abc"],
            ["policy", "create", "/tmp/policy.json"],
            ["policy", "update", "123", "/tmp/policy.json"],
            ["policy", "delete", "abc"],
        ]
        self._validate_parser(args_list)

    def test_policy_type(self):
        args_list = [
            ["policy-type", "list"],
            ["policy-type", "list", "-r", "action"],
            ["policy-type", "list", "--resource-type", "action"],
            ["policy-type", "get", "abc"],
        ]
        self._validate_parser(args_list)

    def test_pack(self):
        args_list = [
            ["pack", "list"],
            ["pack", "get", "abc"],
            ["pack", "search", "abc"],
            ["pack", "show", "abc"],
            ["pack", "remove", "abc"],
            ["pack", "remove", "abc", "--detail"],
            ["pack", "install", "abc"],
            ["pack", "install", "abc", "--force"],
            ["pack", "install", "abc", "--detail"],
            ["pack", "config", "abc"],
        ]
        self._validate_parser(args_list)

    @mock.patch("st2client.base.ST2_CONFIG_PATH", "/home/does/not/exist")
    def test_print_config_default_config_no_config(self):
        os.environ["ST2_CONFIG_FILE"] = "/home/does/not/exist"
        argv = ["--print-config"]
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertIn("username = None", stdout)
        self.assertIn("cache_token = True", stdout)

    def test_print_config_custom_config_as_env_variable(self):
        os.environ["ST2_CONFIG_FILE"] = CONFIG_FILE_PATH_FULL
        argv = ["--print-config"]
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertIn("username = test1", stdout)
        self.assertIn("cache_token = False", stdout)

    def test_print_config_custom_config_as_command_line_argument(self):
        argv = ["--print-config", "--config-file=%s" % (CONFIG_FILE_PATH_FULL)]
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertIn("username = test1", stdout)
        self.assertIn("cache_token = False", stdout)

    def test_run(self):
        args_list = [
            ["run", "-h"],
            ["run", "abc", "-h"],
            ["run", "remote", "hosts=192.168.1.1", "user=st2", 'cmd="ls -l"'],
            ["run", "remote-fib", "hosts=192.168.1.1", "3", "8"],
        ]
        self._validate_parser(args_list, is_subcommand=False)

    def test_runner(self):
        args_list = [["runner", "list"], ["runner", "get", "abc"]]
        self._validate_parser(args_list)

    def test_rule(self):
        args_list = [
            ["rule", "list"],
            ["rule", "list", "-n", "1"],
            ["rule", "get", "abc"],
            ["rule", "create", "/tmp/rule.json"],
            ["rule", "update", "123", "/tmp/rule.json"],
            ["rule", "delete", "abc"],
        ]
        self._validate_parser(args_list)

    def test_trigger(self):
        args_list = [
            ["trigger", "list"],
            ["trigger", "get", "abc"],
            ["trigger", "create", "/tmp/trigger.json"],
            ["trigger", "update", "123", "/tmp/trigger.json"],
            ["trigger", "delete", "abc"],
        ]
        self._validate_parser(args_list)

    def test_workflow(self):
        args_list = [
            ["workflow", "inspect", "--file", "/path/to/workflow/definition"],
            ["workflow", "inspect", "--action", "mock.foobar"],
        ]

        self._validate_parser(args_list)

    @mock.patch("sys.exit", mock.Mock())
    @mock.patch("st2client.shell.__version__", "v2.8.0")
    def test_get_version_no_package_metadata_file_stable_version(self):
        # stable version, package metadata file doesn't exist on disk - no git revision should be
        # included
        shell = Shell()
        shell.parser.parse_args(args=["--version"])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn("v2.8.0, on Python", stderr)

    @mock.patch("sys.exit", mock.Mock())
    @mock.patch("st2client.shell.__version__", "v2.8.0")
    def test_get_version_package_metadata_file_exists_stable_version(self):
        # stable version, package metadata file exists on disk - no git revision should be included
        package_metadata_path = self._write_mock_package_metadata_file()
        st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path

        shell = Shell()
        shell.run(argv=["--version"])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn("v2.8.0, on Python", stderr)

    @mock.patch("sys.exit", mock.Mock())
    @mock.patch("st2client.shell.__version__", "v2.9dev")
    @mock.patch("st2client.shell.PACKAGE_METADATA_FILE_PATH",
                "/tmp/doesnt/exist.1")
    def test_get_version_no_package_metadata_file_dev_version(self):
        # dev version, package metadata file doesn't exist on disk - no git revision should be
        # included since package metadata file doesn't exist on disk
        shell = Shell()
        shell.parser.parse_args(args=["--version"])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn("v2.9dev, on Python", stderr)

    @mock.patch("sys.exit", mock.Mock())
    @mock.patch("st2client.shell.__version__", "v2.9dev")
    def test_get_version_package_metadata_file_exists_dev_version(self):
        # dev version, package metadata file exists on disk - git revision should be included
        # since package metadata file exists on disk and contains server.git_sha attribute
        package_metadata_path = self._write_mock_package_metadata_file()
        st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path

        shell = Shell()
        shell.parser.parse_args(args=["--version"])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn("v2.9dev (abcdefg), on Python", stderr)

    @mock.patch("locale.getdefaultlocale", mock.Mock(return_value=["en_US"]))
    @mock.patch("locale.getpreferredencoding", mock.Mock(return_value="iso"))
    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(return_value=base.FakeResponse(
            json.dumps(base.RESOURCES), 200, "OK")),
    )
    @mock.patch("st2client.shell.LOGGER")
    def test_non_unicode_encoding_locale_warning_is_printed(self, mock_logger):
        shell = Shell()
        shell.run(argv=["trigger", "list"])

        call_args = mock_logger.warn.call_args[0][0]
        self.assertIn(
            "Locale en_US with encoding iso which is not UTF-8 is used.",
            call_args)

    @mock.patch("locale.getdefaultlocale",
                mock.Mock(side_effect=ValueError("bar")))
    @mock.patch("locale.getpreferredencoding",
                mock.Mock(side_effect=ValueError("bar")))
    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(return_value=base.FakeResponse(
            json.dumps(base.RESOURCES), 200, "OK")),
    )
    @mock.patch("st2client.shell.LOGGER")
    def test_failed_to_get_locale_encoding_warning_is_printed(
            self, mock_logger):
        shell = Shell()
        shell.run(argv=["trigger", "list"])

        call_args = mock_logger.warn.call_args[0][0]
        self.assertTrue(
            "Locale unknown with encoding unknown which is not UTF-8 is used."
            in call_args)

    def _write_mock_package_metadata_file(self):
        _, package_metadata_path = tempfile.mkstemp()

        with open(package_metadata_path, "w") as fp:
            fp.write(MOCK_PACKAGE_METADATA)

        return package_metadata_path

    @unittest2.skipIf(True, "skipping until checks are re-enabled")
    @mock.patch.object(
        requests, "get",
        mock.MagicMock(return_value=base.FakeResponse("{}", 200, "OK")))
    def test_dont_warn_multiple_times(self):
        mock_temp_dir_path = tempfile.mkdtemp()
        mock_config_dir_path = os.path.join(mock_temp_dir_path, "testconfig")
        mock_config_path = os.path.join(mock_config_dir_path, "config")

        # Make the temporary config directory
        os.makedirs(mock_config_dir_path)

        old_perms = os.stat(mock_config_dir_path).st_mode
        new_perms = old_perms | 0o7
        os.chmod(mock_config_dir_path, new_perms)

        # Make the temporary config file
        shutil.copyfile(CONFIG_FILE_PATH_FULL, mock_config_path)
        os.chmod(mock_config_path, 0o777)  # nosec

        shell = Shell()
        shell.LOG = mock.Mock()

        # Test without token.
        shell.run(["--config-file", mock_config_path, "action", "list"])

        self.assertEqual(shell.LOG.warn.call_count, 2)
        self.assertEqual(
            shell.LOG.warn.call_args_list[0][0][0][:63],
            "The StackStorm configuration directory permissions are insecure",
        )
        self.assertEqual(
            shell.LOG.warn.call_args_list[1][0][0][:58],
            "The StackStorm configuration file permissions are insecure",
        )

        self.assertEqual(shell.LOG.info.call_count, 2)
        self.assertEqual(
            shell.LOG.info.call_args_list[0][0][0],
            "The SGID bit is not "
            "set on the StackStorm configuration directory.",
        )

        self.assertEqual(shell.LOG.info.call_args_list[1][0][0],
                         "Skipping parsing CLI config")

    def test_policy_list_with_pack_option(self):
        argv = ["policy", "list", "-p", "test"]
        mock_obj = mock.MagicMock(return_value=base.FakeResponse(
            json.dumps(base.RESOURCES), 200, "OK"))
        with mock.patch.object(httpclient.HTTPClient, "get", mock_obj):
            self.shell.run(argv)
            self.assertEqual(
                mock_obj.mock_calls[0],
                mock.call("/policies/?include_attributes=ref%2Cresource_ref%2C"
                          "policy_type%2Cenabled&pack=test"),
            )
class ActionExecutionTailCommandTestCase(BaseCLITestCase):
    capture_output = True

    def __init__(self, *args, **kwargs):
        super(ActionExecutionTailCommandTestCase, self).__init__(*args, **kwargs)
        self.shell = Shell()

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(
            return_value=base.FakeResponse(
                json.dumps(MOCK_LIVEACTION_1_SUCCEEDED), 200, "OK"
            )
        ),
    )
    def test_tail_simple_execution_already_finished_succeeded(self):
        argv = ["execution", "tail", "idfoo1"]

        self.assertEqual(self.shell.run(argv), 0)
        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()
        self.assertIn("Execution idfoo1 has completed (status=succeeded)", stdout)
        self.assertEqual(stderr, "")

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(
            return_value=base.FakeResponse(
                json.dumps(MOCK_LIVEACTION_2_FAILED), 200, "OK"
            )
        ),
    )
    def test_tail_simple_execution_already_finished_failed(self):
        argv = ["execution", "tail", "idfoo2"]

        self.assertEqual(self.shell.run(argv), 0)
        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()
        self.assertIn("Execution idfoo2 has completed (status=failed)", stdout)
        self.assertEqual(stderr, "")

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(
            return_value=base.FakeResponse(
                json.dumps(MOCK_LIVEACTION_1_RUNNING), 200, "OK"
            )
        ),
    )
    @mock.patch("st2client.client.StreamManager", autospec=True)
    def test_tail_simple_execution_running_no_data_produced(self, mock_stream_manager):
        argv = ["execution", "tail", "idfoo1"]

        MOCK_EVENTS = [MOCK_LIVEACTION_1_SUCCEEDED]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo1 has completed (status=succeeded).
"""
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, "")

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(
            return_value=base.FakeResponse(
                json.dumps(MOCK_LIVEACTION_3_RUNNING), 200, "OK"
            )
        ),
    )
    @mock.patch("st2client.client.StreamManager", autospec=True)
    def test_tail_simple_execution_running_with_data(self, mock_stream_manager):
        argv = ["execution", "tail", "idfoo3"]

        MOCK_EVENTS = [
            MOCK_LIVEACTION_3_RUNNING,
            MOCK_OUTPUT_1,
            MOCK_OUTPUT_2,
            MOCK_LIVEACTION_3_SUCCEDED,
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo3 has started.

line 1
line 2

Execution idfoo3 has completed (status=succeeded).
""".lstrip()
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, "")

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(
            return_value=base.FakeResponse(
                json.dumps(MOCK_LIVEACTION_3_RUNNING), 200, "OK"
            )
        ),
    )
    @mock.patch("st2client.client.StreamManager", autospec=True)
    def test_tail_action_chain_workflow_execution(self, mock_stream_manager):
        argv = ["execution", "tail", "idfoo3"]

        MOCK_EVENTS = [
            # Workflow started running
            MOCK_LIVEACTION_3_RUNNING,
            # Child task 1 started running
            MOCK_LIVEACTION_3_CHILD_1_RUNNING,
            # Output produced by the child task
            MOCK_LIVEACTION_3_CHILD_1_OUTPUT_1,
            MOCK_LIVEACTION_3_CHILD_1_OUTPUT_2,
            # Child task 1 finished
            MOCK_LIVEACTION_3_CHILD_1_SUCCEEDED,
            # Child task 2 started running
            MOCK_LIVEACTION_3_CHILD_2_RUNNING,
            # Output produced by child task
            MOCK_LIVEACTION_3_CHILD_2_OUTPUT_1,
            # Child task 2 finished
            MOCK_LIVEACTION_3_CHILD_2_FAILED,
            # Parent workflow task finished
            MOCK_LIVEACTION_3_SUCCEDED,
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo3 has started.

Child execution (task=task_1) idchild1 has started.

line ac 4
line ac 5

Child execution (task=task_1) idchild1 has finished (status=succeeded).
Child execution (task=task_2) idchild2 has started.

line ac 100

Child execution (task=task_2) idchild2 has finished (status=failed).

Execution idfoo3 has completed (status=succeeded).
""".lstrip()
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, "")

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(
            return_value=base.FakeResponse(
                json.dumps(MOCK_LIVEACTION_4_RUNNING), 200, "OK"
            )
        ),
    )
    @mock.patch("st2client.client.StreamManager", autospec=True)
    def test_tail_orquesta_workflow_execution(self, mock_stream_manager):
        argv = ["execution", "tail", "idfoo4"]

        MOCK_EVENTS = [
            # Workflow started running
            MOCK_LIVEACTION_4_RUNNING,
            # Child task 1 started running
            MOCK_LIVEACTION_4_CHILD_1_RUNNING,
            # Output produced by the child task
            MOCK_LIVEACTION_4_CHILD_1_OUTPUT_1,
            MOCK_LIVEACTION_4_CHILD_1_OUTPUT_2,
            # Child task 1 finished
            MOCK_LIVEACTION_4_CHILD_1_SUCCEEDED,
            # Child task 2 started running
            MOCK_LIVEACTION_4_CHILD_2_RUNNING,
            # Output produced by child task
            MOCK_LIVEACTION_4_CHILD_2_OUTPUT_1,
            # Child task 2 finished
            MOCK_LIVEACTION_4_CHILD_2_TIMED_OUT,
            # Parent workflow task finished
            MOCK_LIVEACTION_4_SUCCEDED,
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo4 has started.

Child execution (task=task_1) idorquestachild1 has started.

line orquesta 4
line orquesta 5

Child execution (task=task_1) idorquestachild1 has finished (status=succeeded).
Child execution (task=task_2) idorquestachild2 has started.

line orquesta 100

Child execution (task=task_2) idorquestachild2 has finished (status=timeout).

Execution idfoo4 has completed (status=succeeded).
""".lstrip()
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, "")

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(
            return_value=base.FakeResponse(
                json.dumps(MOCK_LIVEACTION_4_RUNNING), 200, "OK"
            )
        ),
    )
    @mock.patch("st2client.client.StreamManager", autospec=True)
    def test_tail_double_nested_orquesta_workflow_execution(self, mock_stream_manager):
        argv = ["execution", "tail", "idfoo4"]

        MOCK_EVENTS = [
            # Workflow started running
            MOCK_LIVEACTION_4_RUNNING,
            # Child task 1 started running (sub workflow)
            MOCK_LIVEACTION_4_CHILD_1_RUNNING,
            # Child task 1 started running
            MOCK_LIVEACTION_4_CHILD_1_1_RUNNING,
            # Output produced by the child task
            MOCK_LIVEACTION_4_CHILD_1_1_OUTPUT_1,
            MOCK_LIVEACTION_4_CHILD_1_1_OUTPUT_2,
            # Another execution has started, this output should not be included
            MOCK_LIVEACTION_3_RUNNING,
            # Child task 1 started running
            MOCK_LIVEACTION_3_CHILD_1_RUNNING,
            # Output produced by the child task
            MOCK_LIVEACTION_3_CHILD_1_OUTPUT_1,
            MOCK_LIVEACTION_3_CHILD_1_OUTPUT_2,
            # Child task 1 finished
            MOCK_LIVEACTION_3_CHILD_1_SUCCEEDED,
            # Parent workflow task finished
            MOCK_LIVEACTION_3_SUCCEDED,
            # End another execution
            # Child task 1 has finished
            MOCK_LIVEACTION_4_CHILD_1_1_SUCCEEDED,
            # Child task 1 finished (sub workflow)
            MOCK_LIVEACTION_4_CHILD_1_SUCCEEDED,
            # Child task 2 started running
            MOCK_LIVEACTION_4_CHILD_2_RUNNING,
            # Output produced by child task
            MOCK_LIVEACTION_4_CHILD_2_OUTPUT_1,
            # Child task 2 finished
            MOCK_LIVEACTION_4_CHILD_2_TIMED_OUT,
            # Parent workflow task finished
            MOCK_LIVEACTION_4_SUCCEDED,
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo4 has started.

Child execution (task=task_1) idorquestachild1 has started.

Child execution (task=task_1) idorquestachild1_1 has started.

line orquesta 4
line orquesta 5

Child execution (task=task_1) idorquestachild1_1 has finished (status=succeeded).

Child execution (task=task_1) idorquestachild1 has finished (status=succeeded).
Child execution (task=task_2) idorquestachild2 has started.

line orquesta 100

Child execution (task=task_2) idorquestachild2 has finished (status=timeout).

Execution idfoo4 has completed (status=succeeded).
""".lstrip()

        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, "")

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(
            return_value=base.FakeResponse(
                json.dumps(MOCK_LIVEACTION_4_CHILD_2_RUNNING), 200, "OK"
            )
        ),
    )
    @mock.patch("st2client.client.StreamManager", autospec=True)
    def test_tail_child_execution_directly(self, mock_stream_manager):
        argv = ["execution", "tail", "idfoo4"]

        MOCK_EVENTS = [
            # Child task 2 started running
            MOCK_LIVEACTION_4_CHILD_2_RUNNING,
            # Output produced by child task
            MOCK_LIVEACTION_4_CHILD_2_OUTPUT_1,
            # Other executions should not interfere
            # Child task 1 started running
            MOCK_LIVEACTION_3_CHILD_1_RUNNING,
            # Child task 1 finished (sub workflow)
            MOCK_LIVEACTION_4_CHILD_1_SUCCEEDED,
            # Child task 2 finished
            MOCK_LIVEACTION_4_CHILD_2_TIMED_OUT,
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Child execution (task=task_2) idorquestachild2 has started.

line orquesta 100

Child execution (task=task_2) idorquestachild2 has finished (status=timeout).
""".lstrip()

        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, "")
Example #32
0
class TestShell(base.BaseCLITestCase):
    capture_output = True

    def __init__(self, *args, **kwargs):
        super(TestShell, self).__init__(*args, **kwargs)
        self.shell = Shell()

    def test_endpoints_default(self):
        base_url = 'http://127.0.0.1'
        auth_url = 'http://127.0.0.1:9100'
        api_url = 'http://127.0.0.1:9101/v1'
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)

    def test_endpoints_base_url_from_cli(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:9100'
        api_url = 'http://www.st2.com:9101/v1'
        args = ['--url', base_url, 'trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)

    def test_endpoints_base_url_from_env(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:9100'
        api_url = 'http://www.st2.com:9101/v1'
        os.environ['ST2_BASE_URL'] = base_url
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)

    def test_endpoints_override_from_cli(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:8888'
        api_url = 'http://www.stackstorm1.com:9101/v1'
        args = ['--url', base_url,
                '--auth-url', auth_url,
                '--api-url', api_url,
                'trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)

    def test_endpoints_override_from_env(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:8888'
        api_url = 'http://www.stackstorm1.com:9101/v1'
        os.environ['ST2_BASE_URL'] = base_url
        os.environ['ST2_AUTH_URL'] = auth_url
        os.environ['ST2_API_URL'] = api_url
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(base.RESOURCES), 200, 'OK')))
    def test_exit_code_on_success(self):
        argv = ['trigger', 'list']
        self.assertEqual(self.shell.run(argv), 0)

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(None, 500, 'INTERNAL SERVER ERROR')))
    def test_exit_code_on_error(self):
        argv = ['trigger', 'list']
        self.assertEqual(self.shell.run(argv), 1)

    def _validate_parser(self, args_list, is_subcommand=True):
        for args in args_list:
            ns = self.shell.parser.parse_args(args)
            func = (self.shell.commands[args[0]].run_and_print
                    if not is_subcommand
                    else self.shell.commands[args[0]].commands[args[1]].run_and_print)
            self.assertEqual(ns.func, func)

    def test_action(self):
        args_list = [
            ['action', 'list'],
            ['action', 'get', 'abc'],
            ['action', 'create', '/tmp/action.json'],
            ['action', 'update', '123', '/tmp/action.json'],
            ['action', 'delete', 'abc'],
            ['action', 'execute', '-h'],
            ['action', 'execute', 'remote', '-h'],
            ['action', 'execute', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"'],
            ['action', 'execute', 'remote-fib', 'hosts=192.168.1.1', '3', '8']
        ]
        self._validate_parser(args_list)

    def test_action_execution(self):
        args_list = [
            ['execution', 'list'],
            ['execution', 'get', '123'],
            ['execution', 'get', '123', '-d'],
            ['execution', 'get', '123', '-k', 'localhost.stdout']
        ]
        self._validate_parser(args_list)

        # Test mutually exclusive argument groups
        self.assertRaises(SystemExit, self._validate_parser,
                          [['execution', 'get', '123', '-d', '-k', 'localhost.stdout']])

    def test_key(self):
        args_list = [
            ['key', 'list'],
            ['key', 'get', 'abc'],
            ['key', 'set', 'abc', '123'],
            ['key', 'delete', 'abc'],
            ['key', 'load', '/tmp/keys.json']
        ]
        self._validate_parser(args_list)

    def test_policy(self):
        args_list = [
            ['policy', 'list'],
            ['policy', 'list', '-p', 'core'],
            ['policy', 'list', '--pack', 'core'],
            ['policy', 'list', '-r', 'core.local'],
            ['policy', 'list', '--resource-ref', 'core.local'],
            ['policy', 'list', '-pt', 'action.type1'],
            ['policy', 'list', '--policy-type', 'action.type1'],
            ['policy', 'list', '-r', 'core.local', '-pt', 'action.type1'],
            ['policy', 'list', '--resource-ref', 'core.local', '--policy-type', 'action.type1'],
            ['policy', 'get', 'abc'],
            ['policy', 'create', '/tmp/policy.json'],
            ['policy', 'update', '123', '/tmp/policy.json'],
            ['policy', 'delete', 'abc']
        ]
        self._validate_parser(args_list)

    def test_policy_type(self):
        args_list = [
            ['policy-type', 'list'],
            ['policy-type', 'list', '-r', 'action'],
            ['policy-type', 'list', '--resource-type', 'action'],
            ['policy-type', 'get', 'abc']
        ]
        self._validate_parser(args_list)

    @mock.patch('st2client.shell.ST2_CONFIG_PATH', '/home/does/not/exist')
    def test_print_config_default_config_no_config(self):
        os.environ['ST2_CONFIG_FILE'] = '/home/does/not/exist'
        argv = ['--print-config']
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertTrue('username = None' in stdout)
        self.assertTrue('cache_token = True' in stdout)

    def test_print_config_custom_config_as_env_variable(self):
        os.environ['ST2_CONFIG_FILE'] = CONFIG_FILE_PATH_FULL
        argv = ['--print-config']
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertTrue('username = test1' in stdout)
        self.assertTrue('cache_token = False' in stdout)

    def test_print_config_custom_config_as_command_line_argument(self):
        argv = ['--print-config', '--config-file=%s' % (CONFIG_FILE_PATH_FULL)]
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertTrue('username = test1' in stdout)
        self.assertTrue('cache_token = False' in stdout)

    def test_run(self):
        args_list = [
            ['run', '-h'],
            ['run', 'abc', '-h'],
            ['run', 'remote', 'hosts=192.168.1.1', 'user=st2', 'cmd="ls -l"'],
            ['run', 'remote-fib', 'hosts=192.168.1.1', '3', '8']
        ]
        self._validate_parser(args_list, is_subcommand=False)

    def test_runner(self):
        args_list = [
            ['runner', 'list'],
            ['runner', 'get', 'abc']
        ]
        self._validate_parser(args_list)

    def test_rule(self):
        args_list = [
            ['rule', 'list'],
            ['rule', 'get', 'abc'],
            ['rule', 'create', '/tmp/rule.json'],
            ['rule', 'update', '123', '/tmp/rule.json'],
            ['rule', 'delete', 'abc']
        ]
        self._validate_parser(args_list)

    def test_trigger(self):
        args_list = [
            ['trigger', 'list'],
            ['trigger', 'get', 'abc'],
            ['trigger', 'create', '/tmp/trigger.json'],
            ['trigger', 'update', '123', '/tmp/trigger.json'],
            ['trigger', 'delete', 'abc']
        ]
        self._validate_parser(args_list)
Example #33
0
 def __init__(self, *args, **kwargs):
     super(TestShell, self).__init__(*args, **kwargs)
     self.shell = Shell()
Example #34
0
    def test_non_unicode_encoding_locale_warning_is_printed(self, mock_logger):
        shell = Shell()
        shell.run(argv=['trigger', 'list'])

        call_args = mock_logger.warn.call_args[0][0]
        self.assertTrue('Locale en_US with encoding iso which is not UTF-8 is used.' in call_args)
Example #35
0
 def __init__(self, *args, **kwargs):
     super(ShellTestCase, self).__init__(*args, **kwargs)
     self.shell = Shell()
Example #36
0
class ShellTestCase(base.BaseCLITestCase):
    capture_output = True

    def __init__(self, *args, **kwargs):
        super(ShellTestCase, self).__init__(*args, **kwargs)
        self.shell = Shell()

    def setUp(self):
        super(ShellTestCase, self).setUp()

        if six.PY3:
            # In python --version outputs to stdout and in 2.x to stderr
            self.version_output = self.stdout
        else:
            self.version_output = self.stderr

    def test_commands_usage_and_help_strings(self):
        # No command, should print out user friendly usage / help string
        self.assertEqual(self.shell.run([]), 2)

        self.stderr.seek(0)
        stderr = self.stderr.read()
        self.assertIn('Usage: ', stderr)
        self.assertIn('For example:', stderr)
        self.assertIn('CLI for StackStorm', stderr)
        self.assertIn('positional arguments:', stderr)

        self.stdout.truncate()
        self.stderr.truncate()

        # --help should result in the same output
        try:
            self.assertEqual(self.shell.run(['--help']), 0)
        except SystemExit as e:
            self.assertEqual(e.code, 0)

        self.stdout.seek(0)
        stdout = self.stdout.read()
        self.assertIn('Usage: ', stdout)
        self.assertIn('For example:', stdout)
        self.assertIn('CLI for StackStorm', stdout)
        self.assertIn('positional arguments:', stdout)

        self.stdout.truncate()
        self.stderr.truncate()

        # Sub command with no args
        try:
            self.assertEqual(self.shell.run(['action']), 2)
        except SystemExit as e:
            self.assertEqual(e.code, 2)

        self.stderr.seek(0)
        stderr = self.stderr.read()

        self.assertIn('usage', stderr)

        if six.PY2:
            self.assertIn('{list,get,create,update', stderr)
            self.assertIn('error: too few arguments', stderr)

    def test_endpoints_default(self):
        base_url = 'http://127.0.0.1'
        auth_url = 'http://127.0.0.1:9100'
        api_url = 'http://127.0.0.1:9101/v1'
        stream_url = 'http://127.0.0.1:9102/v1'
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)
        self.assertEqual(client.endpoints['stream'], stream_url)

    def test_endpoints_base_url_from_cli(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:9100'
        api_url = 'http://www.st2.com:9101/v1'
        stream_url = 'http://www.st2.com:9102/v1'
        args = ['--url', base_url, 'trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)
        self.assertEqual(client.endpoints['stream'], stream_url)

    def test_endpoints_base_url_from_env(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:9100'
        api_url = 'http://www.st2.com:9101/v1'
        stream_url = 'http://www.st2.com:9102/v1'
        os.environ['ST2_BASE_URL'] = base_url
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)
        self.assertEqual(client.endpoints['stream'], stream_url)

    def test_endpoints_override_from_cli(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:8888'
        api_url = 'http://www.stackstorm1.com:9101/v1'
        stream_url = 'http://www.stackstorm1.com:9102/v1'
        args = [
            '--url', base_url, '--auth-url', auth_url, '--api-url', api_url,
            '--stream-url', stream_url, 'trigger', 'list'
        ]
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)
        self.assertEqual(client.endpoints['stream'], stream_url)

    def test_endpoints_override_from_env(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:8888'
        api_url = 'http://www.stackstorm1.com:9101/v1'
        stream_url = 'http://www.stackstorm1.com:9102/v1'
        os.environ['ST2_BASE_URL'] = base_url
        os.environ['ST2_AUTH_URL'] = auth_url
        os.environ['ST2_API_URL'] = api_url
        os.environ['ST2_STREAM_URL'] = stream_url
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)
        self.assertEqual(client.endpoints['stream'], stream_url)

    @mock.patch.object(httpclient.HTTPClient, 'get',
                       mock.MagicMock(return_value=base.FakeResponse(
                           json.dumps(base.RESOURCES), 200, 'OK')))
    def test_exit_code_on_success(self):
        argv = ['trigger', 'list']
        self.assertEqual(self.shell.run(argv), 0)

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(
            return_value=base.FakeResponse(None, 500, 'INTERNAL SERVER ERROR'))
    )
    def test_exit_code_on_error(self):
        argv = ['trigger', 'list']
        self.assertEqual(self.shell.run(argv), 1)

    def _validate_parser(self, args_list, is_subcommand=True):
        for args in args_list:
            ns = self.shell.parser.parse_args(args)
            func = (
                self.shell.commands[args[0]].run_and_print
                if not is_subcommand else
                self.shell.commands[args[0]].commands[args[1]].run_and_print)
            self.assertEqual(ns.func, func)

    def test_action(self):
        args_list = [
            ['action', 'list'], ['action', 'get', 'abc'],
            ['action', 'create', '/tmp/action.json'],
            ['action', 'update', '123', '/tmp/action.json'],
            ['action', 'delete', 'abc'], ['action', 'execute', '-h'],
            ['action', 'execute', 'remote', '-h'],
            [
                'action', 'execute', 'remote', 'hosts=192.168.1.1', 'user=st2',
                'cmd="ls -l"'
            ],
            ['action', 'execute', 'remote-fib', 'hosts=192.168.1.1', '3', '8']
        ]
        self._validate_parser(args_list)

    def test_action_execution(self):
        args_list = [['execution', 'list'], ['execution', 'list', '-a', 'all'],
                     ['execution', 'list', '--attr=all'],
                     ['execution', 'get', '123'],
                     ['execution', 'get', '123', '-d'],
                     ['execution', 'get', '123', '-k', 'localhost.stdout'],
                     ['execution', 're-run', '123'],
                     ['execution', 're-run', '123', '--tasks', 'x', 'y', 'z'],
                     [
                         'execution', 're-run', '123', '--tasks', 'x', 'y',
                         'z', '--no-reset', 'x'
                     ], ['execution', 're-run', '123', 'a=1', 'b=x', 'c=True'],
                     ['execution', 'cancel', '123'],
                     ['execution', 'cancel', '123', '456'],
                     ['execution', 'pause', '123'],
                     ['execution', 'pause', '123', '456'],
                     ['execution', 'resume', '123'],
                     ['execution', 'resume', '123', '456']]
        self._validate_parser(args_list)

        # Test mutually exclusive argument groups
        self.assertRaises(
            SystemExit, self._validate_parser,
            [['execution', 'get', '123', '-d', '-k', 'localhost.stdout']])

    def test_key(self):
        args_list = [['key', 'list'], ['key', 'list', '-n', '2'],
                     ['key', 'get', 'abc'], ['key', 'set', 'abc', '123'],
                     ['key', 'delete', 'abc'],
                     ['key', 'load', '/tmp/keys.json']]
        self._validate_parser(args_list)

    def test_policy(self):
        args_list = [
            ['policy', 'list'], ['policy', 'list', '-p', 'core'],
            ['policy', 'list', '--pack', 'core'],
            ['policy', 'list', '-r', 'core.local'],
            ['policy', 'list', '--resource-ref', 'core.local'],
            ['policy', 'list', '-pt', 'action.type1'],
            ['policy', 'list', '--policy-type', 'action.type1'],
            ['policy', 'list', '-r', 'core.local', '-pt', 'action.type1'],
            [
                'policy', 'list', '--resource-ref', 'core.local',
                '--policy-type', 'action.type1'
            ], ['policy', 'get', 'abc'],
            ['policy', 'create', '/tmp/policy.json'],
            ['policy', 'update', '123', '/tmp/policy.json'],
            ['policy', 'delete', 'abc']
        ]
        self._validate_parser(args_list)

    def test_policy_type(self):
        args_list = [['policy-type', 'list'],
                     ['policy-type', 'list', '-r', 'action'],
                     ['policy-type', 'list', '--resource-type', 'action'],
                     ['policy-type', 'get', 'abc']]
        self._validate_parser(args_list)

    def test_pack(self):
        args_list = [['pack', 'list'], ['pack', 'get', 'abc'],
                     ['pack', 'search', 'abc'], ['pack', 'show', 'abc'],
                     ['pack', 'remove', 'abc'],
                     ['pack', 'remove', 'abc', '--detail'],
                     ['pack', 'install', 'abc'],
                     ['pack', 'install', 'abc', '--force'],
                     ['pack', 'install', 'abc', '--detail'],
                     ['pack', 'config', 'abc']]
        self._validate_parser(args_list)

    @mock.patch('st2client.base.ST2_CONFIG_PATH', '/home/does/not/exist')
    def test_print_config_default_config_no_config(self):
        os.environ['ST2_CONFIG_FILE'] = '/home/does/not/exist'
        argv = ['--print-config']
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertIn('username = None', stdout)
        self.assertIn('cache_token = True', stdout)

    def test_print_config_custom_config_as_env_variable(self):
        os.environ['ST2_CONFIG_FILE'] = CONFIG_FILE_PATH_FULL
        argv = ['--print-config']
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertIn('username = test1', stdout)
        self.assertIn('cache_token = False', stdout)

    def test_print_config_custom_config_as_command_line_argument(self):
        argv = ['--print-config', '--config-file=%s' % (CONFIG_FILE_PATH_FULL)]
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertIn('username = test1', stdout)
        self.assertIn('cache_token = False', stdout)

    def test_run(self):
        args_list = [['run', '-h'], ['run', 'abc', '-h'],
                     [
                         'run', 'remote', 'hosts=192.168.1.1', 'user=st2',
                         'cmd="ls -l"'
                     ], ['run', 'remote-fib', 'hosts=192.168.1.1', '3', '8']]
        self._validate_parser(args_list, is_subcommand=False)

    def test_runner(self):
        args_list = [['runner', 'list'], ['runner', 'get', 'abc']]
        self._validate_parser(args_list)

    def test_rule(self):
        args_list = [['rule', 'list'], ['rule', 'list', '-n', '1'],
                     ['rule', 'get', 'abc'],
                     ['rule', 'create', '/tmp/rule.json'],
                     ['rule', 'update', '123', '/tmp/rule.json'],
                     ['rule', 'delete', 'abc']]
        self._validate_parser(args_list)

    def test_trigger(self):
        args_list = [['trigger', 'list'], ['trigger', 'get', 'abc'],
                     ['trigger', 'create', '/tmp/trigger.json'],
                     ['trigger', 'update', '123', '/tmp/trigger.json'],
                     ['trigger', 'delete', 'abc']]
        self._validate_parser(args_list)

    def test_workflow(self):
        args_list = [[
            'workflow', 'inspect', '--file', '/path/to/workflow/definition'
        ], ['workflow', 'inspect', '--action', 'mock.foobar']]

        self._validate_parser(args_list)

    @mock.patch('sys.exit', mock.Mock())
    @mock.patch('st2client.shell.__version__', 'v2.8.0')
    def test_get_version_no_package_metadata_file_stable_version(self):
        # stable version, package metadata file doesn't exist on disk - no git revision should be
        # included
        shell = Shell()
        shell.parser.parse_args(args=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn('v2.8.0, on Python', stderr)

    @mock.patch('sys.exit', mock.Mock())
    @mock.patch('st2client.shell.__version__', 'v2.8.0')
    def test_get_version_package_metadata_file_exists_stable_version(self):
        # stable version, package metadata file exists on disk - no git revision should be included
        package_metadata_path = self._write_mock_package_metadata_file()
        st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path

        shell = Shell()
        shell.run(argv=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn('v2.8.0, on Python', stderr)

    @mock.patch('sys.exit', mock.Mock())
    @mock.patch('st2client.shell.__version__', 'v2.9dev')
    @mock.patch('st2client.shell.PACKAGE_METADATA_FILE_PATH',
                '/tmp/doesnt/exist.1')
    def test_get_version_no_package_metadata_file_dev_version(self):
        # dev version, package metadata file doesn't exist on disk - no git revision should be
        # included since package metadata file doesn't exist on disk
        shell = Shell()
        shell.parser.parse_args(args=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn('v2.9dev, on Python', stderr)

    @mock.patch('sys.exit', mock.Mock())
    @mock.patch('st2client.shell.__version__', 'v2.9dev')
    def test_get_version_package_metadata_file_exists_dev_version(self):
        # dev version, package metadata file exists on disk - git revision should be included
        # since package metadata file exists on disk and contains server.git_sha attribute
        package_metadata_path = self._write_mock_package_metadata_file()
        st2client.shell.PACKAGE_METADATA_FILE_PATH = package_metadata_path

        shell = Shell()
        shell.parser.parse_args(args=['--version'])

        self.version_output.seek(0)
        stderr = self.version_output.read()
        self.assertIn('v2.9dev (abcdefg), on Python', stderr)

    @mock.patch('locale.getdefaultlocale', mock.Mock(return_value=['en_US']))
    @mock.patch('locale.getpreferredencoding', mock.Mock(return_value='iso'))
    @mock.patch.object(httpclient.HTTPClient, 'get',
                       mock.MagicMock(return_value=base.FakeResponse(
                           json.dumps(base.RESOURCES), 200, 'OK')))
    @mock.patch('st2client.shell.LOGGER')
    def test_non_unicode_encoding_locale_warning_is_printed(self, mock_logger):
        shell = Shell()
        shell.run(argv=['trigger', 'list'])

        call_args = mock_logger.warn.call_args[0][0]
        self.assertIn(
            'Locale en_US with encoding iso which is not UTF-8 is used.',
            call_args)

    @mock.patch('locale.getdefaultlocale',
                mock.Mock(side_effect=ValueError('bar')))
    @mock.patch('locale.getpreferredencoding',
                mock.Mock(side_effect=ValueError('bar')))
    @mock.patch.object(httpclient.HTTPClient, 'get',
                       mock.MagicMock(return_value=base.FakeResponse(
                           json.dumps(base.RESOURCES), 200, 'OK')))
    @mock.patch('st2client.shell.LOGGER')
    def test_failed_to_get_locale_encoding_warning_is_printed(
            self, mock_logger):
        shell = Shell()
        shell.run(argv=['trigger', 'list'])

        call_args = mock_logger.warn.call_args[0][0]
        self.assertTrue(
            'Locale unknown with encoding unknown which is not UTF-8 is used.'
            in call_args)

    def _write_mock_package_metadata_file(self):
        _, package_metadata_path = tempfile.mkstemp()

        with open(package_metadata_path, 'w') as fp:
            fp.write(MOCK_PACKAGE_METADATA)

        return package_metadata_path

    @unittest2.skipIf(True, 'skipping until checks are re-enabled')
    @mock.patch.object(
        requests, 'get',
        mock.MagicMock(return_value=base.FakeResponse("{}", 200, 'OK')))
    def test_dont_warn_multiple_times(self):
        mock_temp_dir_path = tempfile.mkdtemp()
        mock_config_dir_path = os.path.join(mock_temp_dir_path, 'testconfig')
        mock_config_path = os.path.join(mock_config_dir_path, 'config')

        # Make the temporary config directory
        os.makedirs(mock_config_dir_path)

        old_perms = os.stat(mock_config_dir_path).st_mode
        new_perms = old_perms | 0o7
        os.chmod(mock_config_dir_path, new_perms)

        # Make the temporary config file
        shutil.copyfile(CONFIG_FILE_PATH_FULL, mock_config_path)
        os.chmod(mock_config_path, 0o777)  # nosec

        shell = Shell()
        shell.LOG = mock.Mock()

        # Test without token.
        shell.run(['--config-file', mock_config_path, 'action', 'list'])

        self.assertEqual(shell.LOG.warn.call_count, 2)
        self.assertEqual(
            shell.LOG.warn.call_args_list[0][0][0][:63],
            'The StackStorm configuration directory permissions are insecure')
        self.assertEqual(
            shell.LOG.warn.call_args_list[1][0][0][:58],
            'The StackStorm configuration file permissions are insecure')

        self.assertEqual(shell.LOG.info.call_count, 2)
        self.assertEqual(
            shell.LOG.info.call_args_list[0][0][0], "The SGID bit is not "
            "set on the StackStorm configuration directory.")

        self.assertEqual(shell.LOG.info.call_args_list[1][0][0],
                         'Skipping parsing CLI config')
Example #37
0
class ActionExecutionTailCommandTestCase(BaseCLITestCase):
    capture_output = True

    def __init__(self, *args, **kwargs):
        super(ActionExecutionTailCommandTestCase,
              self).__init__(*args, **kwargs)
        self.shell = Shell()

    @mock.patch.object(httpclient.HTTPClient, 'get',
                       mock.MagicMock(return_value=base.FakeResponse(
                           json.dumps(MOCK_LIVEACTION_1_SUCCEEDED), 200, 'OK'))
                       )
    def test_tail_simple_execution_already_finished_succeeded(self):
        argv = ['execution', 'tail', 'idfoo1']

        self.assertEqual(self.shell.run(argv), 0)
        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()
        self.assertTrue(
            'Execution idfoo1 has completed (status=succeeded)' in stdout)
        self.assertEqual(stderr, '')

    @mock.patch.object(httpclient.HTTPClient, 'get',
                       mock.MagicMock(return_value=base.FakeResponse(
                           json.dumps(MOCK_LIVEACTION_2_FAILED), 200, 'OK')))
    def test_tail_simple_execution_already_finished_failed(self):
        argv = ['execution', 'tail', 'idfoo2']

        self.assertEqual(self.shell.run(argv), 0)
        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()
        self.assertTrue(
            'Execution idfoo2 has completed (status=failed)' in stdout)
        self.assertEqual(stderr, '')

    @mock.patch.object(httpclient.HTTPClient, 'get',
                       mock.MagicMock(return_value=base.FakeResponse(
                           json.dumps(MOCK_LIVEACTION_1_RUNNING), 200, 'OK')))
    @mock.patch('st2client.client.StreamManager', autospec=True)
    def test_tail_simple_execution_running_no_data_produced(
            self, mock_stream_manager):
        argv = ['execution', 'tail', 'idfoo1']

        MOCK_EVENTS = [MOCK_LIVEACTION_1_SUCCEEDED]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo1 has completed (status=succeeded).
"""
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, '')

    @mock.patch.object(httpclient.HTTPClient, 'get',
                       mock.MagicMock(return_value=base.FakeResponse(
                           json.dumps(MOCK_LIVEACTION_3_RUNNING), 200, 'OK')))
    @mock.patch('st2client.client.StreamManager', autospec=True)
    def test_tail_simple_execution_running_with_data(self,
                                                     mock_stream_manager):
        argv = ['execution', 'tail', 'idfoo3']

        MOCK_EVENTS = [
            MOCK_LIVEACTION_3_RUNNING, MOCK_OUTPUT_1, MOCK_OUTPUT_2,
            MOCK_LIVEACTION_3_SUCCEDED
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
line 1
line 2

Execution idfoo3 has completed (status=succeeded).
""".lstrip()
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, '')

    @mock.patch.object(httpclient.HTTPClient, 'get',
                       mock.MagicMock(return_value=base.FakeResponse(
                           json.dumps(MOCK_LIVEACTION_3_RUNNING), 200, 'OK')))
    @mock.patch('st2client.client.StreamManager', autospec=True)
    def test_tail_action_chain_workflow_execution(self, mock_stream_manager):
        argv = ['execution', 'tail', 'idfoo3']

        MOCK_EVENTS = [
            # Workflow started running
            MOCK_LIVEACTION_3_RUNNING,

            # Child task 1 started running
            MOCK_LIVEACTION_3_CHILD_1_RUNNING,

            # Output produced by the child task
            MOCK_LIVEACTION_3_CHILD_1_OUTPUT_1,
            MOCK_LIVEACTION_3_CHILD_1_OUTPUT_2,

            # Child task 1 finished
            MOCK_LIVEACTION_3_CHILD_1_SUCCEEDED,

            # Child task 2 started running
            MOCK_LIVEACTION_3_CHILD_2_RUNNING,

            # Output produced by child task
            MOCK_LIVEACTION_3_CHILD_2_OUTPUT_1,

            # Child task 2 finished
            MOCK_LIVEACTION_3_CHILD_2_FAILED,

            # Parent workflow task finished
            MOCK_LIVEACTION_3_SUCCEDED
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Child execution (task=task_1) idchild1 has started.

line ac 4
line ac 5

Child execution (task=task_1) idchild1 has finished (status=succeeded).
Child execution (task=task_2) idchild2 has started.

line ac 100

Child execution (task=task_2) idchild2 has finished (status=failed).

Execution idfoo3 has completed (status=succeeded).
""".lstrip()
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, '')

    @mock.patch.object(httpclient.HTTPClient, 'get',
                       mock.MagicMock(return_value=base.FakeResponse(
                           json.dumps(MOCK_LIVEACTION_4_RUNNING), 200, 'OK')))
    @mock.patch('st2client.client.StreamManager', autospec=True)
    def test_tail_mistral_workflow_execution(self, mock_stream_manager):
        argv = ['execution', 'tail', 'idfoo4']

        MOCK_EVENTS = [
            # Workflow started running
            MOCK_LIVEACTION_4_RUNNING,

            # Child task 1 started running
            MOCK_LIVEACTION_4_CHILD_1_RUNNING,

            # Output produced by the child task
            MOCK_LIVEACTION_4_CHILD_1_OUTPUT_1,
            MOCK_LIVEACTION_4_CHILD_1_OUTPUT_2,

            # Child task 1 finished
            MOCK_LIVEACTION_4_CHILD_1_SUCCEEDED,

            # Child task 2 started running
            MOCK_LIVEACTION_4_CHILD_2_RUNNING,

            # Output produced by child task
            MOCK_LIVEACTION_4_CHILD_2_OUTPUT_1,

            # Child task 2 finished
            MOCK_LIVEACTION_4_CHILD_2_TIMED_OUT,

            # Parent workflow task finished
            MOCK_LIVEACTION_4_SUCCEDED
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Child execution (task=task_1) idmistralchild1 has started.

line mistral 4
line mistral 5

Child execution (task=task_1) idmistralchild1 has finished (status=succeeded).
Child execution (task=task_2) idmistralchild2 has started.

line mistral 100

Child execution (task=task_2) idmistralchild2 has finished (status=timeout).

Execution idfoo4 has completed (status=succeeded).
""".lstrip()
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, '')
Example #38
0
class TestShell(base.BaseCLITestCase):
    capture_output = True

    def __init__(self, *args, **kwargs):
        super(TestShell, self).__init__(*args, **kwargs)
        self.shell = Shell()

    def test_endpoints_default(self):
        base_url = 'http://127.0.0.1'
        auth_url = 'http://127.0.0.1:9100'
        api_url = 'http://127.0.0.1:9101/v1'
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)

    def test_endpoints_base_url_from_cli(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:9100'
        api_url = 'http://www.st2.com:9101/v1'
        args = ['--url', base_url, 'trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)

    def test_endpoints_base_url_from_env(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:9100'
        api_url = 'http://www.st2.com:9101/v1'
        os.environ['ST2_BASE_URL'] = base_url
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)

    def test_endpoints_override_from_cli(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:8888'
        api_url = 'http://www.stackstorm1.com:9101/v1'
        args = [
            '--url', base_url, '--auth-url', auth_url, '--api-url', api_url,
            'trigger', 'list'
        ]
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)

    def test_endpoints_override_from_env(self):
        base_url = 'http://www.st2.com'
        auth_url = 'http://www.st2.com:8888'
        api_url = 'http://www.stackstorm1.com:9101/v1'
        os.environ['ST2_BASE_URL'] = base_url
        os.environ['ST2_AUTH_URL'] = auth_url
        os.environ['ST2_API_URL'] = api_url
        args = ['trigger', 'list']
        parsed_args = self.shell.parser.parse_args(args)
        client = self.shell.get_client(parsed_args)
        self.assertEqual(client.endpoints['base'], base_url)
        self.assertEqual(client.endpoints['auth'], auth_url)
        self.assertEqual(client.endpoints['api'], api_url)

    @mock.patch.object(httpclient.HTTPClient, 'get',
                       mock.MagicMock(return_value=base.FakeResponse(
                           json.dumps(base.RESOURCES), 200, 'OK')))
    def test_exit_code_on_success(self):
        argv = ['trigger', 'list']
        self.assertEqual(self.shell.run(argv), 0)

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(
            return_value=base.FakeResponse(None, 500, 'INTERNAL SERVER ERROR'))
    )
    def test_exit_code_on_error(self):
        argv = ['trigger', 'list']
        self.assertEqual(self.shell.run(argv), 1)

    def _validate_parser(self, args_list, is_subcommand=True):
        for args in args_list:
            ns = self.shell.parser.parse_args(args)
            func = (
                self.shell.commands[args[0]].run_and_print
                if not is_subcommand else
                self.shell.commands[args[0]].commands[args[1]].run_and_print)
            self.assertEqual(ns.func, func)

    def test_action(self):
        args_list = [
            ['action', 'list'], ['action', 'get', 'abc'],
            ['action', 'create', '/tmp/action.json'],
            ['action', 'update', '123', '/tmp/action.json'],
            ['action', 'delete', 'abc'], ['action', 'execute', '-h'],
            ['action', 'execute', 'remote', '-h'],
            [
                'action', 'execute', 'remote', 'hosts=192.168.1.1', 'user=st2',
                'cmd="ls -l"'
            ],
            ['action', 'execute', 'remote-fib', 'hosts=192.168.1.1', '3', '8']
        ]
        self._validate_parser(args_list)

    def test_action_execution(self):
        args_list = [['execution', 'list'], ['execution', 'get', '123'],
                     ['execution', 'get', '123', '-d'],
                     ['execution', 'get', '123', '-k', 'localhost.stdout'],
                     ['execution', 're-run', '123'],
                     ['execution', 're-run', '123', '--tasks', 'x', 'y', 'z'],
                     [
                         'execution', 're-run', '123', '--tasks', 'x', 'y',
                         'z', '--no-reset', 'x'
                     ], ['execution', 're-run', '123', 'a=1', 'b=x', 'c=True']]
        self._validate_parser(args_list)

        # Test mutually exclusive argument groups
        self.assertRaises(
            SystemExit, self._validate_parser,
            [['execution', 'get', '123', '-d', '-k', 'localhost.stdout']])

    def test_key(self):
        args_list = [['key', 'list'], ['key', 'get', 'abc'],
                     ['key', 'set', 'abc', '123'], ['key', 'delete', 'abc'],
                     ['key', 'load', '/tmp/keys.json']]
        self._validate_parser(args_list)

    def test_policy(self):
        args_list = [
            ['policy', 'list'], ['policy', 'list', '-p', 'core'],
            ['policy', 'list', '--pack', 'core'],
            ['policy', 'list', '-r', 'core.local'],
            ['policy', 'list', '--resource-ref', 'core.local'],
            ['policy', 'list', '-pt', 'action.type1'],
            ['policy', 'list', '--policy-type', 'action.type1'],
            ['policy', 'list', '-r', 'core.local', '-pt', 'action.type1'],
            [
                'policy', 'list', '--resource-ref', 'core.local',
                '--policy-type', 'action.type1'
            ], ['policy', 'get', 'abc'],
            ['policy', 'create', '/tmp/policy.json'],
            ['policy', 'update', '123', '/tmp/policy.json'],
            ['policy', 'delete', 'abc']
        ]
        self._validate_parser(args_list)

    def test_policy_type(self):
        args_list = [['policy-type', 'list'],
                     ['policy-type', 'list', '-r', 'action'],
                     ['policy-type', 'list', '--resource-type', 'action'],
                     ['policy-type', 'get', 'abc']]
        self._validate_parser(args_list)

    @mock.patch('st2client.shell.ST2_CONFIG_PATH', '/home/does/not/exist')
    def test_print_config_default_config_no_config(self):
        os.environ['ST2_CONFIG_FILE'] = '/home/does/not/exist'
        argv = ['--print-config']
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertTrue('username = None' in stdout)
        self.assertTrue('cache_token = True' in stdout)

    def test_print_config_custom_config_as_env_variable(self):
        os.environ['ST2_CONFIG_FILE'] = CONFIG_FILE_PATH_FULL
        argv = ['--print-config']
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertTrue('username = test1' in stdout)
        self.assertTrue('cache_token = False' in stdout)

    def test_print_config_custom_config_as_command_line_argument(self):
        argv = ['--print-config', '--config-file=%s' % (CONFIG_FILE_PATH_FULL)]
        self.assertEqual(self.shell.run(argv), 3)

        self.stdout.seek(0)
        stdout = self.stdout.read()

        self.assertTrue('username = test1' in stdout)
        self.assertTrue('cache_token = False' in stdout)

    def test_run(self):
        args_list = [['run', '-h'], ['run', 'abc', '-h'],
                     [
                         'run', 'remote', 'hosts=192.168.1.1', 'user=st2',
                         'cmd="ls -l"'
                     ], ['run', 'remote-fib', 'hosts=192.168.1.1', '3', '8']]
        self._validate_parser(args_list, is_subcommand=False)

    def test_runner(self):
        args_list = [['runner', 'list'], ['runner', 'get', 'abc']]
        self._validate_parser(args_list)

    def test_rule(self):
        args_list = [['rule', 'list'], ['rule', 'get', 'abc'],
                     ['rule', 'create', '/tmp/rule.json'],
                     ['rule', 'update', '123', '/tmp/rule.json'],
                     ['rule', 'delete', 'abc']]
        self._validate_parser(args_list)

    def test_trigger(self):
        args_list = [['trigger', 'list'], ['trigger', 'get', 'abc'],
                     ['trigger', 'create', '/tmp/trigger.json'],
                     ['trigger', 'update', '123', '/tmp/trigger.json'],
                     ['trigger', 'delete', 'abc']]
        self._validate_parser(args_list)
Example #39
0
 def __init__(self, *args, **kwargs):
     super(ShellTestCase, self).__init__(*args, **kwargs)
     self.shell = Shell()
Example #40
0
class TestCommands(base.BaseCLITestCase):
    def __init__(self, *args, **kwargs):
        super(TestCommands, self).__init__(*args, **kwargs)
        self.shell = Shell()

    @mock.patch.object(
        httpclient.HTTPClient,
        "get",
        mock.MagicMock(
            return_value=base.FakeResponse(json.dumps({}), 404, "NOT FOUND")),
    )
    def test_all_resources_get_multi(self):
        # 1. Verify that st2 <resource> get <id 1> ... <id n> notation works for all get commands
        resources = [
            ("action", models.Action),
            ("action-alias", models.ActionAlias),
            ("rule", models.Rule),
            ("sensor", models.Sensor),
            ("pack", models.Pack),
            ("execution", models.Execution),
            ("key", models.KeyValuePair),
            ("webhook", models.Webhook),
            ("trigger-instance", models.TriggerInstance),
            ("trigger", models.TriggerType),
            ("apikey", models.ApiKey),
            ("inquiry", models.Inquiry),
            ("policy", models.Policy),
            ("policy-type", models.PolicyType),
            ("timer", models.Timer),
            ("trace", models.Trace),
            ("runner", models.RunnerType),
            ("rule-enforcement", models.RuleEnforcement),
            ("role", models.Role),
            ("role-assignment", models.UserRoleAssignment),
        ]

        for command_name, resource_ in resources:
            display_name = resource_.get_display_name()

            self._reset_output_streams()
            return_code = self.shell.run(
                [command_name, "get", "id1", "id2", "id3"])
            self.assertEqual(return_code, 0)

            stdout = self.stdout.getvalue()
            self.assertTrue('%s "id1" is not found.' %
                            (display_name) in stdout)
            self.assertTrue('%s "id2" is not found.' %
                            (display_name) in stdout)
            self.assertTrue('%s "id3" is not found.' %
                            (display_name) in stdout)
            self._reset_output_streams()

        # 2. When a single id is provided, command should return non-zero in case resource is not
        # found
        for command_name, resource_ in resources:
            display_name = resource_.get_display_name()

            self._reset_output_streams()
            return_code = self.shell.run([command_name, "get", "id3"])
            self.assertEqual(return_code, 1)

            stdout = self.stdout.getvalue()
            self.assertTrue('%s "id3" is not found.' %
                            (display_name) in stdout)
            self._reset_output_streams()
 def __init__(self, *args, **kwargs):
     super(ActionExecutionTailCommandTestCase, self).__init__(*args, **kwargs)
     self.shell = Shell()
class ActionExecutionTailCommandTestCase(BaseCLITestCase):
    capture_output = True

    def __init__(self, *args, **kwargs):
        super(ActionExecutionTailCommandTestCase, self).__init__(*args, **kwargs)
        self.shell = Shell()

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(MOCK_LIVEACTION_1_SUCCEEDED),
                                                      200, 'OK')))
    def test_tail_simple_execution_already_finished_succeeded(self):
        argv = ['execution', 'tail', 'idfoo1']

        self.assertEqual(self.shell.run(argv), 0)
        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()
        self.assertTrue('Execution idfoo1 has completed (status=succeeded)' in stdout)
        self.assertEqual(stderr, '')

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(MOCK_LIVEACTION_2_FAILED),
                                                      200, 'OK')))
    def test_tail_simple_execution_already_finished_failed(self):
        argv = ['execution', 'tail', 'idfoo2']

        self.assertEqual(self.shell.run(argv), 0)
        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()
        self.assertTrue('Execution idfoo2 has completed (status=failed)' in stdout)
        self.assertEqual(stderr, '')

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(MOCK_LIVEACTION_1_RUNNING),
                                                      200, 'OK')))
    @mock.patch('st2client.client.StreamManager', autospec=True)
    def test_tail_simple_execution_running_no_data_produced(self, mock_stream_manager):
        argv = ['execution', 'tail', 'idfoo1']

        MOCK_EVENTS = [
            MOCK_LIVEACTION_1_SUCCEEDED
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo1 has completed (status=succeeded).
"""
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, '')

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(MOCK_LIVEACTION_3_RUNNING),
                                                      200, 'OK')))
    @mock.patch('st2client.client.StreamManager', autospec=True)
    def test_tail_simple_execution_running_with_data(self, mock_stream_manager):
        argv = ['execution', 'tail', 'idfoo3']

        MOCK_EVENTS = [
            MOCK_LIVEACTION_3_RUNNING,
            MOCK_OUTPUT_1,
            MOCK_OUTPUT_2,
            MOCK_LIVEACTION_3_SUCCEDED
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo3 has started.

line 1
line 2

Execution idfoo3 has completed (status=succeeded).
""".lstrip()
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, '')

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(MOCK_LIVEACTION_3_RUNNING),
                                                      200, 'OK')))
    @mock.patch('st2client.client.StreamManager', autospec=True)
    def test_tail_action_chain_workflow_execution(self, mock_stream_manager):
        argv = ['execution', 'tail', 'idfoo3']

        MOCK_EVENTS = [
            # Workflow started running
            MOCK_LIVEACTION_3_RUNNING,

            # Child task 1 started running
            MOCK_LIVEACTION_3_CHILD_1_RUNNING,

            # Output produced by the child task
            MOCK_LIVEACTION_3_CHILD_1_OUTPUT_1,
            MOCK_LIVEACTION_3_CHILD_1_OUTPUT_2,

            # Child task 1 finished
            MOCK_LIVEACTION_3_CHILD_1_SUCCEEDED,

            # Child task 2 started running
            MOCK_LIVEACTION_3_CHILD_2_RUNNING,

            # Output produced by child task
            MOCK_LIVEACTION_3_CHILD_2_OUTPUT_1,

            # Child task 2 finished
            MOCK_LIVEACTION_3_CHILD_2_FAILED,

            # Parent workflow task finished
            MOCK_LIVEACTION_3_SUCCEDED
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo3 has started.

Child execution (task=task_1) idchild1 has started.

line ac 4
line ac 5

Child execution (task=task_1) idchild1 has finished (status=succeeded).
Child execution (task=task_2) idchild2 has started.

line ac 100

Child execution (task=task_2) idchild2 has finished (status=failed).

Execution idfoo3 has completed (status=succeeded).
""".lstrip()
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, '')

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(MOCK_LIVEACTION_4_RUNNING),
                                                     200, 'OK')))
    @mock.patch('st2client.client.StreamManager', autospec=True)
    def test_tail_mistral_workflow_execution(self, mock_stream_manager):
        argv = ['execution', 'tail', 'idfoo4']

        MOCK_EVENTS = [
            # Workflow started running
            MOCK_LIVEACTION_4_RUNNING,

            # Child task 1 started running
            MOCK_LIVEACTION_4_CHILD_1_RUNNING,

            # Output produced by the child task
            MOCK_LIVEACTION_4_CHILD_1_OUTPUT_1,
            MOCK_LIVEACTION_4_CHILD_1_OUTPUT_2,

            # Child task 1 finished
            MOCK_LIVEACTION_4_CHILD_1_SUCCEEDED,

            # Child task 2 started running
            MOCK_LIVEACTION_4_CHILD_2_RUNNING,

            # Output produced by child task
            MOCK_LIVEACTION_4_CHILD_2_OUTPUT_1,

            # Child task 2 finished
            MOCK_LIVEACTION_4_CHILD_2_TIMED_OUT,

            # Parent workflow task finished
            MOCK_LIVEACTION_4_SUCCEDED
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo4 has started.

Child execution (task=task_1) idmistralchild1 has started.

line mistral 4
line mistral 5

Child execution (task=task_1) idmistralchild1 has finished (status=succeeded).
Child execution (task=task_2) idmistralchild2 has started.

line mistral 100

Child execution (task=task_2) idmistralchild2 has finished (status=timeout).

Execution idfoo4 has completed (status=succeeded).
""".lstrip()
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, '')

    @mock.patch.object(
        httpclient.HTTPClient, 'get',
        mock.MagicMock(return_value=base.FakeResponse(json.dumps(MOCK_LIVEACTION_4_RUNNING),
                                                     200, 'OK')))
    @mock.patch('st2client.client.StreamManager', autospec=True)
    def test_tail_double_nested_mistral_workflow_execution(self, mock_stream_manager):
        argv = ['execution', 'tail', 'idfoo4']

        MOCK_EVENTS = [
            # Workflow started running
            MOCK_LIVEACTION_4_RUNNING,

            # Child task 1 started running (sub workflow)
            MOCK_LIVEACTION_4_CHILD_1_RUNNING,

            # Child task 1 started running
            MOCK_LIVEACTION_4_CHILD_1_1_RUNNING,

            # Output produced by the child task
            MOCK_LIVEACTION_4_CHILD_1_1_OUTPUT_1,
            MOCK_LIVEACTION_4_CHILD_1_1_OUTPUT_2,

            # Child task 1 has finished
            MOCK_LIVEACTION_4_CHILD_1_1_SUCCEEDED,

            # Child task 1 finished (sub workflow)
            MOCK_LIVEACTION_4_CHILD_1_SUCCEEDED,

            # Child task 2 started running
            MOCK_LIVEACTION_4_CHILD_2_RUNNING,

            # Output produced by child task
            MOCK_LIVEACTION_4_CHILD_2_OUTPUT_1,

            # Child task 2 finished
            MOCK_LIVEACTION_4_CHILD_2_TIMED_OUT,

            # Parent workflow task finished
            MOCK_LIVEACTION_4_SUCCEDED
        ]

        mock_cls = mock.Mock()
        mock_cls.listen = mock.Mock()
        mock_listen_generator = mock.Mock()
        mock_listen_generator.return_value = MOCK_EVENTS
        mock_cls.listen.side_effect = mock_listen_generator
        mock_stream_manager.return_value = mock_cls

        self.assertEqual(self.shell.run(argv), 0)
        self.assertEqual(mock_listen_generator.call_count, 1)

        stdout = self.stdout.getvalue()
        stderr = self.stderr.getvalue()

        expected_result = """
Execution idfoo4 has started.

Child execution (task=task_1) idmistralchild1 has started.

Child execution (task=task_1) idmistralchild1_1 has started.

line mistral 4
line mistral 5

Child execution (task=task_1) idmistralchild1_1 has finished (status=succeeded).

Child execution (task=task_1) idmistralchild1 has finished (status=succeeded).
Child execution (task=task_2) idmistralchild2 has started.

line mistral 100

Child execution (task=task_2) idmistralchild2 has finished (status=timeout).

Execution idfoo4 has completed (status=succeeded).
""".lstrip()
        self.assertEqual(stdout, expected_result)
        self.assertEqual(stderr, '')
Example #43
0
 def __init__(self, *args, **kwargs):
     super(TestShell, self).__init__(*args, **kwargs)
     self.shell = Shell()
Example #44
0
 def __init__(self, *args, **kwargs):
     super(ActionExecutionTailCommandTestCase,
           self).__init__(*args, **kwargs)
     self.shell = Shell()