GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — develop ( 02e3c9...94bc53 )
by Plexxi
08:58 queued 04:23
created

ContentPackConfigLoader._assign_default_values()   D

Complexity

Conditions 8

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 8
dl 0
loc 27
rs 4
c 2
b 0
f 0
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 copy
17
18
import six
19
20
from oslo_config import cfg
21
22
from st2common import log as logging
23
from st2common.persistence.pack import ConfigSchema
24
from st2common.persistence.pack import Config
25
from st2common.content import utils as content_utils
26
from st2common.util import jinja as jinja_utils
27
from st2common.util.templating import render_template_with_system_and_user_context
28
from st2common.util.config_parser import ContentPackConfigParser
29
from st2common.exceptions.db import StackStormDBObjectNotFoundError
30
31
__all__ = [
32
    'ContentPackConfigLoader'
33
]
34
35
LOG = logging.getLogger(__name__)
36
37
38
class ContentPackConfigLoader(object):
39
    """
40
    Class which loads and resolves all the config values and returns a dictionary of resolved values
41
    which can be passed to the resource.
42
43
    It loads and resolves values in the following order:
44
45
    1. Static values from <pack path>/config.yaml file
46
    2. Dynamic and or static values from /opt/stackstorm/configs/<pack name>.yaml file.
47
48
    Values are merged from left to right which means values from "<pack name>.yaml" file have
49
    precedence and override values from pack local config file.
50
    """
51
52
    def __init__(self, pack_name, user=None):
53
        self.pack_name = pack_name
54
        self.user = user or cfg.CONF.system_user.user
55
56
        self.pack_path = content_utils.get_pack_base_path(pack_name=pack_name)
57
        self._config_parser = ContentPackConfigParser(pack_name=pack_name)
58
59
    def get_config(self):
60
        result = {}
61
62
        # 1. Retrieve values from pack local config.yaml file
63
        config = self._config_parser.get_config()
64
65
        if config:
66
            config = config.config or {}
67
            result.update(config)
68
69
        # Retrieve corresponding ConfigDB and ConfigSchemaDB object
70
        # Note: ConfigSchemaDB is optional right now. If it doesn't exist, we assume every value
71
        # is of a type string
72
        try:
73
            config_db = Config.get_by_pack(value=self.pack_name)
74
        except StackStormDBObjectNotFoundError:
75
            # Corresponding pack config doesn't exist, return early
76
            return result
77
78
        try:
79
            config_schema_db = ConfigSchema.get_by_pack(value=self.pack_name)
80
        except StackStormDBObjectNotFoundError:
81
            config_schema_db = None
82
83
        # 2. Retrieve values from "global" pack config file (if available) and resolve them if
84
        # necessary
85
        config = self._get_values_for_config(config_schema_db=config_schema_db,
86
                                             config_db=config_db)
87
        result.update(config)
88
89
        return result
90
91
    def _get_values_for_config(self, config_schema_db, config_db):
92
        schema_values = getattr(config_schema_db, 'attributes', {})
93
        config_values = getattr(config_db, 'values', {})
94
95
        config = copy.deepcopy(config_values)
96
97
        # Assign dynamic config values based on the values in the datastore
98
        config = self._assign_dynamic_config_values(schema=schema_values, config=config)
99
100
        # If config_schema is available we do a second pass and set default values for required
101
        # items which values are not provided / available in the config itself
102
        config = self._assign_default_values(schema=schema_values, config=config)
103
        return config
104
105
    def _assign_dynamic_config_values(self, schema, config, parent_keys=None):
106
        """
107
        Assign dynamic config value for a particular config item if the ite utilizes a Jinja
108
        expression for dynamic config values.
109
110
        Note: This method mutates config argument in place.
111
112
        :rtype: ``dict``
113
        """
114
        parent_keys = parent_keys or []
115
116
        for config_item_key, config_item_value in six.iteritems(config):
117
            schema_item = schema.get(config_item_key, {})
118
            is_dictionary = isinstance(config_item_value, dict)
119
120
            # Inspect nested object properties
121
            if is_dictionary:
122
                parent_keys += [config_item_key]
123
                self._assign_dynamic_config_values(schema=schema_item.get('properties', {}),
124
                                                   config=config[config_item_key],
125
                                                   parent_keys=parent_keys)
126
            else:
127
                is_jinja_expression = jinja_utils.is_jinja_expression(value=config_item_value)
128
129
                if is_jinja_expression:
130
                    # Resolve / render the Jinja template expression
131
                    full_config_item_key = '.'.join(parent_keys + [config_item_key])
132
                    value = self._get_datastore_value_for_expression(key=full_config_item_key,
133
                        value=config_item_value,
134
                        config_schema_item=schema_item)
135
136
                    config[config_item_key] = value
137
                else:
138
                    # Static value, no resolution needed
139
                    config[config_item_key] = config_item_value
140
141
        return config
142
143
    def _assign_default_values(self, schema, config):
144
        """
145
        Assign default values for particular config if default values are provided in the config
146
        schema and a value is not specified in the config.
147
148
        Note: This method mutates config argument in place.
149
150
        :rtype: ``dict``
151
        """
152
        for schema_item_key, schema_item in six.iteritems(schema):
153
            default_value = schema_item.get('default', None)
154
            is_required = schema_item.get('required', False)
155
            is_object = schema_item.get('type', None) == 'object'
156
            has_properties = schema_item.get('properties', None)
157
158
            if is_required and default_value and not config.get(schema_item_key, None):
159
                config[schema_item_key] = default_value
160
161
            # Inspect nested object properties
162
            if is_object and has_properties:
163
                if not config.get(schema_item_key, None):
164
                    config[schema_item_key] = {}
165
166
                self._assign_default_values(schema=schema_item['properties'],
167
                                            config=config[schema_item_key])
168
169
        return config
170
171
    def _get_datastore_value_for_expression(self, key, value, config_schema_item=None):
172
        """
173
        Retrieve datastore value by first resolving the datastore expression and then retrieving
174
        the value from the datastore.
175
176
        :param key: Full path to the config item key (e.g. "token" / "auth.settings.token", etc.)
177
        """
178
        from st2common.services.config import deserialize_key_value
179
180
        config_schema_item = config_schema_item or {}
181
        secret = config_schema_item.get('secret', False)
182
183
        try:
184
            value = render_template_with_system_and_user_context(value=value,
185
                                                                 user=self.user)
186
        except Exception as e:
187
            # Throw a more user-friendly exception on failed render
188
            exc_class = type(e)
189
            original_msg = str(e)
190
            msg = ('Failed to render dynamic configuration value for key "%s" with value '
191
                   '"%s" for pack "%s" config: %s ' % (key, value, self.pack_name, original_msg))
192
            raise exc_class(msg)
193
194
        if value:
195
            # Deserialize the value
196
            value = deserialize_key_value(value=value, secret=secret)
197
        else:
198
            value = None
199
200
        return value
201