1#!/usr/bin/python
2# Copyright 2016 Google Inc. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""Unittest for script_executor.py module."""
17
18import stat
19
20from google_compute_engine.metadata_scripts import script_executor
21from google_compute_engine.test_compat import mock
22from google_compute_engine.test_compat import unittest
23
24
25class ScriptExecutorTest(unittest.TestCase):
26
27  def setUp(self):
28    self.script_type = 'test'
29    self.metadata_script = '/tmp/script'
30    self.mock_logger = mock.Mock()
31    self.executor = script_executor.ScriptExecutor(
32        self.mock_logger, self.script_type)
33
34  @mock.patch('google_compute_engine.metadata_scripts.script_executor.os')
35  def testMakeExecutable(self, mock_os):
36    st_mode = 1
37    chmod_mode = st_mode + stat.S_IEXEC
38    mock_os_stat = mock.Mock()
39    mock_os_stat.st_mode = st_mode
40    mock_os.stat.return_value = mock_os_stat
41    self.executor._MakeExecutable(self.metadata_script)
42    mock_os.chmod.assert_called_once_with(self.metadata_script, chmod_mode)
43
44  @mock.patch('google_compute_engine.metadata_scripts.script_executor.subprocess')
45  def testRunScript(self, mock_subprocess):
46    mock_readline = mock.Mock()
47    mock_readline.side_effect = [bytes(b'a\n'), bytes(b'b\n'), bytes(b'')]
48    mock_stdout = mock.Mock()
49    mock_stdout.readline = mock_readline
50    mock_process = mock.Mock()
51    mock_process.poll.return_value = 0
52    mock_process.stdout = mock_stdout
53    mock_process.returncode = 1
54    mock_subprocess.Popen.return_value = mock_process
55    metadata_key = '%s-script' % self.script_type
56
57    self.executor._RunScript(metadata_key, self.metadata_script)
58    expected_calls = [
59        mock.call('%s: %s', metadata_key, 'a'),
60        mock.call('%s: %s', metadata_key, 'b'),
61        mock.call('%s: Return code %s.', metadata_key, 1),
62    ]
63    self.assertEqual(self.mock_logger.info.mock_calls, expected_calls)
64    mock_subprocess.Popen.assert_called_once_with(
65        self.metadata_script, shell=True, executable='/bin/bash',
66        stderr=mock_subprocess.STDOUT, stdout=mock_subprocess.PIPE)
67    mock_process.poll.assert_called_once_with()
68
69  def testRunScripts(self):
70    self.executor._MakeExecutable = mock.Mock()
71    self.executor._RunScript = mock.Mock()
72    mocks = mock.Mock()
73    mocks.attach_mock(self.executor._MakeExecutable, 'make_executable')
74    mocks.attach_mock(self.executor._RunScript, 'run_script')
75    mocks.attach_mock(self.mock_logger, 'logger')
76    script_dict = {
77        '%s-script' % self.script_type: 'a',
78        '%s-script-key' % self.script_type: 'b',
79        '%s-script-url' % self.script_type: 'c',
80    }
81
82    self.executor.RunScripts(script_dict)
83    expected_calls = [
84        mock.call.make_executable('c'),
85        mock.call.run_script('%s-script-url' % self.script_type, 'c'),
86        mock.call.make_executable('a'),
87        mock.call.run_script('%s-script' % self.script_type, 'a'),
88    ]
89    self.assertEqual(mocks.mock_calls, expected_calls)
90
91  def testRunScriptsEmpty(self):
92    self.executor._MakeExecutable = mock.Mock()
93    self.executor._RunScript = mock.Mock()
94    mocks = mock.Mock()
95    mocks.attach_mock(self.executor._MakeExecutable, 'make_executable')
96    mocks.attach_mock(self.executor._RunScript, 'run_script')
97    mocks.attach_mock(self.mock_logger, 'logger')
98    script_dict = {
99        '%s-invalid' % self.script_type: 'script',
100    }
101
102    self.executor.RunScripts(script_dict)
103    expected_calls = [
104        mock.call.logger.info(mock.ANY, self.script_type),
105    ]
106    self.assertEqual(mocks.mock_calls, expected_calls)
107
108
109if __name__ == '__main__':
110  unittest.main()
111