Passed
Push — develop ( b8d4ca...421369 )
by Plexxi
07:01 queued 03:57
created

get_runners_base_paths()   B

Complexity

Conditions 5

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
c 0
b 0
f 0
dl 0
loc 24
rs 8.1671
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
0 ignored issues
show
Bug introduced by
There seems to be a cyclic import (st2common.models.db.liveaction -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.liveaction).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.db.action -> st2common.models.db.liveaction -> st2common.util.action_db -> st2common.persistence.action).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.executionstate -> st2common.persistence.base -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.executionstate -> st2common.transport -> st2common.transport.bootstrap_utils -> st2common.transport.announcement -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.executionstate -> st2common.transport -> st2common.transport.bootstrap_utils -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.execution -> st2common.persistence.base -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.runner -> st2common.persistence.base -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.actionalias -> st2common.persistence.base -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.db.liveaction -> st2common.util.action_db -> st2common.persistence.liveaction).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.liveaction -> st2common.transport -> st2common.transport.bootstrap_utils -> st2common.transport.announcement -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.liveaction -> st2common.transport -> st2common.transport.bootstrap_utils -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.execution -> st2common.transport -> st2common.transport.bootstrap_utils -> st2common.transport.announcement -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.execution -> st2common.transport -> st2common.transport.bootstrap_utils -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.base -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  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
import os
17
import os.path
18
19
from oslo_config import cfg
20
21
from st2common.constants.action import LIBS_DIR as ACTION_LIBS_DIR
22
from st2common.util.types import OrderedSet
23
from st2common.util.shell import quote_unix
24
25
__all__ = [
26
    'get_system_packs_base_path',
27
    'get_packs_base_paths',
28
    'get_pack_base_path',
29
    'get_pack_directory',
30
    'get_pack_file_abs_path',
31
    'get_pack_resource_file_abs_path',
32
    'get_relative_path_to_pack',
33
    'check_pack_directory_exists',
34
    'check_pack_content_directory_exists'
35
]
36
37
38
def get_system_packs_base_path():
39
    """
40
    Return a path to the directory where system packs are stored.
41
42
    :rtype: ``str``
43
    """
44
    return cfg.CONF.content.system_packs_base_path
45
46
47
def get_system_runners_base_path():
48
    """
49
    Return a path to the directory where system runners are stored.
50
51
    :rtype: ``str``
52
    """
53
    return cfg.CONF.content.system_runners_base_path
54
55
56
def get_runners_base_paths():
57
    """
58
    Return a list of base paths which are searched for runners.
59
60
    :rtype: ``list``
61
    """
62
    system_runners_base_path = get_system_runners_base_path()
63
    runners_base_paths = cfg.CONF.content.runners_base_paths or ''
64
65
    # Remove trailing colon (if present)
66
    if runners_base_paths.endswith(':'):
67
        runners_base_paths = runners_base_paths[:-1]
68
69
    result = []
70
    # System path is always first
71
    if system_runners_base_path:
72
        result.append(system_runners_base_path)
73
74
    runners_base_paths = runners_base_paths.split(':')
75
76
    result = result + runners_base_paths
77
    result = [path for path in result if path]
78
    result = list(OrderedSet(result))
79
    return result
80
81
82
def get_packs_base_paths():
83
    """
84
    Return a list of base paths which are searched for integration packs.
85
86
    :rtype: ``list``
87
    """
88
    system_packs_base_path = get_system_packs_base_path()
89
    packs_base_paths = cfg.CONF.content.packs_base_paths or ''
90
91
    # Remove trailing colon (if present)
92
    if packs_base_paths.endswith(':'):
93
        packs_base_paths = packs_base_paths[:-1]
94
95
    result = []
96
    # System path is always first
97
    if system_packs_base_path:
98
        result.append(system_packs_base_path)
99
100
    packs_base_paths = packs_base_paths.split(':')
101
102
    result = result + packs_base_paths
103
    result = [path for path in result if path]
104
    result = list(OrderedSet(result))
105
    return result
106
107
108
def check_pack_directory_exists(pack):
109
    """
110
    Check if a provided pack exists in one of the pack paths.
111
112
    :param pack: Pack name.
113
    :type pack: ``str``
114
115
    :rtype: ``bool``
116
    """
