Passed
Push — develop ( ca19e2...75fdf9 )
by Plexxi
08:10 queued 05:19
created

test_run_pack_lock_is_already_acquired()   A

Complexity

Conditions 3

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
dl 0
loc 23
rs 9.0856

1 Method

Rating   Name   Duplication   Size   Complexity  
A DownloadGitRepoActionTestCase.mock_acquire() 0 2 1
1
#!/usr/bin/env python
2
3
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
4
# contributor license agreements.  See the NOTICE file distributed with
5
# this work for additional information regarding copyright ownership.
6
# The ASF licenses this file to You under the Apache License, Version 2.0
7
# (the "License"); you may not use this file except in compliance with
8
# the License.  You may obtain a copy of the License at
9
#
10
#     http://www.apache.org/licenses/LICENSE-2.0
11
#
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS,
14
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
# See the License for the specific language governing permissions and
16
# limitations under the License.
17
18
import os
19
import mock
20
import shutil
21
import tempfile
22
import hashlib
23
24
from lockfile import LockFile
25
from lockfile import LockTimeout
26
from git.repo import Repo
27
from gitdb.exc import BadName
28
from st2common.services import packs as pack_service
29
from st2tests.base import BaseActionTestCase
30
31
import pack_mgmt.download
32
from pack_mgmt.download import DownloadGitRepoAction
33
34
PACK_INDEX = {
35
    "test": {
36
        "version": "0.4.0",
37
        "name": "test",
38
        "repo_url": "https://github.com/StackStorm-Exchange/stackstorm-test",
39
        "author": "st2-dev",
40
        "keywords": ["some", "search", "another", "terms"],
41
        "email": "[email protected]",
42
        "description": "st2 pack to test package management pipeline"
43
    },
44
    "test2": {
45
        "version": "0.5.0",
46
        "name": "test2",
47
        "repo_url": "https://github.com/StackStorm-Exchange/stackstorm-test2",
48
        "author": "stanley",
49
        "keywords": ["some", "special", "terms"],
50
        "email": "[email protected]",
51
        "description": "another st2 pack to test package management pipeline"
52
    },
53
    "test3": {
54
        "version": "0.5.0",
55
        "stackstorm_version": ">=1.6.0, <2.2.0",
56
        "name": "test3",
57
        "repo_url": "https://github.com/StackStorm-Exchange/stackstorm-test3",
58
        "author": "stanley",
59
        "keywords": ["some", "special", "terms"],
60
        "email": "[email protected]",
61
        "description": "another st2 pack to test package management pipeline"
62
    }
63
}
64
65
66
@mock.patch.object(pack_service, 'fetch_pack_index', mock.MagicMock(return_value=(PACK_INDEX, {})))
67
class DownloadGitRepoActionTestCase(BaseActionTestCase):
68
    action_cls = DownloadGitRepoAction
69
70
    def setUp(self):
71
        super(DownloadGitRepoActionTestCase, self).setUp()
72
73
        clone_from = mock.patch.object(Repo, 'clone_from')
74
75
        self.addCleanup(clone_from.stop)
76
        self.clone_from = clone_from.start()
77
78
        expand_user = mock.patch.object(os.path, 'expanduser',
79
                                        mock.MagicMock(return_value=tempfile.mkdtemp()))
80
81
        self.addCleanup(expand_user.stop)
82
        self.expand_user = expand_user.start()
83
84
        self.repo_base = tempfile.mkdtemp()
85
86
        self.repo_instance = mock.MagicMock()
87
88
        def side_effect(url, to_path, **kwargs):
89
            # Since we have no way to pass pack name here, we would have to derive it from repo url
90
            fixture_name = url.split('/')[-1]
91
            fixture_path = os.path.join(self._get_base_pack_path(), 'tests/fixtures', fixture_name)
92
            shutil.copytree(fixture_path, to_path)
93
            return self.repo_instance
94
95
        self.clone_from.side_effect = side_effect
96
97
    def tearDown(self):
