File: //snap/google-cloud-cli/396/lib/googlecloudsdk/api_lib/run/condition.py
# -*- coding: utf-8 -*- #
# Copyright 2018 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Wraps a Cloud Run Condition messages, making fields easier to access."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import collections
import sys
_SEVERITY_ERROR = 'Error'
_SEVERITY_WARNING = 'Warning'
collections_abc = collections
if sys.version_info > (3, 8):
collections_abc = collections.abc
def GetNonTerminalMessages(conditions, ignore_retry=False):
"""Get messages for non-terminal subconditions.
Only show a message for some non-terminal subconditions:
- if severity == warning
- if message is provided
Non-terminal subconditions that aren't warnings are effectively neutral,
so messages for these aren't included unless provided.
Args:
conditions: Conditions
ignore_retry: bool, if True, ignores the "Retry" condition
Returns:
list(str) messages of non-terminal subconditions
"""
messages = []
for c in conditions.NonTerminalSubconditions():
if ignore_retry and c == 'Retry':
continue
if conditions[c]['severity'] == _SEVERITY_WARNING:
messages.append('{}: {}'.format(
c, conditions[c]['message'] or 'Unknown Warning.'))
elif conditions[c]['message']:
messages.append('{}: {}'.format(c, conditions[c]['message']))
return messages
class Conditions(collections_abc.Mapping):
"""Represents the status Conditions of a resource in a dict-like way.
Resource means a Cloud Run resource, e.g: Configuration.
The conditions of a resource describe error, warning, and completion states of
the last set of operations on the resource. True is success, False is failure,
and "Unknown" is an operation in progress.
The special "ready condition" describes the overall success state of the
(last operation on) the resource.
Other conditions may be "terminal", in which case they are required to be True
for overall success of the operation, and being False indicates failure.
If a condition has a severity of "info" or "warning" in the API, it's not
terminal.
More info: https://github.com/knative/serving/blob/master/docs/spec/errors.md
Note, status field of conditions is converted to boolean type.
"""
def __init__(
self, conditions, ready_condition=None,
observed_generation=None, generation=None):
"""Constructor.
Args:
conditions: A list of objects of condition_class.
ready_condition: str, The one condition type that indicates it is ready.
observed_generation: The observedGeneration field of the status object
generation: The generation of the object. Incremented every time a user
changes the object directly.
"""
self._conditions = {}
for cond in conditions:
status = None # Unset or Unknown
if cond.status.lower() == 'true':
status = True
elif cond.status.lower() == 'false':
status = False
self._conditions[cond.type] = {
'severity': cond.severity,
'reason': cond.reason,
'message': cond.message,
'lastTransitionTime': cond.lastTransitionTime,
'status': status
}
self._ready_condition = ready_condition
self._fresh = (observed_generation is None or
(observed_generation == generation))
def __getitem__(self, key):
"""Implements evaluation of `self[key]`."""
return self._conditions[key]
def __contains__(self, item):
"""Implements evaluation of `item in self`."""
return any(cond_type == item for cond_type in self._conditions)
def __len__(self):
"""Implements evaluation of `len(self)`."""
return len(self._conditions)
def __iter__(self):
"""Returns a generator yielding the condition types."""
for cond_type in self._conditions:
yield cond_type
def TerminalSubconditions(self):
"""Yields keys of the conditions which if all True, Ready should be true."""
for k in self:
if (k != self._ready_condition and
(not self[k]['severity'] or self[k]['severity'] == _SEVERITY_ERROR)):
yield k
def NonTerminalSubconditions(self):
"""Yields keys of the conditions which do not directly affect Ready."""
for k in self:
if (k != self._ready_condition and self[k]['severity'] and
self[k]['severity'] != _SEVERITY_ERROR):
yield k
def TerminalCondition(self):
return self._ready_condition
def TerminalConditionReason(self):
"""Returns the reason of the terminal condition."""
if (
self._ready_condition
and self._ready_condition in self
and self[self._ready_condition]['reason']
):
return self[self._ready_condition]['reason']
return None
def DescriptiveMessage(self):
"""Descriptive message about what's happened to the last user operation."""
if (self._ready_condition and
self._ready_condition in self and
self[self._ready_condition]['message']):
return self[self._ready_condition]['message']
return None
def IsTerminal(self):
"""True if the resource has finished the last operation, for good or ill.
conditions are considered terminal if and only if the ready condition is
either true or false.
Returns:
A bool representing if terminal.
"""
if not self._ready_condition:
raise NotImplementedError()
if not self._fresh:
return False
if self._ready_condition not in self._conditions:
return False
return self._conditions[self._ready_condition]['status'] is not None
def IsReady(self):
"""Return True if the resource has succeeded its current operation."""
if not self.IsTerminal():
return False
return self._conditions[self._ready_condition]['status']
def IsFailed(self):
""""Return True if the resource has failed its current operation."""
return self.IsTerminal() and not self.IsReady()
def IsFresh(self):
return self._fresh