Passed
Pull Request — master (#3556)
by Lakshmi
04:52
created

PackSearchController   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 38
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 38
rs 10
c 0
b 0
f 0
wmc 6

2 Methods

Rating   Name   Duplication   Size   Complexity  
A post() 0 13 4
A _get_proxy_config() 0 22 2
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
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 re
18
19
from collections import defaultdict
20
from collections import OrderedDict
21
22
import six
23
24
import st2common
25
from st2common import log as logging
26
from st2common.bootstrap.triggersregistrar import TriggersRegistrar
27
from st2common.bootstrap.sensorsregistrar import SensorsRegistrar
28
from st2common.bootstrap.actionsregistrar import ActionsRegistrar
29
from st2common.bootstrap.aliasesregistrar import AliasesRegistrar
30
from st2common.bootstrap.policiesregistrar import PolicyRegistrar
31
import st2common.bootstrap.policiesregistrar as policies_registrar
32
import st2common.bootstrap.runnersregistrar as runners_registrar
33
from st2common.bootstrap.rulesregistrar import RulesRegistrar
34
import st2common.bootstrap.ruletypesregistrar as rule_types_registrar
35
from st2common.bootstrap.configsregistrar import ConfigsRegistrar
36
import st2common.content.utils as content_utils
37
from st2common.models.api.action import LiveActionCreateAPI
38
from st2common.models.api.pack import PackAPI
39
from st2common.models.api.pack import PackAsyncAPI
40
from st2common.exceptions.db import StackStormDBObjectNotFoundError
41
from st2common.persistence.pack import Pack
42
from st2common.rbac.types import PermissionType
43
from st2common.rbac import utils as rbac_utils
44
from st2common.services import packs as packs_service
45
from st2common.router import abort
46
from st2common.router import Response
47
48
from st2api.controllers.resource import ResourceController
49
from st2api.controllers.v1.actionexecutions import ActionExecutionsControllerMixin
50
51
http_client = six.moves.http_client
52
53
__all__ = [
54
    'PacksController',
55
    'BasePacksController',
56
    'ENTITIES'
57
]
58
59
LOG = logging.getLogger(__name__)
60
61
# Note: The order those are defined it's important so they are registered in
62
# the same order as they are in st2-register-content.
63
# We also need to use list of tuples to preserve the order.
64
ENTITIES = OrderedDict([
65
    ('trigger', (TriggersRegistrar, 'triggers')),
66
    ('sensor', (SensorsRegistrar, 'sensors')),
67
    ('action', (ActionsRegistrar, 'actions')),
68
    ('rule', (RulesRegistrar, 'rules')),
69
    ('alias', (AliasesRegistrar, 'aliases')),
70
    ('policy', (PolicyRegistrar, 'policies')),
71
    ('config', (ConfigsRegistrar, 'configs'))
72
])
73
74
75
class PackInstallController(ActionExecutionsControllerMixin):
76
77
    def post(self, pack_install_request):
78
        parameters = {
79
            'packs': pack_install_request.packs,
80
        }
81
82
        if pack_install_request.force:
83
            parameters['force'] = True
84
85
        new_liveaction_api = LiveActionCreateAPI(action='packs.install',
86
                                                 parameters=parameters,
87
                                                 user=None)
88
89
        execution_resp = self._handle_schedule_execution(liveaction_api=new_liveaction_api,
90
                                                         requester_user=None)
91
92
        exec_id = PackAsyncAPI(execution_id=execution_resp.json['id'])
93
94
        return Response(json=exec_id, status=http_client.ACCEPTED)
95
96
97
class PackUninstallController(ActionExecutionsControllerMixin):
98
99
    def post(self, pack_uninstall_request, ref_or_id=None):
100
        if ref_or_id:
101
            parameters = {
102
                'packs': [ref_or_id]
103
            }
104
        else:
105
            parameters = {
106
                'packs': pack_uninstall_request.packs
107
            }
108
109
        new_liveaction_api = LiveActionCreateAPI(action='packs.uninstall',
110
                                                 parameters=parameters,
111
                                                 user=None)
112
113
        execution_resp = self._handle_schedule_execution(liveaction_api=new_liveaction_api,
114
                                                         requester_user=None)
115
116
        exec_id = PackAsyncAPI(execution_id=execution_resp.json['id'])
117
118
        return Response(json=exec_id, status=http_client.ACCEPTED)
119
120
121
class PackRegisterController(object):
122
    CONTENT_TYPES = ['runner', 'action', 'trigger', 'sensor', 'rule',
123
                     'rule_type', 'alias', 'policy_type', 'policy', 'config']
124
125
    def post(self, pack_register_request):
126
        if pack_register_request and hasattr(pack_register_request, 'types'):
127
            types = pack_register_request.types
128
            if 'all' in types:
129
                types = PackRegisterController.CONTENT_TYPES
130
        else:
131
            types = PackRegisterController.CONTENT_TYPES
132
133
        if pack_register_request and hasattr(pack_register_request, 'packs'):
134
            packs = list(set(pack_register_request.packs))
135
        else:
136
            packs = None
137
138
        result = defaultdict(int)
139
140
        # Register depended resources (actions depend on runners, rules depend on rule types, etc)
141
        if ('runner' in types or 'runners' in types) or ('action' in types or 'actions' in types):
142
            result['runners'] = runners_registrar.register_runners(experimental=True)
143
        if ('rule_type' in types or 'rule_types' in types) or \
144
           ('rule' in types or 'rules' in types):
145
            result['rule_types'] = rule_types_registrar.register_rule_types()
146
        if ('policy_type' in types or 'policy_types' in types) or \
147
           ('policy' in types or 'policies' in types):
148
            result['policy_types'] = policies_registrar.register_policy_types(st2common)
149
150
        use_pack_cache = False
151
152
        fail_on_failure = getattr(pack_register_request, 'fail_on_failure', True)
153
        for type, (Registrar, name) in six.iteritems(ENTITIES):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
154
            if type in types or name in types:
155
                registrar = Registrar(use_pack_cache=use_pack_cache,
156
                                      fail_on_failure=fail_on_failure)
157
                if packs:
158
                    for pack in packs:
159
                        pack_path = content_utils.get_pack_base_path(pack)
160
161
                        try:
162
                            registered_count = registrar.register_from_pack(pack_dir=pack_path)
163
                            result[name] += registered_count
164
                        except ValueError as e:
165
                            # Throw more user-friendly exception if requsted pack doesn't exist
166
                            if re.match('Directory ".*?" doesn\'t exist', str(e)):
167
                                msg = 'Pack "%s" not found on disk: %s' % (pack, str(e))
168
                                raise ValueError(msg)
169
170
                            raise e
171
                else:
172
                    packs_base_paths = content_utils.get_packs_base_paths()
173
                    registered_count = registrar.register_from_packs(base_dirs=packs_base_paths)
174
                    result[name] += registered_count
175
176
        return result
177
178
179
class PackSearchController(object):
180
181
    def post(self, pack_search_request):
182
183
        proxy_config = self._get_proxy_config(pack_search_request)
184
185
        if hasattr(pack_search_request, 'query'):
186
            packs = packs_service.search_pack_index(pack_search_request.query,
187
                                                    case_sensitive=False,
188
                                                    proxy_config=proxy_config)
189
            return [PackAPI(**pack) for pack in packs]
190
        else:
191
            pack = packs_service.get_pack_from_index(pack_search_request.pack,
192
                                                     proxy_config=proxy_config)
193
            return PackAPI(**pack) if pack else []
194
195
    def _get_proxy_config(self, pack_search_request):
196
        proxy_config = None
197
        if hasattr(pack_search_request, 'proxy_config'):
198
            LOG.debug('Loading proxy configuration from pack search HTTP request.')
199
            proxy_config = pack_search_request.proxy_config
200
        else:
201
            LOG.debug('Loading proxy configuration from env variables %s.', os.environ)
202
            http_proxy = os.environ.get('http_proxy', None)
203
            https_proxy = os.environ.get('https_proxy', None)
204
            no_proxy = os.environ.get('no_proxy', None)
205
            proxy_ca_bundle_path = os.environ.get('proxy_ca_bundle_path', None)
206
207
            proxy_config = {
208
                'http_proxy': http_proxy,
209
                'https_proxy': https_proxy,
210
                'proxy_ca_bundle_path': proxy_ca_bundle_path,
211
                'no_proxy': no_proxy
212
            }
213
214
        LOG.debug('Proxy configuration: %s', proxy_config)
215
216
        return proxy_config
217
218
219
class IndexHealthController(object):
220
221
    def get(self):
222
        """
223
        Check if all listed indexes are healthy: they should be reachable,
224
        return valid JSON objects, and yield more than one result.
225
        """
226
        _, status = packs_service.fetch_pack_index(allow_empty=True)
227
228
        health = {
229
            "indexes": {
230
                "count": len(status),
231
                "valid": 0,
232
                "invalid": 0,
233
                "errors": {},
234
                "status": status,
235
            },
236
            "packs": {
237
                "count": 0,
238
            },
239
        }
240
241
        for index in status:
242
            if index['error']:
243
                error_count = health['indexes']['errors'].get(index['error'], 0) + 1
244
                health['indexes']['invalid'] += 1
245
                health['indexes']['errors'][index['error']] = error_count
246
            else:
247
                health['indexes']['valid'] += 1
248
            health['packs']['count'] += index['packs']
249
250
        return health
251
252
253
class BasePacksController(ResourceController):
254
    model = PackAPI
255
    access = Pack
256
257
    def _get_one_by_ref_or_id(self, ref_or_id, requester_user, exclude_fields=None):
258
        instance = self._get_by_ref_or_id(ref_or_id=ref_or_id, exclude_fields=exclude_fields)
259
260
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
261
                                                          resource_db=instance,
262
                                                          permission_type=PermissionType.PACK_VIEW)
