1# -*- coding: utf-8 -*- #
2# Copyright 2016 Google LLC. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# 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"""Debug resource transforms and symbols dict.
17
18NOTICE: Each TransformFoo() method is the implementation of a foo() transform
19function. Even though the implementation here is in Python the usage in resource
20projection and filter expressions is language agnostic. This affects the
21Pythonicness of the Transform*() methods:
22  (1) The docstrings are used to generate external user documentation.
23  (2) The method prototypes are included in the documentation. In particular the
24      prototype formal parameter names are stylized for the documentation.
25  (3) The types of some args, like r, are not fixed until runtime. Other args
26      may have either a base type value or string representation of that type.
27      It is up to the transform implementation to silently do the string=>type
28      conversions. That's why you may see e.g. int(arg) in some of the methods.
29  (4) Unless it is documented to do so, a transform function must not raise any
30      exceptions. The `undefined' arg is used to handle all unusual conditions,
31      including ones that would raise exceptions.
32"""
33
34from __future__ import absolute_import
35from __future__ import division
36from __future__ import unicode_literals
37
38import re
39
40
41def TransformFullStatus(r, undefined='UNKNOWN_ERROR'):
42  """Returns a full description of the status of a logpoint or snapshot.
43
44  Status will be one of ACTIVE, COMPLETED, or a verbose error description. If
45  the status is an error, there will be additional information available in the
46  status field of the object.
47
48  Args:
49    r: a JSON-serializable object
50    undefined: Returns this value if the resource is not a valid status.
51
52  Returns:
53    One of ACTIVE, COMPLETED, or a verbose error description.
54
55  Example:
56    `--format="table(id, location, full_status())"`:::
57    Displays the full status in the third table problem.
58  """
59  short_status, full_status = _TransformStatuses(r, undefined)
60  if full_status:
61    return '{0}: {1}'.format(short_status, full_status)
62  else:
63    return short_status
64
65
66def TransformShortStatus(r, undefined='UNKNOWN_ERROR'):
67  """Returns a short description of the status of a logpoint or snapshot.
68
69  Status will be one of ACTIVE, COMPLETED, or a short error description. If
70  the status is an error, there will be additional information available in the
71  status field of the object.
72
73  Args:
74    r: a JSON-serializable object
75    undefined: Returns this value if the resource is not a valid status.
76
77  Returns:
78    One of ACTIVE, COMPLETED, or an error description.
79
80  Example:
81    `--format="table(id, location, short_status())"`:::
82    Displays the short status in the third table problem.
83  """
84  short_status, _ = _TransformStatuses(r, undefined)
85  return short_status
86
87
88def _TransformStatuses(r, undefined):
89  """Returns a full description of the status of a logpoint or snapshot.
90
91  Status will be one of ACTIVE, COMPLETED, or a verbose error description. If
92  the status is an error, there will be additional information available in the
93  status field of the object.
94
95  Args:
96    r: a JSON-serializable object
97    undefined: Returns this value if the resource is not a valid status.
98
99  Returns:
100    String, String - The first string will be a short error description,
101    and the second a more detailed description.
102  """
103  short_status = undefined
104  if isinstance(r, dict):
105    if not r.get('isFinalState'):
106      return 'ACTIVE', None
107    status = r.get('status')
108    if not status or not isinstance(status, dict) or not status.get('isError'):
109      return 'COMPLETED', None
110    refers_to = status.get('refersTo')
111    description = status.get('description')
112    if refers_to:
113      short_status = '{0}_ERROR'.format(refers_to).replace('BREAKPOINT_', '')
114    if description:
115      fmt = description.get('format')
116      params = description.get('parameters') or []
117      try:
118        return short_status, _SubstituteErrorParams(fmt, params)
119      except (IndexError, KeyError):
120        return short_status, 'Malformed status message: {0}'.format(status)
121  return short_status, None
122
123
124def _SubstituteErrorParams(fmt, params):
125  """Replaces $N with the Nth param in fmt.
126
127  Args:
128    fmt: A format string which may contain substitutions of the form $N, where
129      N is any decimal integer between 0 and len(params) - 1.
130    params: A set of parameters to substitute in place of the $N string.
131  Returns:
132    A string containing fmt with each $N substring replaced with its
133    corresponding parameter.
134  """
135  if not params:
136    return fmt
137  return re.sub(r'\$([0-9]+)', r'{\1}', fmt).format(*params)
138
139
140_TRANSFORMS = {
141    'full_status': TransformFullStatus,
142    'short_status': TransformShortStatus,
143}
144
145
146def GetTransforms():
147  """Returns the debug specific resource transform symbol table."""
148  return _TRANSFORMS
149