98
        shutil.rmtree(self.repo_base)
99
        shutil.rmtree(self.expand_user())
100
101
    def test_run_pack_download(self):
102
        action = self.get_action_instance()
103
        result = action.run(packs=['test'], abs_repo_base=self.repo_base)
104
        temp_dir = hashlib.md5(PACK_INDEX['test']['repo_url']).hexdigest()
105
106
        self.assertEqual(result, {'test': 'Success.'})
107
        self.clone_from.assert_called_once_with(PACK_INDEX['test']['repo_url'],
108
                                                os.path.join(os.path.expanduser('~'), temp_dir))
109
        self.assertTrue(os.path.isfile(os.path.join(self.repo_base, 'test/pack.yaml')))
110
111
    def test_run_pack_download_existing_pack(self):
112
        action = self.get_action_instance()
113
        action.run(packs=['test'], abs_repo_base=self.repo_base)
114
        self.assertTrue(os.path.isfile(os.path.join(self.repo_base, 'test/pack.yaml')))
115
116
        result = action.run(packs=['test'], abs_repo_base=self.repo_base)
117
118
        self.assertEqual(result, {'test': 'Success.'})
119
120
    def test_run_pack_download_multiple_packs(self):
121
        action = self.get_action_instance()
122
        result = action.run(packs=['test', 'test2'], abs_repo_base=self.repo_base)
123
        temp_dirs = [
124
            hashlib.md5(PACK_INDEX['test']['repo_url']).hexdigest(),
125
            hashlib.md5(PACK_INDEX['test2']['repo_url']).hexdigest()
126
        ]
127
128
        self.assertEqual(result, {'test': 'Success.', 'test2': 'Success.'})
129
        self.clone_from.assert_any_call(PACK_INDEX['test']['repo_url'],
130
                                        os.path.join(os.path.expanduser('~'), temp_dirs[0]))
131
        self.clone_from.assert_any_call(PACK_INDEX['test2']['repo_url'],
132
                                        os.path.join(os.path.expanduser('~'), temp_dirs[1]))
133
        self.assertEqual(self.clone_from.call_count, 2)
134
        self.assertTrue(os.path.isfile(os.path.join(self.repo_base, 'test/pack.yaml')))
135
        self.assertTrue(os.path.isfile(os.path.join(self.repo_base, 'test2/pack.yaml')))
136
137
    @mock.patch.object(Repo, 'clone_from')
138
    def test_run_pack_download_error(self, clone_from):
139
        clone_from.side_effect = Exception('Something went terribly wrong during the clone')
140
141
        action = self.get_action_instance()
142
        self.assertRaises(Exception, action.run, packs=['test'], abs_repo_base=self.repo_base)
143
144
    def test_run_pack_download_no_tag(self):
145
        self.repo_instance.commit.side_effect = BadName
146
147
        action = self.get_action_instance()
148
        self.assertRaises(ValueError, action.run, packs=['test=1.2.3'],
149
                          abs_repo_base=self.repo_base)
150
151
    def test_run_pack_lock_is_already_acquired(self):
152
        action = self.get_action_instance()
153
        temp_dir = hashlib.md5(PACK_INDEX['test']['repo_url']).hexdigest()
154
155
        original_acquire = LockFile.acquire
156
        def mock_acquire(self, timeout=None):
157
            original_acquire(self, timeout=0.1)
158
159
        LockFile.acquire = mock_acquire
160
161
        try:
162
            lock_file = LockFile('/tmp/%s' % (temp_dir))
163
164
            # Acquire a lock (file) so acquire inside download will fail
165
            with open(lock_file.lock_file, 'w') as fp:
166
                fp.write('')
167
168
            expected_msg = 'Timeout waiting to acquire lock for'
169
            self.assertRaisesRegexp(LockTimeout, expected_msg, action.run, packs=['test'],
170
                                    abs_repo_base=self.repo_base)
171
        finally:
172
            os.unlink(lock_file.lock_file)
173
            LockFile.acquire = original_acquire