117
    packs_base_paths = get_packs_base_paths()
118
119
    for base_dir in packs_base_paths:
120
        pack_path = os.path.join(base_dir, pack)
121
        if os.path.exists(pack_path):
122
            return True
123
124
    return False
125
126
127
def check_pack_content_directory_exists(pack, content_type):
128
    """
129
    Check if a provided pack exists in one of the pack paths.
130
131
    :param pack: Pack name.
132
    :type pack: ``str``
133
134
    :param content_type: Content type (actions, sensors, rules).
135
    :type content_type: ``str``
136
137
    :rtype: ``bool``
138
    """
139
    packs_base_paths = get_packs_base_paths()
140
141
    for base_dir in packs_base_paths:
142
        pack_content_pack = os.path.join(base_dir, pack, content_type)
143
        if os.path.exists(pack_content_pack):
144
            return True
145
146
    return False
147
148
149
def get_pack_base_path(pack_name):
150
    """
151
    Return full absolute base path to the content pack directory.
152
153
    Note: This function looks for a pack in all the load paths and return path to the first pack
154
    which matched the provided name.
155
156
    If a pack is not found, we return a pack which points to the first packs directory (this is
157
    here for backward compatibility reasons).
158
159
    :param pack_name: Content pack name.
160
    :type pack_name: ``str``
161
162
    :rtype: ``str``
163
    """
164
    if not pack_name:
165
        return None
166
167
    packs_base_paths = get_packs_base_paths()
168
    for packs_base_path in packs_base_paths:
169
        pack_base_path = os.path.join(packs_base_path, quote_unix(pack_name))
170
        pack_base_path = os.path.abspath(pack_base_path)
171
172
        if os.path.isdir(pack_base_path):
173
            return pack_base_path
174
175
    # Path with the provided name not found
176
    pack_base_path = os.path.join(packs_base_paths[0], quote_unix(pack_name))
177
    pack_base_path = os.path.abspath(pack_base_path)
178
    return pack_base_path
179
180
181
def get_pack_directory(pack_name):
182
    """
183
    Retrieve a directory for the provided pack.
184
185
    If a directory for the provided pack doesn't exist in any of the search paths, None
186
    is returned instead.
187
188
    Note: If same pack exists in multiple search path, path to the first one is returned.
189
190
    :param pack_name: Pack name.
191
    :type pack_name: ``str``
192
193
    :return: Pack to the pack directory.
194
    :rtype: ``str`` or ``None``
195
    """
196
    packs_base_paths = get_packs_base_paths()
197
    for packs_base_path in packs_base_paths:
198
        pack_base_path = os.path.join(packs_base_path, quote_unix(pack_name))
199
        pack_base_path = os.path.abspath(pack_base_path)
200
201
        if os.path.isdir(pack_base_path):
202
            return pack_base_path
203
204
    return None
205
206
207
def get_entry_point_abs_path(pack=None, entry_point=None):
208
    """
209
    Return full absolute path of an action entry point in a pack.
210
211
    :param pack_name: Content pack name.
212
    :type pack_name: ``str``
213
    :param entry_point: Action entry point.
214
    :type entry_point: ``str``
215
216
    :rtype: ``str``
217
    """
218
    if not entry_point:
219
        return None
220
221
    if os.path.isabs(entry_point):
222
        pack_base_path = get_pack_base_path(pack_name=pack)
223
        common_prefix = os.path.commonprefix([pack_base_path, entry_point])
224
225
        if common_prefix != pack_base_path:
226
            raise ValueError('Entry point file "%s" is located outside of the pack directory' %
227
                             (entry_point))
228
229
        return entry_point
230
231
    entry_point_abs_path = get_pack_resource_file_abs_path(pack_name=pack,
232
                                                           resource_type='action',
233
                                                           file_path=entry_point)
234
    return entry_point_abs_path
235
236
237
def get_pack_file_abs_path(pack_name, file_path):
238
    """
239
    Retrieve full absolute path to the pack file.
240
241
    Note: This function also takes care of sanitizing ``file_name`` argument
242
          preventing directory traversal and similar attacks.
243
244
    :param pack_name: Pack name.
245
    :type pack_name: ``str``
246
247
    :pack file_path: Resource file path relative to the pack directory (e.g. my_file.py or
248
                     actions/directory/my_file.py)
249
    :type file_path: ``str``
250
251
    :rtype: ``str``
252
    """