263
264
        if not instance:
265
            msg = 'Unable to identify resource with ref_or_id "%s".' % (ref_or_id)
266
            abort(http_client.NOT_FOUND, msg)
267
            return
268
269
        result = self.model.from_model(instance, **self.from_model_kwargs)
270
271
        return result
272
273
    def _get_by_ref_or_id(self, ref_or_id, exclude_fields=None):
274
        resource_db = self._get_by_id(resource_id=ref_or_id, exclude_fields=exclude_fields)
275
276
        if not resource_db:
277
            # Try ref
278
            resource_db = self._get_by_ref(ref=ref_or_id, exclude_fields=exclude_fields)
279
280
        if not resource_db:
281
            msg = 'Resource with a ref or id "%s" not found' % (ref_or_id)
282
            raise StackStormDBObjectNotFoundError(msg)
283
284
        return resource_db
285
286
    def _get_by_ref(self, ref, exclude_fields=None):
287
        """
288
        Note: In this case "ref" is pack name and not StackStorm's ResourceReference.
289
        """
290
        resource_db = self.access.query(ref=ref, exclude_fields=exclude_fields).first()
291
        return resource_db
292
293
294
class PacksIndexController():
295
    search = PackSearchController()
296
    health = IndexHealthController()
297
298
299
class PacksController(BasePacksController):
300
    from st2api.controllers.v1.packviews import PackViewsController
301
302
    model = PackAPI
303
    access = Pack
304
    supported_filters = {
305
        'name': 'name',
306
        'ref': 'ref'
307
    }
308
309
    query_options = {
310
        'sort': ['ref']
311
    }
312
313
    # Nested controllers
314
    install = PackInstallController()
315
    uninstall = PackUninstallController()
316
    register = PackRegisterController()
317
    views = PackViewsController()
318
    index = PacksIndexController()
319
320
    def __init__(self):
321
        super(PacksController, self).__init__()
322
        self.get_one_db_method = self._get_by_ref_or_id
323
324
    def get_all(self, sort=None, offset=0, limit=None, **raw_filters):
325
        return super(PacksController, self)._get_all(sort=sort,
326
                                                     offset=offset,
327
                                                     limit=limit,
328
                                                     raw_filters=raw_filters)
329
330
    def get_one(self, ref_or_id, requester_user):
331
        return self._get_one_by_ref_or_id(ref_or_id=ref_or_id, requester_user=requester_user)
332
333
334
packs_controller = PacksController()
335