def test_user_cap_no(self): size_byte = bitmath.GiB(1).to_Byte().value new_size = supplement_filesystem(size_byte, False) assert_that(new_size, equal_to(bitmath.GiB(1).to_Byte().value)) size_byte = bitmath.GiB(3).to_Byte().value new_size = supplement_filesystem(size_byte, False) assert_that(new_size, equal_to(bitmath.GiB(3).to_Byte().value))
class EMCVNXBlockAPIInterfaceTests( test_blockdevice.make_iblockdeviceapi_tests( blockdevice_api_factory=( lambda test_case: emcblockdeviceapi_for_test( # XXX A hack to work around the LUN name length limit. We # need a better way to store the cluster_id. unicode(uuid4()).split('-')[0], test_case) ), minimum_allocatable_size=int(bitmath.GiB(8).to_Byte().value), device_allocation_unit=int(bitmath.GiB(8).to_Byte().value), unknown_blockdevice_id_factory=lambda test: unicode(uuid4()) ) ): """
def create_lun(self, lun_name=None, size_gb=1, sp=None, host_access=None, is_thin=None, description=None, tiering_policy=None, is_repl_dst=None, snap_schedule=None, io_limit_policy=None, is_compression=None): size = int(bitmath.GiB(size_gb).to_Byte().value) return UnityLun.create(self._cli, lun_name, self, size, sp=sp, host_access=host_access, is_thin=is_thin, description=description, is_repl_dst=is_repl_dst, tiering_policy=tiering_policy, snap_schedule=snap_schedule, io_limit_policy=io_limit_policy, is_compression=is_compression)
def test_scheduler_deployment_script(): config = ClusterConfigImpl( host='host', port=1234, user='******', auth=AuthMethod.ASK, setup_actions=SetupActionsConfigImpl(dask=['echo ABC', 'echo DEF'])) script = get_worker_deployment_script( scheduler_address='tcp://localhost:1111/', bokeh_port=2222, scratch_subdir='/scratch', cores=12, memory_limit=bitmath.GiB(10), log_file='/home/user/log', config=config) expected = ( '#!/usr/bin/env bash\n' 'echo ABC\n' 'echo DEF\n' 'export PATH="$PATH:$(python -m site --user-base)/bin"\n' 'dask-worker tcp://localhost:1111/ --host 0.0.0.0 --bokeh' ' --bokeh-port 2222 --nanny --reconnect --nprocs 1 --nthreads 12' ' --memory-limit 10737418240 --local-directory /scratch' ' > /home/user/log 2>&1\n' 'exit $?') print() print(repr(script)) print(repr(expected)) assert script == expected
def _gib_to_bytes(size): """Convert size in bytes to GiB. :param size: The number of bytes. :returns: The size in gigabytes. """ return int(bitmath.GiB(size).bytes)
def allocate_nodes(self, nodes: int = 1, cores: int = 1, memory_per_node: Union[str, bitmath.Byte] = None, walltime: Union[str, Walltime] = None, native_args: Optional[ Dict[str, Optional[ str]]] = None) -> Nodes: # noqa, pylint: disable=bad-whitespace,line-too-long if memory_per_node is None: memory_per_node = bitmath.GiB(1) elif isinstance(memory_per_node, str): memory_per_node = bitmath.parse_string(memory_per_node) if walltime is None: walltime = Walltime(minutes=10) elif isinstance(walltime, str): walltime = Walltime.from_string(walltime) parameters = AllocationParameters(nodes=nodes, cores=cores, memory_per_node=memory_per_node, walltime=walltime, native_args=native_args) return allocate_slurm_nodes(parameters=parameters, config=self._config)
def test_with_format(self): """bitmath.format context mgr sets and restores formatting""" to_print = [ bitmath.Byte(101), bitmath.KiB(202), bitmath.MB(303), bitmath.GiB(404), bitmath.TB(505), bitmath.PiB(606), bitmath.EB(707) ] str_reps = [ "101.00-Byte", "202.00-KiB", "303.00-MB", "404.00-GiB", "505.00-TB", "606.00-PiB", "707.00-EB" ] # Make sure formatting looks right BEFORE the context manager self.assertEqual(str(bitmath.KiB(1.337)), "1.337 KiB") with bitmath.format("{value:.2f}-{unit}"): for (inst, inst_str) in zip(to_print, str_reps): self.assertEqual(str(inst), inst_str) # Make sure formatting looks right AFTER the context manager self.assertEqual(str(bitmath.KiB(1.337)), "1.337 KiB")
def test_parse_unsafe_NIST(self): """parse_string_unsafe can parse all accepted NIST inputs""" # Begin with the kilo unit because it's the most tricky (SI # defines the unit as a lower-case 'k') kilo_inputs = [ '100ki', '100Ki', '100kib', '100KiB', '100kiB' ] expected_kilo_result = bitmath.KiB(100) for ki in kilo_inputs: _parsed = bitmath.parse_string_unsafe(ki) self.assertEqual(_parsed, expected_kilo_result) self.assertIs(type(_parsed), type(expected_kilo_result)) # Now check for other easier to parse prefixes other_inputs = [ '100gi', '100Gi', '100gib', '100giB', '100GiB' ] expected_gig_result = bitmath.GiB(100) for gi in other_inputs: _parsed = bitmath.parse_string_unsafe(gi) self.assertEqual(_parsed, expected_gig_result) self.assertIs(type(_parsed), type(expected_gig_result))
def test_best_prefix_prefer_NIST_from_NIST(self): """NIST: Best prefix honors a NIST preference when starting with an NIST unit Start with a NIST (GiB) unit and prefer a NIST unit as the result (MiB)""" # This should be MiB(512.0) should_be_MiB = bitmath.GiB(0.5).best_prefix(system=bitmath.NIST) self.assertIs(type(should_be_MiB), bitmath.MiB)
def test_serialize_deserialize(): value = AllocationParameters(nodes=20, cores=10, memory_per_node=bitmath.GiB(20), walltime=Walltime(days=1, hours=12, minutes=5, seconds=32), native_args={ '--arg1': 'value 1', '--arg2': None, '--arg3': '65' }) serialized = value.serialize() assert serialized == { 'type': 'SerializableTypes.ALLOCATION_PARAMETERS', 'nodes': 20, 'cores': 10, 'memory_per_node': '20.0 GiB', 'walltime': '1-12:05:32', 'native_args': { '--arg1': 'value 1', '--arg2': None, '--arg3': '65' } } deserialized = AllocationParameters.deserialize(serialized=serialized) assert deserialized == value
def test_BitmathType_good_two_args(self): """Argparse: BitmathType - Works when given two correct parameters""" args = "--two-args 1337B 0.001GiB" result = self._parse_two_args(args) self.assertEqual(len(result.two_args), 2) self.assertIn(bitmath.Byte(1337), result.two_args) self.assertIn(bitmath.GiB(0.001), result.two_args)
def create_lun(self, lun_name=None, size_gb=1, sp=None, host_access=None, is_thin=None, description=None, tiering_policy=None, is_repl_dst=None, snap_schedule=None, is_snap_schedule_paused=None, skip_sync_to_remote_system=None, io_limit_policy=None, is_compression=None, is_advanced_dedup_enabled=None): size = int(bitmath.GiB(size_gb).to_Byte().value) return UnityLun.create( self._cli, lun_name, self, size, sp=sp, host_access=host_access, is_thin=is_thin, description=description, is_repl_dst=is_repl_dst, tiering_policy=tiering_policy, snap_schedule=snap_schedule, is_snap_schedule_paused=is_snap_schedule_paused, skip_sync_to_remote_system=skip_sync_to_remote_system, io_limit_policy=io_limit_policy, is_compression=is_compression, is_advanced_dedup_enabled=is_advanced_dedup_enabled)
def test_best_prefix_prefer_SI_from_NIST(self): """SI: Best prefix honors a SI preference when starting with a NIST unit Start with a NIST (GiB) unit and prefer a SI unit as the result (MB)""" # Start with GiB, an NIST unit should_be_MB = bitmath.GiB(0.5).best_prefix(system=bitmath.SI) self.assertIs(type(should_be_MB), bitmath.MB)
def test_parse_unsafe_NIST_units(self): """parse_string_unsafe can parse abbreviated NIST units (Gi, Ki, ...)""" nist_input = "100 Gi" expected_result = bitmath.GiB(100) self.assertEqual( bitmath.parse_string_unsafe(nist_input), expected_result)
def test_able_to_sync_nodes_before_and_after_wait(): user = USER_43 with ExitStack() as stack: stack.enter_context(disable_pytest_stdin()) stack.enter_context(set_up_key_location(user)) stack.enter_context(reset_environment(user)) stack.enter_context(set_password(get_test_user_password(user))) stack.enter_context(clear_deployment_sync_data(user)) cluster = show_cluster(name=TEST_CLUSTER) nodes = cluster.allocate_nodes() nodes_2 = None nodes_3 = None try: deployments = cluster.pull_deployments() assert not deployments.nodes cluster.push_deployment(deployment=nodes) nodes = None deployments = cluster.pull_deployments() print(deployments) assert len(deployments.nodes) == 1 nodes_2 = deployments.nodes[0] assert len(nodes_2) == 1 nodes_2.wait(timeout=SLURM_WAIT_TIMEOUT) assert nodes_2.running() node_2 = nodes_2[0] cluster.push_deployment(deployment=nodes_2) nodes_2 = None deployments = cluster.pull_deployments() print(deployments) assert len(deployments.nodes) == 1 nodes_3 = deployments.nodes[0] assert nodes_3.running() with pytest.raises(RuntimeError): nodes_3.wait() assert len(nodes_3) == 1 node_3 = nodes_3[0] assert node_2.host == node_3.host assert node_2.port == node_3.port assert node_3.resources.cpu_cores == 1 assert node_3.resources.memory_total == bitmath.GiB(1) print(node_3) assert node_3.run('whoami') == user finally: if nodes is not None: nodes.cancel() if nodes_2 is not None: nodes_2.cancel() if nodes_3 is not None: nodes_3.cancel()
def test_can_read_node_resources(): user = USER_39 with ExitStack() as stack: stack.enter_context(disable_pytest_stdin()) stack.enter_context(set_up_key_location(user)) stack.enter_context(reset_environment(user)) stack.enter_context(set_password(get_test_user_password(user))) cluster = show_cluster(name=TEST_CLUSTER) access_node = cluster.get_access_node() print(str(access_node)) assert str(access_node) == repr(access_node) assert access_node.resources.cpu_cores is None assert access_node.resources.memory_total is None start_stress_cpu(user=user, timeout=10) try: check_resources_in_believable_range(access_node.resources) finally: stop_stress_cpu(user=user) nodes = cluster.allocate_nodes(cores=1, memory_per_node=bitmath.GiB(0.8)) assert len(nodes) == 1 node = nodes[0] stack.enter_context(cancel_on_exit(nodes)) nodes.wait(timeout=SLURM_WAIT_TIMEOUT) assert nodes.running() assert node.resources.cpu_cores == 1 assert node.resources.memory_total == bitmath.GiB(0.8) start_stress_cpu(user=user, timeout=10) try: check_resources_in_believable_range(access_node.resources) finally: stop_stress_cpu(user=user) assert node.run('whoami') == user
def to_MiB(n): if "K" in n[1]: return int(round(bitmath.KiB(n[0]).to_MiB())) elif "M" in n[1]: return int(round(bitmath.MiB(n[0]).to_MiB())) elif "G" in n[1]: return int(round(bitmath.GiB(n[0]).to_MiB())) elif "T" in n[1]: return int(round(bitmath.TiB(n[0]).to_MiB())) else: return int(round(float(n[0])))
def test_click_BitmathType_good_two_args(self): @click.command() @click.argument('arg1', type=BitmathType()) @click.argument('arg2', type=BitmathType()) def func(arg1, arg2): click.echo(arg1) click.echo(arg2) result = self.runner.invoke(func, ['1337B', '0.001GiB']) self.assertFalse(result.exception) self.assertEqual(result.output.splitlines(), [str(bitmath.Byte(1337)), str(bitmath.GiB(0.001))])
def test_sort_heterogeneous_list(self): """Different types in a list can be sorted properly Define these with the bytes keyword so we don't lose our minds trying to figure out if the results are correct.""" first = bitmath.KiB(bytes=0) second = bitmath.GiB(bytes=1337) third = bitmath.Eb(bytes=2048) fourth = bitmath.Byte(bytes=96783) unsorted_list = [fourth, second, first, third] sorted_list = sorted(unsorted_list) self.assertIs(sorted_list[0], first) self.assertIs(sorted_list[1], second) self.assertIs(sorted_list[2], third) self.assertIs(sorted_list[3], fourth)
def setUp(self): self.bit = bitmath.Bit(1) self.byte = bitmath.Byte(1) # NIST units self.kib = bitmath.KiB(1) self.mib = bitmath.MiB(1) self.gib = bitmath.GiB(1) self.tib = bitmath.TiB(1) self.pib = bitmath.PiB(1) self.eib = bitmath.EiB(1) # SI units self.kb = bitmath.kB(1) self.mb = bitmath.MB(1) self.gb = bitmath.GB(1) self.tb = bitmath.TB(1) self.pb = bitmath.PB(1) self.eb = bitmath.EB(1)
def size_to_bytes(human_size): PARSE_REGEXP = r"(\d+)([MGTPE]i)" parse = re.compile(PARSE_REGEXP) try: size, unit = re.match(parse, human_size).group(1, 2) size = int(size) assert size > 0 if unit == 'Mi': return int(bitmath.MiB(size).to_Byte()) elif unit == 'Gi': return int(bitmath.GiB(size).to_Byte()) elif unit == 'Ti': return int(bitmath.TiB(size).to_Byte()) elif unit == 'Pi': return int(bitmath.PiB(size).to_Byte()) elif unit == 'Ei': return int(bitmath.EiB(size).to_Byte()) else: return 0 except Exception as e: return 0
def test_allocate_defaults(): user = USER_22 with ExitStack() as stack: stack.enter_context(disable_pytest_stdin()) stack.enter_context(set_up_key_location(user)) stack.enter_context(reset_environment(user)) stack.enter_context(set_password(get_test_user_password(user))) cluster = show_cluster(name=TEST_CLUSTER) nodes = cluster.allocate_nodes() stack.enter_context(cancel_on_exit(nodes)) assert len(nodes) == 1 node = nodes[0] nodes.wait(timeout=SLURM_WAIT_TIMEOUT) assert nodes.running() assert node.resources.cpu_cores == 1 assert node.resources.memory_total == bitmath.GiB(1) print(node) assert node.run('whoami') == user
def test_serialize_deserialize(): config = get_config_for_test() value = NodeImpl(config=config) value.make_allocated(host='node1', port=12323, cores=30, memory=bitmath.GiB(60), allocated_until=(datetime.datetime( 2018, 11, 12, 13, 14).replace(tzinfo=dateutil.tz.tzutc()))) serialized = value.serialize() assert serialized == { 'type': 'SerializableTypes.NODE_IMPL', 'host': 'node1', 'port': 12323, 'cores': 30, 'memory': '60.0 GiB', 'allocated_until': '2018-11-12T13:14:00+00:00' } deserialized = NodeImpl.deserialize(config=config, serialized=serialized) assert deserialized == value
def create_vmfs(self, vmfs_name=None, size_gb=1, sp=None, host_access=None, is_thin=None, description=None, tiering_policy=None, is_repl_dst=None, snap_schedule=None, is_snap_schedule_paused=None, skip_sync_to_remote_system=None, io_limit_policy=None, is_compression=None, major_version=None, block_size=None): size = int(bitmath.GiB(size_gb).to_Byte().value) return UnityLun.create( self._cli, vmfs_name, self, size, sp=sp, host_access=host_access, is_thin=is_thin, description=description, is_repl_dst=is_repl_dst, tiering_policy=tiering_policy, snap_schedule=snap_schedule, is_snap_schedule_paused=is_snap_schedule_paused, skip_sync_to_remote_system=skip_sync_to_remote_system, io_limit_policy=io_limit_policy, is_compression=is_compression, create_vmfs=True, major_version=major_version, block_size=block_size)
import os from uuid import uuid4 import yaml from flocker.node.agents import blockdevice from flocker.node.agents.test.test_blockdevice import ( make_iblockdeviceapi_tests) from flocker.node.agents.test.test_blockdevice import ( make_iprofiledblockdeviceapi_tests) from twisted.python.components import proxyForInterface from zope.interface import implementer from dell_storagecenter_driver.dell_storagecenter_blockdevice import ( create_driver_instance) MIN_ALLOCATION_SIZE = bitmath.GiB(1).bytes MIN_ALLOCATION_UNIT = MIN_ALLOCATION_SIZE LOG = logging.getLogger(__name__) @implementer(blockdevice.IBlockDeviceAPI, blockdevice.IProfiledBlockDeviceAPI) class TestDriver(proxyForInterface(blockdevice.IBlockDeviceAPI, 'original')): """Wrapper around driver class to provide test cleanup.""" def __init__(self, original): self.original = original self.volumes = {} def _cleanup(self): """Clean up testing artifacts.""" with self.original._client.open_connection() as api:
import bitmath from eliot import Logger from eliot import Message from eliot import startTask from flocker.node.agents import blockdevice from twisted.python import filepath from zope.interface import implementer from flocker.node.agents.blockdevice import ( AlreadyAttachedVolume, BlockDeviceVolume, IBlockDeviceAPI, UnknownVolume, UnattachedVolume, IProfiledBlockDeviceAPI) from solidfire_flocker_driver import sfapi from solidfire_flocker_driver import utils ALLOCATION_UNIT = bitmath.GiB(1).bytes logger = Logger() def initialize_driver(cluster_id, **kwargs): """Initialize a new instance of the SolidFire driver. :param kwargs['endpoint', 'vag_name', 'account_name'] :return: SolidFireBolockDeviceAPI object """ return SolidFireBlockDeviceAPI(str(cluster_id), **kwargs) @implementer(IBlockDeviceAPI) @implementer(IProfiledBlockDeviceAPI) class SolidFireBlockDeviceAPI(object):
def test_simple_round_down(self): """NIST: 1 MiB (as a GiB()) rounds down into a MiB()""" # Represent one MiB as a small GiB MiB_in_GiB = bitmath.GiB(bytes=1048576) # This should turn into a MiB self.assertIs(type(MiB_in_GiB.best_prefix()), bitmath.MiB)
def _GiB_to_Byte(size_gb): return bitmath.GiB(size_gb).to_Byte().value
def test_parse_string_unicode(self): """parse_string can handle a unicode string""" self.assertEqual( bitmath.parse_string(u"750 GiB"), bitmath.GiB(750))
class Config: """ The central configuration hub for the operator. To access the config from another module, import :data:`crate.operator.config.config` and access its attributes. """ #: Time in seconds for which the operator will continue and wait to #: bootstrap a cluster. Once this threshold has passed, a bootstrapping is #: considered failed BOOTSTRAP_TIMEOUT: Optional[int] = 1800 #: When set, enable special handling for the defind cloud provider, e.g. on #: AWS pass the availability zone as a CrateDB node attribute. CLOUD_PROVIDER: Optional[CloudProvider] = None #: The Docker image that contains scripts to run cluster backups. CLUSTER_BACKUP_IMAGE: Optional[str] = None #: The volume size for the ``PersistentVolume`` that is used as a storage #: location for Java heap dumps. DEBUG_VOLUME_SIZE: bitmath.Byte = bitmath.GiB(256) #: The Kubernetes storage class name for the ``PersistentVolume`` that is #: used as a storage location for Java heap dumps. DEBUG_VOLUME_STORAGE_CLASS: str = "crate-local" #: A list of image pull secrets. Separate names by ``,``. IMAGE_PULL_SECRETS: Optional[List[str]] = None #: JMX exporter version JMX_EXPORTER_VERSION: str #: The path the Kubernetes configuration to use. KUBECONFIG: Optional[str] = None #: The log level to use for all CrateDB operator related log messages. LOG_LEVEL: str = "INFO" #: Time in seconds for which the operator will continue and wait to perform #: a rolling restart of a cluster. Once this threshold has passed, a #: restart is considered failed. ROLLING_RESTART_TIMEOUT = 3600 #: Time in seconds for which the operator will continue and wait to scale a #: cluster up or down, including deallocating nodes before turning them #: off. Once the threshold has passed, a scaling operation is considered #: failed. SCALING_TIMEOUT = 3600 #: Enable several testing behaviors, such as relaxed pod anti-affinity to #: allow for easier testing in smaller Kubernetes clusters. TESTING: bool = False #: HTTP Basic Auth password for web requests made to :attr:`WEBHOOK_URL`. WEBHOOK_PASSWORD: Optional[str] = None #: Full URL where the operator will send HTTP POST requests to when certain #: events occured. WEBHOOK_URL: Optional[str] = None #: HTTP Basic Auth username for web requests made to :attr:`WEBHOOK_URL`. WEBHOOK_USERNAME: Optional[str] = None #: Which table are the running jobs stored in. This is only changed in tests. JOBS_TABLE: str = "sys.jobs" def __init__(self, *, prefix: str): self._prefix = prefix def load(self): bootstrap_timeout = self.env("BOOTSTRAP_TIMEOUT", default=str(self.BOOTSTRAP_TIMEOUT)) try: self.BOOTSTRAP_TIMEOUT = int(bootstrap_timeout) except ValueError: raise ConfigurationError( f"Invalid {self._prefix}BOOTSTRAP_TIMEOUT=" f"'{bootstrap_timeout}'. Needs to be a positive integer or 0.") if self.BOOTSTRAP_TIMEOUT < 0: raise ConfigurationError( f"Invalid {self._prefix}BOOTSTRAP_TIMEOUT=" f"'{bootstrap_timeout}'. Needs to be a positive integer or 0.") if self.BOOTSTRAP_TIMEOUT == 0: self.BOOTSTRAP_TIMEOUT = None cloud_provider = self.env("CLOUD_PROVIDER", default=self.CLOUD_PROVIDER) if cloud_provider is not None: try: self.CLOUD_PROVIDER = CloudProvider(cloud_provider) except ValueError: allowed = ", ".join(CloudProvider.__members__.values()) raise ConfigurationError( f"Invalid {self._prefix}CLOUD_PROVIDER=" f"'{cloud_provider}'. Needs to be of {allowed}.") self.CLUSTER_BACKUP_IMAGE = self.env("CLUSTER_BACKUP_IMAGE", default=self.CLUSTER_BACKUP_IMAGE) debug_volume_size = self.env("DEBUG_VOLUME_SIZE", default=str(self.DEBUG_VOLUME_SIZE)) try: self.DEBUG_VOLUME_SIZE = bitmath.parse_string(debug_volume_size) except ValueError: raise ConfigurationError( f"Invalid {self._prefix}DEBUG_VOLUME_SIZE='{debug_volume_size}'." ) self.DEBUG_VOLUME_STORAGE_CLASS = self.env( "DEBUG_VOLUME_STORAGE_CLASS", default=self.DEBUG_VOLUME_STORAGE_CLASS) secrets = self.env("IMAGE_PULL_SECRETS", default=self.IMAGE_PULL_SECRETS) if secrets is not None: self.IMAGE_PULL_SECRETS = [ s for s in (secret.strip() for secret in secrets.split(",")) if s ] self.JMX_EXPORTER_VERSION = self.env("JMX_EXPORTER_VERSION") self.KUBECONFIG = self.env("KUBECONFIG", default=self.KUBECONFIG) if self.KUBECONFIG is not None: # When the CRATEDB_OPERATOR_KUBECONFIG env var is set we need to # ensure that KUBECONFIG env var is set to the same value for # PyKube login of the Kopf framework to work correctly. os.environ["KUBECONFIG"] = self.KUBECONFIG else: self.KUBECONFIG = os.getenv("KUBECONFIG") if self.KUBECONFIG is not None: for path in self.KUBECONFIG.split(ENV_KUBECONFIG_PATH_SEPARATOR): if not os.path.exists(path): raise ConfigurationError( "The KUBECONFIG environment variable contains a path " f"'{path}' that does not exist.") self.LOG_LEVEL = self.env("LOG_LEVEL", default=self.LOG_LEVEL) level = logging.getLevelName(self.LOG_LEVEL) for logger_name in ("", "crate", "kopf", "kubernetes_asyncio"): logger = logging.getLogger(logger_name) logger.setLevel(level) rolling_restart_timeout = self.env("ROLLING_RESTART_TIMEOUT", default=str( self.ROLLING_RESTART_TIMEOUT)) try: self.ROLLING_RESTART_TIMEOUT = int(rolling_restart_timeout) except ValueError: raise ConfigurationError( f"Invalid {self._prefix}ROLLING_RESTART_TIMEOUT=" f"'{rolling_restart_timeout}'. Needs to be a positive integer or 0." ) if self.ROLLING_RESTART_TIMEOUT < 0: raise ConfigurationError( f"Invalid {self._prefix}ROLLING_RESTART_TIMEOUT=" f"'{rolling_restart_timeout}'. Needs to be a positive integer or 0." ) scaling_timeout = self.env("SCALING_TIMEOUT", default=str(self.SCALING_TIMEOUT)) try: self.SCALING_TIMEOUT = int(scaling_timeout) except ValueError: raise ConfigurationError( f"Invalid {self._prefix}SCALING_TIMEOUT=" f"'{scaling_timeout}'. Needs to be a positive integer or 0.") if self.SCALING_TIMEOUT < 0: raise ConfigurationError( f"Invalid {self._prefix}SCALING_TIMEOUT=" f"'{scaling_timeout}'. Needs to be a positive integer or 0.") testing = self.env("TESTING", default=str(self.TESTING)) self.TESTING = testing.lower() == "true" self.WEBHOOK_PASSWORD = self.env("WEBHOOK_PASSWORD", default=self.WEBHOOK_PASSWORD) self.WEBHOOK_URL = self.env("WEBHOOK_URL", default=self.WEBHOOK_URL) self.WEBHOOK_USERNAME = self.env("WEBHOOK_USERNAME", default=self.WEBHOOK_USERNAME) self.JOBS_TABLE = self.env("JOBS_TABLE", default=self.JOBS_TABLE) def env(self, name: str, *, default=UNDEFINED) -> str: """ Retrieve the environment variable ``name`` or fall-back to its default if provided. If no default is provided, a :exc:`~.ConfigurationError` is raised. """ full_name = f"{self._prefix}{name}" try: return os.environ[full_name] except KeyError: if default is UNDEFINED: # raise from None - so that the traceback of the original # exception (KeyError) is not printed # https://docs.python.org/3.8/reference/simple_stmts.html#the-raise-statement raise ConfigurationError( f"Required environment variable '{full_name}' is not set." ) from None return default