227 lines
6.8 KiB
Python
227 lines
6.8 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2025 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.
|
|
"""Wrapper for Cloud Run InstanceSplit messages."""
|
|
from __future__ import absolute_import
|
|
from __future__ import annotations
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
from __future__ import unicode_literals
|
|
|
|
import collections
|
|
|
|
from googlecloudsdk.core import exceptions
|
|
from googlecloudsdk.generated_clients.apis.run.v1 import run_v1_messages
|
|
|
|
try:
|
|
# Python 3.3 and above.
|
|
collections_abc = collections.abc
|
|
except AttributeError:
|
|
collections_abc = collections
|
|
|
|
|
|
class InvalidInstanceSplitSpecificationError(exceptions.Error):
|
|
"""Error to indicate an invalid instance split specification."""
|
|
|
|
pass
|
|
|
|
|
|
# Designated key value for latest.
|
|
# Revisions' names may not be uppercase, so this is distinct.
|
|
LATEST_REVISION_KEY = 'LATEST'
|
|
|
|
|
|
def NewInstanceSplit(messages, key: str, percent=None):
|
|
"""Creates a new InstanceSplit.
|
|
|
|
Args:
|
|
messages: The message module that defines InstanceSplit.
|
|
key: The key for the instance split assignment in the InstanceSplits
|
|
mapping.
|
|
percent: Optional percent of instance split to assign.
|
|
|
|
Returns:
|
|
The newly created InstanceSplit.
|
|
"""
|
|
if key == LATEST_REVISION_KEY:
|
|
result = messages.InstanceSplit(latestRevision=True, percent=percent)
|
|
else:
|
|
result = messages.InstanceSplit(revisionName=key, percent=percent)
|
|
return result
|
|
|
|
|
|
def GetKey(split: run_v1_messages.InstanceSplit):
|
|
"""Returns the key for a InstanceSplit.
|
|
|
|
Args:
|
|
split: InstanceSplit, the InstanceSplit to check
|
|
|
|
Returns:
|
|
LATEST_REVISION_KEY if split is for the latest revison or
|
|
split.revisionName if not.
|
|
"""
|
|
return LATEST_REVISION_KEY if split.latestRevision else split.revisionName
|
|
|
|
|
|
def SortKeyFromKey(key: str):
|
|
"""Sorted key function to order InstanceSplit keys.
|
|
|
|
InstanceSplits keys are one of:
|
|
o revisionName
|
|
o LATEST_REVISION_KEY
|
|
|
|
Note LATEST_REVISION_KEY is not a str so its ordering with respect
|
|
to revisionName keys is hard to predict.
|
|
|
|
Args:
|
|
key: Key for a InstanceSplits dictionary.
|
|
|
|
Returns:
|
|
A value that sorts by revisionName with LATEST_REVISION_KEY
|
|
last.
|
|
"""
|
|
if key == LATEST_REVISION_KEY:
|
|
result = (2, key)
|
|
else:
|
|
result = (1, key)
|
|
return result
|
|
|
|
|
|
def SortKeyFromSplit(split: run_v1_messages.InstanceSplit):
|
|
"""Sorted key function to order InstanceSplit objects by key.
|
|
|
|
Args:
|
|
split: A InstanceSplit.
|
|
|
|
Returns:
|
|
A value that sorts by revisionName with LATEST_REVISION_KEY
|
|
last.
|
|
"""
|
|
key = GetKey(split)
|
|
return SortKeyFromKey(key)
|
|
|
|
|
|
def _GetItemSortKey(split: run_v1_messages.InstanceSplit):
|
|
"""Key function for sorting InstanceSplit objects during __getitem__."""
|
|
# The list of InstanceSplits returned by InstanceSplits.__getitem__ needs to
|
|
# be sorted for comparisons on InstanceSplits instances to work correctly. The
|
|
# order of the list of instance split assignments for a given key should not
|
|
# affect equality. InstanceSplit is not hashable so a set is not an option.
|
|
percent = split.percent if split.percent else 0
|
|
return percent
|
|
|
|
|
|
class InstanceSplits(collections_abc.MutableMapping):
|
|
"""Wraps a repeated InstanceSplit message and provides dict-like access.
|
|
|
|
The dictionary key is one of
|
|
LATEST_REVISION_KEY for the latest revision
|
|
InstanceSplit.revisionName for InstanceSplits with a revision name.
|
|
|
|
The dictionary value is a list of all instance split assignments referencing
|
|
the same revision, either by name or the latest revision.
|
|
"""
|
|
|
|
def __init__(self, messages_module, to_wrap):
|
|
"""Constructs a new InstanceSplits instance.
|
|
|
|
The InstanceSplits instance wraps the to_wrap argument, which is a repeated
|
|
proto message. Operations that mutate to_wrap will usually occur through
|
|
this class, but that is not a requirement. Callers can directly mutate
|
|
to_wrap by accessing the proto directly.
|
|
|
|
Args:
|
|
messages_module: The message module that defines InstanceSplit.
|
|
to_wrap: The instance split assignments to wrap.
|
|
"""
|
|
self._messages = messages_module
|
|
self._m = to_wrap
|
|
self._instance_split_cls = self._messages.InstanceSplit
|
|
|
|
def __getitem__(self, key):
|
|
"""Gets a sorted list of instance split assignments associated with the given key.
|
|
|
|
Allows accessing instance split assignments based on the revision they
|
|
reference
|
|
(either directly by name or the latest ready revision by specifying
|
|
"LATEST" as the key).
|
|
|
|
Returns a sorted list of instance split assignments to support comparison
|
|
operations on
|
|
InstanceSplits objects which should be independent of the order of the
|
|
instance split assignments for a given key.
|
|
|
|
Args:
|
|
key: A revision name or "LATEST" to get the instance split assignments
|
|
for.
|
|
|
|
Returns:
|
|
A sorted list of instance split assignments associated with the given key.
|
|
|
|
Raises:
|
|
KeyError: If this object does not contain the given key.
|
|
"""
|
|
result = sorted(
|
|
(i for i in self._m if GetKey(i) == key), key=_GetItemSortKey
|
|
)
|
|
if not result:
|
|
raise KeyError(key)
|
|
return result
|
|
|
|
def __delitem__(self, key):
|
|
"""Not implemented for now."""
|
|
raise NotImplementedError()
|
|
|
|
def __setitem__(self, key, new_splits):
|
|
"""Not implemented for now."""
|
|
raise NotImplementedError()
|
|
|
|
def __contains__(self, key):
|
|
"""Implements evaluation of `item in self`."""
|
|
for split in self._m:
|
|
if key == GetKey(split):
|
|
return True
|
|
return False
|
|
|
|
@property
|
|
def _key_set(self):
|
|
"""A set containing the mapping's keys."""
|
|
return set(GetKey(i) for i in self._m)
|
|
|
|
def __len__(self):
|
|
"""Implements evaluation of `len(self)`."""
|
|
return len(self._key_set)
|
|
|
|
def __iter__(self):
|
|
"""Returns an iterator over the instance split assignment keys."""
|
|
return iter(self._key_set)
|
|
|
|
def MakeSerializable(self):
|
|
return self._m
|
|
|
|
def __repr__(self):
|
|
content = ', '.join('{}: {}'.format(k, v) for k, v in self.items())
|
|
return '[%s]' % content
|
|
|
|
def AddSplit(self, key, percent):
|
|
"""Add a new instance split assignments for the given key.
|
|
|
|
Mainly for testing.
|
|
|
|
Args:
|
|
key: Name of the revision (or "LATEST") to set the percent for.
|
|
percent: Percent of instance split to set.
|
|
"""
|
|
self._m.append(NewInstanceSplit(self._messages, key, percent))
|