Browse Source

Merge "Log client-id in UCP API endpoints"

Zuul 1 week ago
parent
commit
4bab2d8ab8

+ 3
- 1
entrypoint.sh View File

@@ -23,7 +23,9 @@ if [ "$1" = 'server' ]; then
23 23
         --paste config:/etc/promenade/api-paste.ini \
24 24
         --enable-threads \
25 25
         --threads "${PROMENADE_THREADS}" \
26
-        --workers "${PROMENADE_WORKERS}"
26
+        --workers "${PROMENADE_WORKERS}" \
27
+        --logger "null file:/dev/null" \
28
+        --log-route "null health"
27 29
 fi
28 30
 
29 31
 exec ${@}

+ 2
- 2
promenade/config.py View File

@@ -60,8 +60,8 @@ class Configuration:
60 60
         return cls(documents=documents, **kwargs)
61 61
 
62 62
     @classmethod
63
-    def from_design_ref(cls, design_ref, **kwargs):
64
-        documents, use_dh_engine = dr.get_documents(design_ref)
63
+    def from_design_ref(cls, design_ref, ctx=None, **kwargs):
64
+        documents, use_dh_engine = dr.get_documents(design_ref, ctx)
65 65
 
66 66
         return cls(
67 67
             documents=documents,

+ 14
- 7
promenade/control/base.py View File

@@ -95,10 +95,10 @@ class PromenadeRequestContext(context.RequestContext):
95 95
     Context object for promenade resource requests
96 96
     """
97 97
 
98
-    def __init__(self, external_marker=None, policy_engine=None, **kwargs):
98
+    def __init__(self, context_marker=None, policy_engine=None, **kwargs):
99 99
         self.log_level = 'error'
100 100
         self.request_id = str(uuid.uuid4())
101
-        self.external_marker = external_marker
101
+        self.context_marker = context_marker
102 102
         self.policy_engine = policy_engine
103 103
         self.is_admin_project = False
104 104
         self.authenticated = False
@@ -123,8 +123,14 @@ class PromenadeRequestContext(context.RequestContext):
123 123
     def remove_role(self, role):
124 124
         self.roles = [x for x in self.roles if x != role]
125 125
 
126
-    def set_external_marker(self, marker):
127
-        self.external_marker = marker
126
+    def set_context_marker(self, context_marker):
127
+        self.context_marker = context_marker
128
+
129
+    def set_request_id(self, request_id):
130
+        self.request_id = request_id
131
+
132
+    def set_end_user(self, end_user):
133
+        self.end_user = end_user
128 134
 
129 135
     def set_policy_engine(self, engine):
130 136
         self.policy_engine = engine
@@ -144,9 +150,10 @@ class PromenadeRequestContext(context.RequestContext):
144 150
     def to_log_context(self):
145 151
         result = {}
146 152
 
147
-        result['request_id'] = self.request_id
148
-        result['external_id'] = self.external_marker
149
-        result['user'] = self.user
153
+        result['request_id'] = getattr(self, 'request_id', None)
154
+        result['context_marker'] = getattr(self, 'context_marker', None)
155
+        result['end_user'] = getattr(self, 'end_user', None)
156
+        result['user'] = getattr(self, 'user', None)
150 157
 
151 158
         return result
152 159
 

+ 61
- 11
promenade/control/middleware.py View File

@@ -26,8 +26,10 @@ class AuthMiddleware(object):
26 26
         ctx = req.context
27 27
         ctx.set_policy_engine(policy.policy_engine)
28 28
 
29
-        for k, v in req.headers.items():
30
-            LOG.debug("Request with header %s: %s" % (k, v))
29
+        # don't spam log with headers for health checks
30
+        if not req.url.endswith('/health'):
31
+            for k, v in req.headers.items():
32
+                LOG.debug("Request with header %s: %s" % (k, v))
31 33
 
32 34
         auth_status = req.get_header(
33 35
             'X-SERVICE-IDENTITY-STATUS')  # will be set to Confirmed or Invalid
@@ -100,21 +102,69 @@ class ContextMiddleware(object):
100 102
 
101 103
     def process_request(self, req, resp):
102 104
         ctx = req.context
103
-        ext_marker = req.get_header('X-Context-Marker')
104
-        if ext_marker is not None and self._is_uuid_like(ext_marker):
105
-            # external passed in an ok context marker
106
-            ctx.set_external_marker(ext_marker)
105
+        context_marker = req.get_header('X-CONTEXT-MARKER')
106
+        end_user = req.get_header('X-END-USER')
107
+        if context_marker is not None:
108
+            ctx.set_context_marker(context_marker)
107 109
         else:
108
-            # use the request id
109
-            ctx.set_external_marker(ctx.request_id)
110
+            ctx.set_context_marker(ctx.request_id)
111
+        if end_user is not None:
112
+            ctx.set_end_user(end_user)
113
+        else:
114
+            ctx.set_end_user(ctx.user)
110 115
 
111 116
 
112 117
 class LoggingMiddleware(object):
118
+    def process_request(self, req, resp):
119
+        # don't log health checks
120
+        if not req.url.endswith('/health'):
121
+            ctx = req.context
122
+            LOG.info(
123
+                "Request: %s %s %s",
124
+                req.method,
125
+                req.uri,
126
+                req.query_string,
127
+                ctx=ctx)
128
+
113 129
     def process_response(self, req, resp, resource, req_succeeded):
114 130
         ctx = req.context
115
-
116
-        resp.append_header('X-Promenade-Req', ctx.request_id)
117
-        LOG.info('%s %s - %s', req.method, req.uri, resp.status, ctx=ctx)
131
+        # only log health check responses if the check failed
132
+        if req.url.endswith('/health'):
133
+            resp_code = self._get_resp_code(resp)
134
+            if not resp_code == 204:
135
+                LOG.error(
136
+                    'Health check has failed with response status %s',
137
+                    resp.status,
138
+                    ctx=ctx)
139
+        else:
140
+            context_marker = getattr(ctx, 'context_marker', None)
141
+            request_id = getattr(ctx, 'request_id', None)
142
+            user = getattr(ctx, 'user', None)
143
+            end_user = getattr(ctx, 'end_user', None)
144
+            if context_marker is not None:
145
+                resp.append_header('X-CONTEXT-MARKER', context_marker)
146
+            if request_id is not None:
147
+                resp.append_header('X-DECKHAND-REQ', request_id)
148
+            if end_user is not None:
149
+                resp.append_header('X-END-USER', end_user)
150
+            if user is not None:
151
+                resp.append_header('X-USER-NAME', user)
152
+            LOG.info(
153
+                "Response: %s %s %s",
154
+                req.method,
155
+                req.uri,
156
+                resp.status,
157
+                ctx=ctx)
158
+
159
+    def _get_resp_code(self, resp):
160
+        # Falcon response object doesn't have a raw status code.
161
+        # Splits by the first space
162
+        try:
163
+            return int(resp.status.split(" ", 1)[0])
164
+        except ValueError:
165
+            # if for some reason this Falcon response doesn't have a valid
166
+            # status, return a high value sentinel
167
+            return 9999
118 168
 
119 169
 
120 170
 class NoAuthFilter(object):

+ 2
- 1
promenade/control/validatedesign.py View File

@@ -28,10 +28,11 @@ class ValidateDesignResource(base.BaseResource):
28 28
     def on_post(self, req, resp):
29 29
         result = ValidationMessage()
30 30
         try:
31
+            ctx = req.context
31 32
             json_data = self.req_json(req)
32 33
             href = json_data.get('href', None)
33 34
             config = Configuration.from_design_ref(
34
-                href, allow_missing_substitutions=False)
35
+                href, allow_missing_substitutions=False, ctx=ctx)
35 36
             result = validation.validate_all(config)
36 37
         except exceptions.InvalidFormatError as e:
37 38
             msg = "Invalid JSON Format: %s" % str(e)

+ 14
- 6
promenade/design_ref.py View File

@@ -14,15 +14,15 @@ _DECKHAND_PREFIX = 'deckhand+'
14 14
 DH_TIMEOUT = 10 * 60  # 10 Minute timeout for fetching from Deckhand.
15 15
 
16 16
 
17
-def get_documents(design_ref):
18
-    LOG.debug('Fetching design_ref="%s"', design_ref)
17
+def get_documents(design_ref, ctx=None):
18
+    LOG.debug('Fetching design_ref="%s"', design_ref, ctx=ctx)
19 19
     if design_ref.startswith(_DECKHAND_PREFIX):
20
-        response = _get_from_deckhand(design_ref)
20
+        response = _get_from_deckhand(design_ref, ctx)
21 21
         use_dh_engine = False
22 22
     else:
23 23
         response = _get_from_basic_web(design_ref)
24 24
         use_dh_engine = True
25
-    LOG.debug('Got response for design_ref="%s"', design_ref)
25
+    LOG.debug('Got response for design_ref="%s"', design_ref, ctx=ctx)
26 26
 
27 27
     response.raise_for_status()
28 28
 
@@ -33,12 +33,20 @@ def _get_from_basic_web(design_ref):
33 33
     return requests.get(design_ref)
34 34
 
35 35
 
36
-def _get_from_deckhand(design_ref):
36
+def _get_from_deckhand(design_ref, ctx=None):
37 37
     keystone_args = {}
38 38
     for attr in ('auth_url', 'password', 'project_domain_name', 'project_name',
39 39
                  'username', 'user_domain_name'):
40 40
         keystone_args[attr] = cfg.CONF.get('keystone_authtoken', {}).get(attr)
41
+    if ctx is not None:
42
+        addl_headers = {
43
+            'X-CONTEXT-MARKER': ctx.context_marker,
44
+            'X-END-USER': ctx.end_user
45
+        }
46
+    else:
47
+        addl_headers = {}
41 48
     auth = keystoneauth1.identity.v3.Password(**keystone_args)
42
-    session = keystoneauth1.session.Session(auth=auth)
49
+    session = keystoneauth1.session.Session(
50
+        auth=auth, additional_headers=addl_headers)
43 51
 
44 52
     return session.get(design_ref[len(_DECKHAND_PREFIX):], timeout=DH_TIMEOUT)

+ 6
- 2
promenade/logging.py View File

@@ -4,12 +4,16 @@ import logging.config
4 4
 
5 5
 __all__ = ['getLogger', 'setup']
6 6
 
7
-LOG_FORMAT = '%(asctime)s %(levelname)-8s %(request_id)s %(external_id)s %(user)s %(name)s:%(filename)s:%(lineno)3d:%(funcName)s %(message)s'  # noqa
7
+LOG_FORMAT = '%(asctime)s %(levelname)-8s ' \
8
+             'req_id=%(request_id)s ctx=%(context_marker)s ' \
9
+             'end_user=%(end_user)s user=%(user)s ' \
10
+             '%(name)s:%(filename)s:%(lineno)3d:%(funcName)s %(message)s'
8 11
 
9 12
 BLANK_CONTEXT_VALUES = [
10
-    'external_id',
13
+    'context_marker',
11 14
     'request_id',
12 15
     'user',
16
+    'end_user',
13 17
 ]
14 18
 
15 19
 DEFAULT_CONFIG = {

+ 1
- 1
requirements-direct.txt View File

@@ -14,4 +14,4 @@ pbr==3.0.1
14 14
 pyyaml==3.12
15 15
 requests==2.18.4
16 16
 uwsgi==2.0.15
17
-git+https://git.openstack.org/openstack/airship-deckhand@2f596fd62713bfd4149757a77a4943dd60b64cca
17
+git+https://git.openstack.org/openstack/airship-deckhand@c1d0ff683ee94626b709954ad442b4f202f714d5

Loading…
Cancel
Save