Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

services.py 14KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. import copy
  2. from docker.errors import create_unexpected_kwargs_error, InvalidArgument
  3. from docker.types import TaskTemplate, ContainerSpec, Placement, ServiceMode
  4. from .resource import Model, Collection
  5. class Service(Model):
  6. """A service."""
  7. id_attribute = 'ID'
  8. @property
  9. def name(self):
  10. """The service's name."""
  11. return self.attrs['Spec']['Name']
  12. @property
  13. def version(self):
  14. """
  15. The version number of the service. If this is not the same as the
  16. server, the :py:meth:`update` function will not work and you will
  17. need to call :py:meth:`reload` before calling it again.
  18. """
  19. return self.attrs.get('Version').get('Index')
  20. def remove(self):
  21. """
  22. Stop and remove the service.
  23. Raises:
  24. :py:class:`docker.errors.APIError`
  25. If the server returns an error.
  26. """
  27. return self.client.api.remove_service(self.id)
  28. def tasks(self, filters=None):
  29. """
  30. List the tasks in this service.
  31. Args:
  32. filters (dict): A map of filters to process on the tasks list.
  33. Valid filters: ``id``, ``name``, ``node``,
  34. ``label``, and ``desired-state``.
  35. Returns:
  36. :py:class:`list`: List of task dictionaries.
  37. Raises:
  38. :py:class:`docker.errors.APIError`
  39. If the server returns an error.
  40. """
  41. if filters is None:
  42. filters = {}
  43. filters['service'] = self.id
  44. return self.client.api.tasks(filters=filters)
  45. def update(self, **kwargs):
  46. """
  47. Update a service's configuration. Similar to the ``docker service
  48. update`` command.
  49. Takes the same parameters as :py:meth:`~ServiceCollection.create`.
  50. Raises:
  51. :py:class:`docker.errors.APIError`
  52. If the server returns an error.
  53. """
  54. # Image is required, so if it hasn't been set, use current image
  55. if 'image' not in kwargs:
  56. spec = self.attrs['Spec']['TaskTemplate']['ContainerSpec']
  57. kwargs['image'] = spec['Image']
  58. if kwargs.get('force_update') is True:
  59. task_template = self.attrs['Spec']['TaskTemplate']
  60. current_value = int(task_template.get('ForceUpdate', 0))
  61. kwargs['force_update'] = current_value + 1
  62. create_kwargs = _get_create_service_kwargs('update', kwargs)
  63. return self.client.api.update_service(
  64. self.id,
  65. self.version,
  66. **create_kwargs
  67. )
  68. def logs(self, **kwargs):
  69. """
  70. Get log stream for the service.
  71. Note: This method works only for services with the ``json-file``
  72. or ``journald`` logging drivers.
  73. Args:
  74. details (bool): Show extra details provided to logs.
  75. Default: ``False``
  76. follow (bool): Keep connection open to read logs as they are
  77. sent by the Engine. Default: ``False``
  78. stdout (bool): Return logs from ``stdout``. Default: ``False``
  79. stderr (bool): Return logs from ``stderr``. Default: ``False``
  80. since (int): UNIX timestamp for the logs staring point.
  81. Default: 0
  82. timestamps (bool): Add timestamps to every log line.
  83. tail (string or int): Number of log lines to be returned,
  84. counting from the current end of the logs. Specify an
  85. integer or ``'all'`` to output all log lines.
  86. Default: ``all``
  87. Returns:
  88. generator: Logs for the service.
  89. """
  90. is_tty = self.attrs['Spec']['TaskTemplate']['ContainerSpec'].get(
  91. 'TTY', False
  92. )
  93. return self.client.api.service_logs(self.id, is_tty=is_tty, **kwargs)
  94. def scale(self, replicas):
  95. """
  96. Scale service container.
  97. Args:
  98. replicas (int): The number of containers that should be running.
  99. Returns:
  100. bool: ``True`` if successful.
  101. """
  102. if 'Global' in self.attrs['Spec']['Mode'].keys():
  103. raise InvalidArgument('Cannot scale a global container')
  104. service_mode = ServiceMode('replicated', replicas)
  105. return self.client.api.update_service(self.id, self.version,
  106. mode=service_mode,
  107. fetch_current_spec=True)
  108. def force_update(self):
  109. """
  110. Force update the service even if no changes require it.
  111. Returns:
  112. bool: ``True`` if successful.
  113. """
  114. return self.update(force_update=True, fetch_current_spec=True)
  115. class ServiceCollection(Collection):
  116. """Services on the Docker server."""
  117. model = Service
  118. def create(self, image, command=None, **kwargs):
  119. """
  120. Create a service. Similar to the ``docker service create`` command.
  121. Args:
  122. image (str): The image name to use for the containers.
  123. command (list of str or str): Command to run.
  124. args (list of str): Arguments to the command.
  125. constraints (list of str): :py:class:`~docker.types.Placement`
  126. constraints.
  127. preferences (list of tuple): :py:class:`~docker.types.Placement`
  128. preferences.
  129. maxreplicas (int): :py:class:`~docker.types.Placement` maxreplicas
  130. or (int) representing maximum number of replicas per node.
  131. platforms (list of tuple): A list of platform constraints
  132. expressed as ``(arch, os)`` tuples.
  133. container_labels (dict): Labels to apply to the container.
  134. endpoint_spec (EndpointSpec): Properties that can be configured to
  135. access and load balance a service. Default: ``None``.
  136. env (list of str): Environment variables, in the form
  137. ``KEY=val``.
  138. hostname (string): Hostname to set on the container.
  139. init (boolean): Run an init inside the container that forwards
  140. signals and reaps processes
  141. isolation (string): Isolation technology used by the service's
  142. containers. Only used for Windows containers.
  143. labels (dict): Labels to apply to the service.
  144. log_driver (str): Log driver to use for containers.
  145. log_driver_options (dict): Log driver options.
  146. mode (ServiceMode): Scheduling mode for the service.
  147. Default:``None``
  148. mounts (list of str): Mounts for the containers, in the form
  149. ``source:target:options``, where options is either
  150. ``ro`` or ``rw``.
  151. name (str): Name to give to the service.
  152. networks (:py:class:`list`): List of network names or IDs or
  153. :py:class:`~docker.types.NetworkAttachmentConfig` to attach the
  154. service to. Default: ``None``.
  155. resources (Resources): Resource limits and reservations.
  156. restart_policy (RestartPolicy): Restart policy for containers.
  157. secrets (list of :py:class:`~docker.types.SecretReference`): List
  158. of secrets accessible to containers for this service.
  159. stop_grace_period (int): Amount of time to wait for
  160. containers to terminate before forcefully killing them.
  161. update_config (UpdateConfig): Specification for the update strategy
  162. of the service. Default: ``None``
  163. rollback_config (RollbackConfig): Specification for the rollback
  164. strategy of the service. Default: ``None``
  165. user (str): User to run commands as.
  166. workdir (str): Working directory for commands to run.
  167. tty (boolean): Whether a pseudo-TTY should be allocated.
  168. groups (:py:class:`list`): A list of additional groups that the
  169. container process will run as.
  170. open_stdin (boolean): Open ``stdin``
  171. read_only (boolean): Mount the container's root filesystem as read
  172. only.
  173. stop_signal (string): Set signal to stop the service's containers
  174. healthcheck (Healthcheck): Healthcheck
  175. configuration for this service.
  176. hosts (:py:class:`dict`): A set of host to IP mappings to add to
  177. the container's `hosts` file.
  178. dns_config (DNSConfig): Specification for DNS
  179. related configurations in resolver configuration file.
  180. configs (:py:class:`list`): List of
  181. :py:class:`~docker.types.ConfigReference` that will be exposed
  182. to the service.
  183. privileges (Privileges): Security options for the service's
  184. containers.
  185. cap_add (:py:class:`list`): A list of kernel capabilities to add to
  186. the default set for the container.
  187. cap_drop (:py:class:`list`): A list of kernel capabilities to drop
  188. from the default set for the container.
  189. sysctls (:py:class:`dict`): A dict of sysctl values to add to the
  190. container
  191. Returns:
  192. :py:class:`Service`: The created service.
  193. Raises:
  194. :py:class:`docker.errors.APIError`
  195. If the server returns an error.
  196. """
  197. kwargs['image'] = image
  198. kwargs['command'] = command
  199. create_kwargs = _get_create_service_kwargs('create', kwargs)
  200. service_id = self.client.api.create_service(**create_kwargs)
  201. return self.get(service_id)
  202. def get(self, service_id, insert_defaults=None):
  203. """
  204. Get a service.
  205. Args:
  206. service_id (str): The ID of the service.
  207. insert_defaults (boolean): If true, default values will be merged
  208. into the output.
  209. Returns:
  210. :py:class:`Service`: The service.
  211. Raises:
  212. :py:class:`docker.errors.NotFound`
  213. If the service does not exist.
  214. :py:class:`docker.errors.APIError`
  215. If the server returns an error.
  216. :py:class:`docker.errors.InvalidVersion`
  217. If one of the arguments is not supported with the current
  218. API version.
  219. """
  220. return self.prepare_model(
  221. self.client.api.inspect_service(service_id, insert_defaults)
  222. )
  223. def list(self, **kwargs):
  224. """
  225. List services.
  226. Args:
  227. filters (dict): Filters to process on the nodes list. Valid
  228. filters: ``id``, ``name`` , ``label`` and ``mode``.
  229. Default: ``None``.
  230. status (bool): Include the service task count of running and
  231. desired tasks. Default: ``None``.
  232. Returns:
  233. list of :py:class:`Service`: The services.
  234. Raises:
  235. :py:class:`docker.errors.APIError`
  236. If the server returns an error.
  237. """
  238. return [
  239. self.prepare_model(s)
  240. for s in self.client.api.services(**kwargs)
  241. ]
  242. # kwargs to copy straight over to ContainerSpec
  243. CONTAINER_SPEC_KWARGS = [
  244. 'args',
  245. 'cap_add',
  246. 'cap_drop',
  247. 'command',
  248. 'configs',
  249. 'dns_config',
  250. 'env',
  251. 'groups',
  252. 'healthcheck',
  253. 'hostname',
  254. 'hosts',
  255. 'image',
  256. 'init',
  257. 'isolation',
  258. 'labels',
  259. 'mounts',
  260. 'open_stdin',
  261. 'privileges',
  262. 'read_only',
  263. 'secrets',
  264. 'stop_grace_period',
  265. 'stop_signal',
  266. 'tty',
  267. 'user',
  268. 'workdir',
  269. 'sysctls',
  270. ]
  271. # kwargs to copy straight over to TaskTemplate
  272. TASK_TEMPLATE_KWARGS = [
  273. 'networks',
  274. 'resources',
  275. 'restart_policy',
  276. ]
  277. # kwargs to copy straight over to create_service
  278. CREATE_SERVICE_KWARGS = [
  279. 'name',
  280. 'labels',
  281. 'mode',
  282. 'update_config',
  283. 'rollback_config',
  284. 'endpoint_spec',
  285. ]
  286. PLACEMENT_KWARGS = [
  287. 'constraints',
  288. 'preferences',
  289. 'platforms',
  290. 'maxreplicas',
  291. ]
  292. def _get_create_service_kwargs(func_name, kwargs):
  293. # Copy over things which can be copied directly
  294. create_kwargs = {}
  295. for key in copy.copy(kwargs):
  296. if key in CREATE_SERVICE_KWARGS:
  297. create_kwargs[key] = kwargs.pop(key)
  298. container_spec_kwargs = {}
  299. for key in copy.copy(kwargs):
  300. if key in CONTAINER_SPEC_KWARGS:
  301. container_spec_kwargs[key] = kwargs.pop(key)
  302. task_template_kwargs = {}
  303. for key in copy.copy(kwargs):
  304. if key in TASK_TEMPLATE_KWARGS:
  305. task_template_kwargs[key] = kwargs.pop(key)
  306. if 'container_labels' in kwargs:
  307. container_spec_kwargs['labels'] = kwargs.pop('container_labels')
  308. placement = {}
  309. for key in copy.copy(kwargs):
  310. if key in PLACEMENT_KWARGS:
  311. placement[key] = kwargs.pop(key)
  312. placement = Placement(**placement)
  313. task_template_kwargs['placement'] = placement
  314. if 'log_driver' in kwargs:
  315. task_template_kwargs['log_driver'] = {
  316. 'Name': kwargs.pop('log_driver'),
  317. 'Options': kwargs.pop('log_driver_options', {})
  318. }
  319. if func_name == 'update':
  320. if 'force_update' in kwargs:
  321. task_template_kwargs['force_update'] = kwargs.pop('force_update')
  322. # fetch the current spec by default if updating the service
  323. # through the model
  324. fetch_current_spec = kwargs.pop('fetch_current_spec', True)
  325. create_kwargs['fetch_current_spec'] = fetch_current_spec
  326. # All kwargs should have been consumed by this point, so raise
  327. # error if any are left
  328. if kwargs:
  329. raise create_unexpected_kwargs_error(func_name, kwargs)
  330. container_spec = ContainerSpec(**container_spec_kwargs)
  331. task_template_kwargs['container_spec'] = container_spec
  332. create_kwargs['task_template'] = TaskTemplate(**task_template_kwargs)
  333. return create_kwargs