Files
zuul/zuul/provider/statemachine.py
T
James E. Blair c53129c79f Improve error handling on deleted nodes
This change:
* catches exceptions in the node cleanup method so they don't
  stop the main run method
* implements a timeout on node deletion so that if we can't delete
  an instance from the cloud, we still eventually delete the node
* switches from monotonic to regular time on the state machine
  start time initializer (since unlike nodepool, this value will
  be used by multiple components).

Change-Id: I17d99dea5f0d165579fc7d804615a0ad09023d44
2024-10-19 09:44:39 -07:00

148 lines
4.3 KiB
Python

# Copyright 2021-2024 Acme Gating, LLC
#
# 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.
import time
class StateMachine:
START = 'start'
def __init__(self, initial_state):
self.state = initial_state.get("state", self.START)
self.complete = False
self.start_time = initial_state.get("start_time", time.time())
def advance(self):
pass
def toDict(self):
return dict(
state=self.state,
start_time=self.start_time,
)
class Instance:
"""Represents a cloud instance
This class is used by the State Machine Driver classes to
represent a standardized version of a remote cloud instance.
Implement this class in your driver, override the :py:meth:`load`
method, and supply as many of the fields as possible.
The following attributes are required:
* ready: bool (whether the instance is ready)
* deleted: bool (whether the instance is in a deleted state)
* interface_ip: str
* metadata: dict
The following are optional:
* public_ipv4: str
* public_ipv6: str
* private_ipv4: str
* cloud: str
* az: str
* region: str
* host_id: str
* driver_data: any
* slot: int
And the following are even more optional (as they are usually
already set from the image configuration):
* username: str
* python_path: str
* shell_type: str
* connection_port: str
* connection_type: str
* host_keys: [str]
This is extremely optional, in fact, it's difficult to imagine
that it's useful for anything other than the metastatic driver:
* node_attributes: dict
"""
def __init__(self):
self.ready = False
self.deleted = False
self.public_ipv4 = None
self.public_ipv6 = None
self.private_ipv4 = None
self.interface_ip = None
self.cloud = None
self.az = None
self.region = None
self.host_id = None
self.metadata = {}
self.driver_data = None
self.slot = None
@property
def external_id(self):
"""Return a string representation of external ID(s) for this instance,
for use in logging"""
raise NotImplementedError()
def __repr__(self):
state = []
if self.ready:
state.append('ready')
if self.deleted:
state.append('deleted')
state = ' '.join(state)
return '<{klass} {external_id} {state}>'.format(
klass=self.__class__.__name__,
external_id=self.external_id,
state=state)
def getQuotaInformation(self):
"""Return quota information about this instance.
:returns: A :py:class:`QuotaInformation` object.
"""
raise NotImplementedError()
class Resource:
"""Represents a cloud resource
This could be an instance, a disk, a floating IP, or anything
else. It is used by the driver to detect leaked resources so the
adapter can clean them up.
The `type` attribute should be an alphanumeric string suitable for
inclusion in a statsd metric name.
The `metadata` attribute is a dictionary of key/value pairs
initially supplied by the driver to the adapter when an instance
or image was created. This is used by the driver to detect leaked
resources. The adapter may add any other information to this
instance for its own bookeeping (resource type, id, etc).
The 'plural_metric_name' attribute is set in the constructor
automatically; override this value if necessary.
:param str type: The type of resource.
:param dict metadata: A dictionary of metadata for the resource.
"""
def __init__(self, metadata, type):
self.type = type
self.plural_metric_name = type + 's'
self.metadata = metadata