174
175
    def test_run_pack_lock_is_already_acquired_force_flag(self):
176
        # Lock is already acquired but force is true so it should be deleted and released
177
        action = self.get_action_instance()
178
        temp_dir = hashlib.md5(PACK_INDEX['test']['repo_url']).hexdigest()
179
180
        original_acquire = LockFile.acquire
181
        def mock_acquire(self, timeout=None):
182
            original_acquire(self, timeout=0.1)
183
184
        LockFile.acquire = mock_acquire
185
186
        try:
187
            lock_file = LockFile('/tmp/%s' % (temp_dir))
188
189
            # Acquire a lock (file) so acquire inside download will fail
190
            with open(lock_file.lock_file, 'w') as fp:
191
                fp.write('')
192
193
            result = action.run(packs=['test'], abs_repo_base=self.repo_base, force=True)
194
        finally:
195
            LockFile.acquire = original_acquire
196
197
    def test_run_pack_download_v_tag(self):
198
        def side_effect(ref):
199
            if ref[0] != 'v':
200
                raise BadName()
201
            return mock.MagicMock(hexsha='abcdef')
202
203
        self.repo_instance.commit.side_effect = side_effect
204
        self.repo_instance.git = mock.MagicMock(
205
            branch=(lambda *args: 'master'),
206
            checkout=(lambda *args: True)
207
        )
208
209
        action = self.get_action_instance()
210
        result = action.run(packs=['test=1.2.3'], abs_repo_base=self.repo_base)
211
212
        self.assertEqual(result, {'test': 'Success.'})
213
214
    def test_download_pack_stackstorm_version_identifier_check(self):
215
        action = self.get_action_instance()
216
217
        # Version is satisfied
218
        pack_mgmt.download.CURRENT_STACKSTROM_VERSION = '2.0.0'
219
220
        result = action.run(packs=['test3'], abs_repo_base=self.repo_base)
221
        self.assertEqual(result['test3'], 'Success.')
222
223
        # Pack requires a version which is not satisfied by current StackStorm version
224
        pack_mgmt.download.CURRENT_STACKSTROM_VERSION = '2.2.0'
225
        expected_msg = ('Pack "test3" requires StackStorm ">=1.6.0, <2.2.0", but '
226
                        'current version is "2.2.0"')
227
        self.assertRaisesRegexp(ValueError, expected_msg, action.run, packs=['test3'],
228
                                abs_repo_base=self.repo_base)
229
230
        pack_mgmt.download.CURRENT_STACKSTROM_VERSION = '2.3.0'
231
        expected_msg = ('Pack "test3" requires StackStorm ">=1.6.0, <2.2.0", but '
232
                        'current version is "2.3.0"')
233
        self.assertRaisesRegexp(ValueError, expected_msg, action.run, packs=['test3'],
234
                                abs_repo_base=self.repo_base)
235
236
        pack_mgmt.download.CURRENT_STACKSTROM_VERSION = '1.5.9'
237
        expected_msg = ('Pack "test3" requires StackStorm ">=1.6.0, <2.2.0", but '
238
                        'current version is "1.5.9"')
239
        self.assertRaisesRegexp(ValueError, expected_msg, action.run, packs=['test3'],
240
                                abs_repo_base=self.repo_base)
241
242
        pack_mgmt.download.CURRENT_STACKSTROM_VERSION = '1.5.0'
243
        expected_msg = ('Pack "test3" requires StackStorm ">=1.6.0, <2.2.0", but '
244
                        'current version is "1.5.0"')
245
        self.assertRaisesRegexp(ValueError, expected_msg, action.run, packs=['test3'],
246
                                abs_repo_base=self.repo_base)
247
248
        # Version is not met, but force=true parameter is provided
249
        pack_mgmt.download.CURRENT_STACKSTROM_VERSION = '1.5.0'
250
        result = action.run(packs=['test3'], abs_repo_base=self.repo_base, force=True)
251
        self.assertEqual(result['test3'], 'Success.')
252