From: John Mulligan Date: Fri, 23 Jun 2023 18:06:45 +0000 (-0400) Subject: python-common: add InitContainerSpec type X-Git-Tag: v19.0.0~711^2~10 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=857a6fe5fb43df91cf7a28d2fa9ad9d551b21dbb;p=ceph.git python-common: add InitContainerSpec type The InitContainerSpec type will be used to define explicit "init containers" - containers that are expected to run and then exit and are executed prior to running a primary container. Signed-off-by: John Mulligan --- diff --git a/src/python-common/ceph/deployment/service_spec.py b/src/python-common/ceph/deployment/service_spec.py index da3cbe7a86d..9ea5c36cb90 100644 --- a/src/python-common/ceph/deployment/service_spec.py +++ b/src/python-common/ceph/deployment/service_spec.py @@ -1372,6 +1372,103 @@ class IngressSpec(ServiceSpec): yaml.add_representer(IngressSpec, ServiceSpec.yaml_representer) +class InitContainerSpec(object): + """An init container is not a service that lives on its own, but rather + is used to run and exit prior to a service container starting in order + to help initialize some aspect of the container environment. + For example: a command to pre-populate a DB file with expected values + before the server starts. + """ + + _basic_fields = [ + 'image', + 'entrypoint', + 'volume_mounts', + 'envs', + 'privileged', + ] + _fields = _basic_fields + ['entrypoint_args'] + + def __init__( + self, + image: Optional[str] = None, + entrypoint: Optional[str] = None, + entrypoint_args: Optional[GeneralArgList] = None, + volume_mounts: Optional[Dict[str, str]] = None, + envs: Optional[List[str]] = None, + privileged: Optional[bool] = None, + ): + self.image = image + self.entrypoint = entrypoint + self.entrypoint_args: Optional[ArgumentList] = None + if entrypoint_args: + self.entrypoint_args = ArgumentSpec.from_general_args( + entrypoint_args + ) + self.volume_mounts = volume_mounts + self.envs = envs + self.privileged = privileged + self.validate() + + def validate(self) -> None: + if all(getattr(self, key) is None for key in self._fields): + raise SpecValidationError( + 'At least one parameter must be set (no values were specified)' + ) + + def to_json(self, flatten_args: bool = False) -> Dict[str, Any]: + data: Dict[str, Any] = {} + for key in self._basic_fields: + value = getattr(self, key, None) + if value is not None: + data[key] = value + if self.entrypoint_args and flatten_args: + data['entrypoint_args'] = sum( + (ea.to_args() for ea in self.entrypoint_args), [] + ) + elif self.entrypoint_args: + data['entrypoint_args'] = ArgumentSpec.map_json( + self.entrypoint_args + ) + return data + + def __repr__(self) -> str: + vals = ((key, getattr(self, key)) for key in self._fields) + contents = ", ".join(f'{key}={val!r}' for key, val in vals if val) + return f'InitContainerSpec({contents})' + + @classmethod + def from_json(cls, data: Dict[str, Any]) -> 'InitContainerSpec': + return cls( + image=data.get('image'), + entrypoint=data.get('entrypoint'), + entrypoint_args=data.get('entrypoint_args'), + volume_mounts=data.get('volume_mounts'), + envs=data.get('envs'), + privileged=data.get('privileged'), + ) + + @classmethod + def import_values( + cls, values: List[Union['InitContainerSpec', Dict[str, Any]]] + ) -> List['InitContainerSpec']: + out: List[InitContainerSpec] = [] + for value in values: + if isinstance(value, dict): + out.append(cls.from_json(value)) + elif isinstance(value, cls): + out.append(value) + elif hasattr(value, 'to_json'): + # This is a workaround for silly ceph mgr object/type identity + # mismatches due to multiple python interpreters in use. + out.append(cls.from_json(value.to_json())) + else: + raise SpecValidationError( + f"Unknown type for InitContainerSpec: {type(value)}" + ) + return out + + class CustomContainerSpec(ServiceSpec): def __init__(self, service_type: str = 'container',