253
    pack_base_path = get_pack_base_path(pack_name=pack_name)
254
255
    path_components = []
256
    path_components.append(pack_base_path)
257
258
    # Normalize the path to prevent directory traversal
259
    normalized_file_path = os.path.normpath('/' + file_path).lstrip('/')
260
261
    if normalized_file_path != file_path:
262
        raise ValueError('Invalid file path: %s' % (file_path))
263
264
    path_components.append(normalized_file_path)
265
    result = os.path.join(*path_components)
266
267
    assert normalized_file_path in result
268
269
    # Final safety check for common prefix to avoid traversal attack
270
    common_prefix = os.path.commonprefix([pack_base_path, result])
271
    if common_prefix != pack_base_path:
272
        raise ValueError('Invalid file_path: %s' % (file_path))
273
274
    return result
275
276
277
def get_pack_resource_file_abs_path(pack_name, resource_type, file_path):
278
    """
279
    Retrieve full absolute path to the pack resource file.
280
281
    Note: This function also takes care of sanitizing ``file_name`` argument
282
          preventing directory traversal and similar attacks.
283
284
    :param pack_name: Pack name.
285
    :type pack_name: ``str``
286
287
    :param resource_type: Pack resource type (e.g. action, sensor, etc.).
288
    :type resource_type: ``str``
289
290
    :pack file_path: Resource file path relative to the pack directory (e.g. my_file.py or
291
                     directory/my_file.py)
292
    :type file_path: ``str``
293
294
    :rtype: ``str``
295
    """
296
    path_components = []
297
    if resource_type == 'action':
298
        path_components.append('actions/')
299
    elif resource_type == 'sensor':
300
        path_components.append('sensors/')
301
    elif resource_type == 'rule':
302
        path_components.append('rules/')
303
    else:
304
        raise ValueError('Invalid resource type: %s' % (resource_type))
305
306
    path_components.append(file_path)
307
    file_path = os.path.join(*path_components)
308
    result = get_pack_file_abs_path(pack_name=pack_name, file_path=file_path)
309
    return result
310
311
312
def get_relative_path_to_pack(pack_name, file_path):
313
    """
314
    Retrieve a file path which is relative to the provided pack directory.
315
316
    :rtype: ``str``
317
    """
318
    pack_base_path = get_pack_base_path(pack_name=pack_name)
319
320
    if not os.path.isabs(file_path):
321
        return file_path
322
323
    common_prefix = os.path.commonprefix([pack_base_path, file_path])
324
    if common_prefix != pack_base_path:
325
        raise ValueError('file_path is not located inside the pack directory')
326
327
    relative_path = os.path.relpath(file_path, common_prefix)
328
    return relative_path
329
330
331
def get_action_libs_abs_path(pack=None, entry_point=None):
332
    """
333
    Return full absolute path of libs for an action.
334
335
    :param pack_name: Content pack name.
336
    :type pack_name: ``str``
337
    :param entry_point: Action entry point.
338
    :type entry_point: ``str``
339
340
    :rtype: ``str``
341
    """
342
    entry_point_abs_path = get_entry_point_abs_path(pack=pack, entry_point=entry_point)
343
    if entry_point_abs_path is not None:
344
        return os.path.join(os.path.dirname(entry_point_abs_path), ACTION_LIBS_DIR)
345
    else:
346
        return None
347
348
349
def get_aliases_base_paths():
350
    """
351
    Return a list of base paths which are searched for action aliases.
352
353
    :rtype: ``list``
354
    """
355
    aliases_base_paths = cfg.CONF.content.aliases_base_paths or ''
356
357
    # Remove trailing colon (if present)
358
    if aliases_base_paths.endswith(':'):
359
        aliases_base_paths = aliases_base_paths[:-1]
360
361
    result = []
362
363
    aliases_base_paths = aliases_base_paths.split(':')
364
365
    result = aliases_base_paths
366
    result = [path for path in result if path]
367
    result = list(OrderedSet(result))
368
    return result
369