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 ( 6960a7...1d918b )
by
unknown
13:11 queued 06:59
created

ContentPackConfigLoader._assign_default_values()   C

Complexity

Conditions 7

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

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