We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Service logs does not appear in online docs
https://github.com/docker/docker-py/blob/e78e4e7491da7055151bfe454282770786a8c270/docker/models/services.py#L85

https://docker-py.readthedocs.io/en/stable/services.html

--- END ISSUE ---

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

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/docker/models/services.py b/docker/models/services.py
--- a/docker/models/services.py
+++ b/docker/models/services.py
@@ -42,7 +42,7 @@
                 ``label``, and ``desired-state``.
 
         Returns:
-            (:py:class:`list`): List of task dictionaries.
+            :py:class:`list`: List of task dictionaries.
 
         Raises:
             :py:class:`docker.errors.APIError`
@@ -84,26 +84,27 @@
 
     def logs(self, **kwargs):
         """
-            Get log stream for the service.
-            Note: This method works only for services with the ``json-file``
-            or ``journald`` logging drivers.
-
-            Args:
-                details (bool): Show extra details provided to logs.
-                    Default: ``False``
-                follow (bool): Keep connection open to read logs as they are
-                    sent by the Engine. Default: ``False``
-                stdout (bool): Return logs from ``stdout``. Default: ``False``
-                stderr (bool): Return logs from ``stderr``. Default: ``False``
-                since (int): UNIX timestamp for the logs staring point.
-                    Default: 0
-                timestamps (bool): Add timestamps to every log line.
-                tail (string or int): Number of log lines to be returned,
-                    counting from the current end of the logs. Specify an
-                    integer or ``'all'`` to output all log lines.
-                    Default: ``all``
-
-            Returns (generator): Logs for the service.
+        Get log stream for the service.
+        Note: This method works only for services with the ``json-file``
+        or ``journald`` logging drivers.
+
+        Args:
+            details (bool): Show extra details provided to logs.
+                Default: ``False``
+            follow (bool): Keep connection open to read logs as they are
+                sent by the Engine. Default: ``False``
+            stdout (bool): Return logs from ``stdout``. Default: ``False``
+            stderr (bool): Return logs from ``stderr``. Default: ``False``
+            since (int): UNIX timestamp for the logs staring point.
+                Default: 0
+            timestamps (bool): Add timestamps to every log line.
+            tail (string or int): Number of log lines to be returned,
+                counting from the current end of the logs. Specify an
+                integer or ``'all'`` to output all log lines.
+                Default: ``all``
+
+        Returns:
+            generator: Logs for the service.
         """
         is_tty = self.attrs['Spec']['TaskTemplate']['ContainerSpec'].get(
             'TTY', False
@@ -118,7 +119,7 @@
             replicas (int): The number of containers that should be running.
 
         Returns:
-            ``True``if successful.
+            bool: ``True`` if successful.
         """
 
         if 'Global' in self.attrs['Spec']['Mode'].keys():
@@ -134,7 +135,7 @@
         Force update the service even if no changes require it.
 
         Returns:
-            ``True``if successful.
+            bool: ``True`` if successful.
         """
 
         return self.update(force_update=True, fetch_current_spec=True)
@@ -206,7 +207,7 @@
                 containers.
 
         Returns:
-            (:py:class:`Service`) The created service.
+            :py:class:`Service`: The created service.
 
         Raises:
             :py:class:`docker.errors.APIError`
@@ -228,7 +229,7 @@
                 into the output.
 
         Returns:
-            (:py:class:`Service`): The service.
+            :py:class:`Service`: The service.
 
         Raises:
             :py:class:`docker.errors.NotFound`
@@ -253,7 +254,7 @@
                 Default: ``None``.
 
         Returns:
-            (list of :py:class:`Service`): The services.
+            list of :py:class:`Service`: The services.
 
         Raises:
             :py:class:`docker.errors.APIError`

{"golden_diff": "diff --git a/docker/models/services.py b/docker/models/services.py\n--- a/docker/models/services.py\n+++ b/docker/models/services.py\n@@ -42,7 +42,7 @@\n                 ``label``, and ``desired-state``.\n \n         Returns:\n-            (:py:class:`list`): List of task dictionaries.\n+            :py:class:`list`: List of task dictionaries.\n \n         Raises:\n             :py:class:`docker.errors.APIError`\n@@ -84,26 +84,27 @@\n \n     def logs(self, **kwargs):\n         \"\"\"\n-            Get log stream for the service.\n-            Note: This method works only for services with the ``json-file``\n-            or ``journald`` logging drivers.\n-\n-            Args:\n-                details (bool): Show extra details provided to logs.\n-                    Default: ``False``\n-                follow (bool): Keep connection open to read logs as they are\n-                    sent by the Engine. Default: ``False``\n-                stdout (bool): Return logs from ``stdout``. Default: ``False``\n-                stderr (bool): Return logs from ``stderr``. Default: ``False``\n-                since (int): UNIX timestamp for the logs staring point.\n-                    Default: 0\n-                timestamps (bool): Add timestamps to every log line.\n-                tail (string or int): Number of log lines to be returned,\n-                    counting from the current end of the logs. Specify an\n-                    integer or ``'all'`` to output all log lines.\n-                    Default: ``all``\n-\n-            Returns (generator): Logs for the service.\n+        Get log stream for the service.\n+        Note: This method works only for services with the ``json-file``\n+        or ``journald`` logging drivers.\n+\n+        Args:\n+            details (bool): Show extra details provided to logs.\n+                Default: ``False``\n+            follow (bool): Keep connection open to read logs as they are\n+                sent by the Engine. Default: ``False``\n+            stdout (bool): Return logs from ``stdout``. Default: ``False``\n+            stderr (bool): Return logs from ``stderr``. Default: ``False``\n+            since (int): UNIX timestamp for the logs staring point.\n+                Default: 0\n+            timestamps (bool): Add timestamps to every log line.\n+            tail (string or int): Number of log lines to be returned,\n+                counting from the current end of the logs. Specify an\n+                integer or ``'all'`` to output all log lines.\n+                Default: ``all``\n+\n+        Returns:\n+            generator: Logs for the service.\n         \"\"\"\n         is_tty = self.attrs['Spec']['TaskTemplate']['ContainerSpec'].get(\n             'TTY', False\n@@ -118,7 +119,7 @@\n             replicas (int): The number of containers that should be running.\n \n         Returns:\n-            ``True``if successful.\n+            bool: ``True`` if successful.\n         \"\"\"\n \n         if 'Global' in self.attrs['Spec']['Mode'].keys():\n@@ -134,7 +135,7 @@\n         Force update the service even if no changes require it.\n \n         Returns:\n-            ``True``if successful.\n+            bool: ``True`` if successful.\n         \"\"\"\n \n         return self.update(force_update=True, fetch_current_spec=True)\n@@ -206,7 +207,7 @@\n                 containers.\n \n         Returns:\n-            (:py:class:`Service`) The created service.\n+            :py:class:`Service`: The created service.\n \n         Raises:\n             :py:class:`docker.errors.APIError`\n@@ -228,7 +229,7 @@\n                 into the output.\n \n         Returns:\n-            (:py:class:`Service`): The service.\n+            :py:class:`Service`: The service.\n \n         Raises:\n             :py:class:`docker.errors.NotFound`\n@@ -253,7 +254,7 @@\n                 Default: ``None``.\n \n         Returns:\n-            (list of :py:class:`Service`): The services.\n+            list of :py:class:`Service`: The services.\n \n         Raises:\n             :py:class:`docker.errors.APIError`\n", "issue": "Service logs does not appear in online docs\nhttps://github.com/docker/docker-py/blob/e78e4e7491da7055151bfe454282770786a8c270/docker/models/services.py#L85\r\n\r\nhttps://docker-py.readthedocs.io/en/stable/services.html\n", "before_files": [{"content": "import copy\nfrom docker.errors import create_unexpected_kwargs_error, InvalidArgument\nfrom docker.types import TaskTemplate, ContainerSpec, Placement, ServiceMode\nfrom .resource import Model, Collection\n\n\nclass Service(Model):\n    \"\"\"A service.\"\"\"\n    id_attribute = 'ID'\n\n    @property\n    def name(self):\n        \"\"\"The service's name.\"\"\"\n        return self.attrs['Spec']['Name']\n\n    @property\n    def version(self):\n        \"\"\"\n        The version number of the service. If this is not the same as the\n        server, the :py:meth:`update` function will not work and you will\n        need to call :py:meth:`reload` before calling it again.\n        \"\"\"\n        return self.attrs.get('Version').get('Index')\n\n    def remove(self):\n        \"\"\"\n        Stop and remove the service.\n\n        Raises:\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n        \"\"\"\n        return self.client.api.remove_service(self.id)\n\n    def tasks(self, filters=None):\n        \"\"\"\n        List the tasks in this service.\n\n        Args:\n            filters (dict): A map of filters to process on the tasks list.\n                Valid filters: ``id``, ``name``, ``node``,\n                ``label``, and ``desired-state``.\n\n        Returns:\n            (:py:class:`list`): List of task dictionaries.\n\n        Raises:\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n        \"\"\"\n        if filters is None:\n            filters = {}\n        filters['service'] = self.id\n        return self.client.api.tasks(filters=filters)\n\n    def update(self, **kwargs):\n        \"\"\"\n        Update a service's configuration. Similar to the ``docker service\n        update`` command.\n\n        Takes the same parameters as :py:meth:`~ServiceCollection.create`.\n\n        Raises:\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n        \"\"\"\n        # Image is required, so if it hasn't been set, use current image\n        if 'image' not in kwargs:\n            spec = self.attrs['Spec']['TaskTemplate']['ContainerSpec']\n            kwargs['image'] = spec['Image']\n\n        if kwargs.get('force_update') is True:\n            task_template = self.attrs['Spec']['TaskTemplate']\n            current_value = int(task_template.get('ForceUpdate', 0))\n            kwargs['force_update'] = current_value + 1\n\n        create_kwargs = _get_create_service_kwargs('update', kwargs)\n\n        return self.client.api.update_service(\n            self.id,\n            self.version,\n            **create_kwargs\n        )\n\n    def logs(self, **kwargs):\n        \"\"\"\n            Get log stream for the service.\n            Note: This method works only for services with the ``json-file``\n            or ``journald`` logging drivers.\n\n            Args:\n                details (bool): Show extra details provided to logs.\n                    Default: ``False``\n                follow (bool): Keep connection open to read logs as they are\n                    sent by the Engine. Default: ``False``\n                stdout (bool): Return logs from ``stdout``. Default: ``False``\n                stderr (bool): Return logs from ``stderr``. Default: ``False``\n                since (int): UNIX timestamp for the logs staring point.\n                    Default: 0\n                timestamps (bool): Add timestamps to every log line.\n                tail (string or int): Number of log lines to be returned,\n                    counting from the current end of the logs. Specify an\n                    integer or ``'all'`` to output all log lines.\n                    Default: ``all``\n\n            Returns (generator): Logs for the service.\n        \"\"\"\n        is_tty = self.attrs['Spec']['TaskTemplate']['ContainerSpec'].get(\n            'TTY', False\n        )\n        return self.client.api.service_logs(self.id, is_tty=is_tty, **kwargs)\n\n    def scale(self, replicas):\n        \"\"\"\n        Scale service container.\n\n        Args:\n            replicas (int): The number of containers that should be running.\n\n        Returns:\n            ``True``if successful.\n        \"\"\"\n\n        if 'Global' in self.attrs['Spec']['Mode'].keys():\n            raise InvalidArgument('Cannot scale a global container')\n\n        service_mode = ServiceMode('replicated', replicas)\n        return self.client.api.update_service(self.id, self.version,\n                                              mode=service_mode,\n                                              fetch_current_spec=True)\n\n    def force_update(self):\n        \"\"\"\n        Force update the service even if no changes require it.\n\n        Returns:\n            ``True``if successful.\n        \"\"\"\n\n        return self.update(force_update=True, fetch_current_spec=True)\n\n\nclass ServiceCollection(Collection):\n    \"\"\"Services on the Docker server.\"\"\"\n    model = Service\n\n    def create(self, image, command=None, **kwargs):\n        \"\"\"\n        Create a service. Similar to the ``docker service create`` command.\n\n        Args:\n            image (str): The image name to use for the containers.\n            command (list of str or str): Command to run.\n            args (list of str): Arguments to the command.\n            constraints (list of str): Placement constraints.\n            preferences (list of str): Placement preferences.\n            platforms (list of tuple): A list of platforms constraints\n                expressed as ``(arch, os)`` tuples\n            container_labels (dict): Labels to apply to the container.\n            endpoint_spec (EndpointSpec): Properties that can be configured to\n                access and load balance a service. Default: ``None``.\n            env (list of str): Environment variables, in the form\n                ``KEY=val``.\n            hostname (string): Hostname to set on the container.\n            isolation (string): Isolation technology used by the service's\n                containers. Only used for Windows containers.\n            labels (dict): Labels to apply to the service.\n            log_driver (str): Log driver to use for containers.\n            log_driver_options (dict): Log driver options.\n            mode (ServiceMode): Scheduling mode for the service.\n                Default:``None``\n            mounts (list of str): Mounts for the containers, in the form\n                ``source:target:options``, where options is either\n                ``ro`` or ``rw``.\n            name (str): Name to give to the service.\n            networks (list of str): List of network names or IDs to attach\n                the service to. Default: ``None``.\n            resources (Resources): Resource limits and reservations.\n            restart_policy (RestartPolicy): Restart policy for containers.\n            secrets (list of :py:class:`docker.types.SecretReference`): List\n                of secrets accessible to containers for this service.\n            stop_grace_period (int): Amount of time to wait for\n                containers to terminate before forcefully killing them.\n            update_config (UpdateConfig): Specification for the update strategy\n                of the service. Default: ``None``\n            rollback_config (RollbackConfig): Specification for the rollback\n                strategy of the service. Default: ``None``\n            user (str): User to run commands as.\n            workdir (str): Working directory for commands to run.\n            tty (boolean): Whether a pseudo-TTY should be allocated.\n            groups (:py:class:`list`): A list of additional groups that the\n                container process will run as.\n            open_stdin (boolean): Open ``stdin``\n            read_only (boolean): Mount the container's root filesystem as read\n                only.\n            stop_signal (string): Set signal to stop the service's containers\n            healthcheck (Healthcheck): Healthcheck\n                configuration for this service.\n            hosts (:py:class:`dict`): A set of host to IP mappings to add to\n                the container's `hosts` file.\n            dns_config (DNSConfig): Specification for DNS\n                related configurations in resolver configuration file.\n            configs (:py:class:`list`): List of :py:class:`ConfigReference`\n                that will be exposed to the service.\n            privileges (Privileges): Security options for the service's\n                containers.\n\n        Returns:\n            (:py:class:`Service`) The created service.\n\n        Raises:\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n        \"\"\"\n        kwargs['image'] = image\n        kwargs['command'] = command\n        create_kwargs = _get_create_service_kwargs('create', kwargs)\n        service_id = self.client.api.create_service(**create_kwargs)\n        return self.get(service_id)\n\n    def get(self, service_id, insert_defaults=None):\n        \"\"\"\n        Get a service.\n\n        Args:\n            service_id (str): The ID of the service.\n            insert_defaults (boolean): If true, default values will be merged\n                into the output.\n\n        Returns:\n            (:py:class:`Service`): The service.\n\n        Raises:\n            :py:class:`docker.errors.NotFound`\n                If the service does not exist.\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n            :py:class:`docker.errors.InvalidVersion`\n                If one of the arguments is not supported with the current\n                API version.\n        \"\"\"\n        return self.prepare_model(\n            self.client.api.inspect_service(service_id, insert_defaults)\n        )\n\n    def list(self, **kwargs):\n        \"\"\"\n        List services.\n\n        Args:\n            filters (dict): Filters to process on the nodes list. Valid\n                filters: ``id``, ``name`` , ``label`` and ``mode``.\n                Default: ``None``.\n\n        Returns:\n            (list of :py:class:`Service`): The services.\n\n        Raises:\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n        \"\"\"\n        return [\n            self.prepare_model(s)\n            for s in self.client.api.services(**kwargs)\n        ]\n\n\n# kwargs to copy straight over to ContainerSpec\nCONTAINER_SPEC_KWARGS = [\n    'args',\n    'command',\n    'configs',\n    'dns_config',\n    'env',\n    'groups',\n    'healthcheck',\n    'hostname',\n    'hosts',\n    'image',\n    'isolation',\n    'labels',\n    'mounts',\n    'open_stdin',\n    'privileges',\n    'read_only',\n    'secrets',\n    'stop_grace_period',\n    'stop_signal',\n    'tty',\n    'user',\n    'workdir',\n]\n\n# kwargs to copy straight over to TaskTemplate\nTASK_TEMPLATE_KWARGS = [\n    'networks',\n    'resources',\n    'restart_policy',\n]\n\n# kwargs to copy straight over to create_service\nCREATE_SERVICE_KWARGS = [\n    'name',\n    'labels',\n    'mode',\n    'update_config',\n    'endpoint_spec',\n]\n\nPLACEMENT_KWARGS = [\n    'constraints',\n    'preferences',\n    'platforms',\n]\n\n\ndef _get_create_service_kwargs(func_name, kwargs):\n    # Copy over things which can be copied directly\n    create_kwargs = {}\n    for key in copy.copy(kwargs):\n        if key in CREATE_SERVICE_KWARGS:\n            create_kwargs[key] = kwargs.pop(key)\n    container_spec_kwargs = {}\n    for key in copy.copy(kwargs):\n        if key in CONTAINER_SPEC_KWARGS:\n            container_spec_kwargs[key] = kwargs.pop(key)\n    task_template_kwargs = {}\n    for key in copy.copy(kwargs):\n        if key in TASK_TEMPLATE_KWARGS:\n            task_template_kwargs[key] = kwargs.pop(key)\n\n    if 'container_labels' in kwargs:\n        container_spec_kwargs['labels'] = kwargs.pop('container_labels')\n\n    placement = {}\n    for key in copy.copy(kwargs):\n        if key in PLACEMENT_KWARGS:\n            placement[key] = kwargs.pop(key)\n    placement = Placement(**placement)\n    task_template_kwargs['placement'] = placement\n\n    if 'log_driver' in kwargs:\n        task_template_kwargs['log_driver'] = {\n            'Name': kwargs.pop('log_driver'),\n            'Options': kwargs.pop('log_driver_options', {})\n        }\n\n    if func_name == 'update':\n        if 'force_update' in kwargs:\n            task_template_kwargs['force_update'] = kwargs.pop('force_update')\n\n        # fetch the current spec by default if updating the service\n        # through the model\n        fetch_current_spec = kwargs.pop('fetch_current_spec', True)\n        create_kwargs['fetch_current_spec'] = fetch_current_spec\n\n    # All kwargs should have been consumed by this point, so raise\n    # error if any are left\n    if kwargs:\n        raise create_unexpected_kwargs_error(func_name, kwargs)\n\n    container_spec = ContainerSpec(**container_spec_kwargs)\n    task_template_kwargs['container_spec'] = container_spec\n    create_kwargs['task_template'] = TaskTemplate(**task_template_kwargs)\n    return create_kwargs\n", "path": "docker/models/services.py"}], "after_files": [{"content": "import copy\nfrom docker.errors import create_unexpected_kwargs_error, InvalidArgument\nfrom docker.types import TaskTemplate, ContainerSpec, Placement, ServiceMode\nfrom .resource import Model, Collection\n\n\nclass Service(Model):\n    \"\"\"A service.\"\"\"\n    id_attribute = 'ID'\n\n    @property\n    def name(self):\n        \"\"\"The service's name.\"\"\"\n        return self.attrs['Spec']['Name']\n\n    @property\n    def version(self):\n        \"\"\"\n        The version number of the service. If this is not the same as the\n        server, the :py:meth:`update` function will not work and you will\n        need to call :py:meth:`reload` before calling it again.\n        \"\"\"\n        return self.attrs.get('Version').get('Index')\n\n    def remove(self):\n        \"\"\"\n        Stop and remove the service.\n\n        Raises:\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n        \"\"\"\n        return self.client.api.remove_service(self.id)\n\n    def tasks(self, filters=None):\n        \"\"\"\n        List the tasks in this service.\n\n        Args:\n            filters (dict): A map of filters to process on the tasks list.\n                Valid filters: ``id``, ``name``, ``node``,\n                ``label``, and ``desired-state``.\n\n        Returns:\n            :py:class:`list`: List of task dictionaries.\n\n        Raises:\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n        \"\"\"\n        if filters is None:\n            filters = {}\n        filters['service'] = self.id\n        return self.client.api.tasks(filters=filters)\n\n    def update(self, **kwargs):\n        \"\"\"\n        Update a service's configuration. Similar to the ``docker service\n        update`` command.\n\n        Takes the same parameters as :py:meth:`~ServiceCollection.create`.\n\n        Raises:\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n        \"\"\"\n        # Image is required, so if it hasn't been set, use current image\n        if 'image' not in kwargs:\n            spec = self.attrs['Spec']['TaskTemplate']['ContainerSpec']\n            kwargs['image'] = spec['Image']\n\n        if kwargs.get('force_update') is True:\n            task_template = self.attrs['Spec']['TaskTemplate']\n            current_value = int(task_template.get('ForceUpdate', 0))\n            kwargs['force_update'] = current_value + 1\n\n        create_kwargs = _get_create_service_kwargs('update', kwargs)\n\n        return self.client.api.update_service(\n            self.id,\n            self.version,\n            **create_kwargs\n        )\n\n    def logs(self, **kwargs):\n        \"\"\"\n        Get log stream for the service.\n        Note: This method works only for services with the ``json-file``\n        or ``journald`` logging drivers.\n\n        Args:\n            details (bool): Show extra details provided to logs.\n                Default: ``False``\n            follow (bool): Keep connection open to read logs as they are\n                sent by the Engine. Default: ``False``\n            stdout (bool): Return logs from ``stdout``. Default: ``False``\n            stderr (bool): Return logs from ``stderr``. Default: ``False``\n            since (int): UNIX timestamp for the logs staring point.\n                Default: 0\n            timestamps (bool): Add timestamps to every log line.\n            tail (string or int): Number of log lines to be returned,\n                counting from the current end of the logs. Specify an\n                integer or ``'all'`` to output all log lines.\n                Default: ``all``\n\n        Returns:\n            generator: Logs for the service.\n        \"\"\"\n        is_tty = self.attrs['Spec']['TaskTemplate']['ContainerSpec'].get(\n            'TTY', False\n        )\n        return self.client.api.service_logs(self.id, is_tty=is_tty, **kwargs)\n\n    def scale(self, replicas):\n        \"\"\"\n        Scale service container.\n\n        Args:\n            replicas (int): The number of containers that should be running.\n\n        Returns:\n            bool: ``True`` if successful.\n        \"\"\"\n\n        if 'Global' in self.attrs['Spec']['Mode'].keys():\n            raise InvalidArgument('Cannot scale a global container')\n\n        service_mode = ServiceMode('replicated', replicas)\n        return self.client.api.update_service(self.id, self.version,\n                                              mode=service_mode,\n                                              fetch_current_spec=True)\n\n    def force_update(self):\n        \"\"\"\n        Force update the service even if no changes require it.\n\n        Returns:\n            bool: ``True`` if successful.\n        \"\"\"\n\n        return self.update(force_update=True, fetch_current_spec=True)\n\n\nclass ServiceCollection(Collection):\n    \"\"\"Services on the Docker server.\"\"\"\n    model = Service\n\n    def create(self, image, command=None, **kwargs):\n        \"\"\"\n        Create a service. Similar to the ``docker service create`` command.\n\n        Args:\n            image (str): The image name to use for the containers.\n            command (list of str or str): Command to run.\n            args (list of str): Arguments to the command.\n            constraints (list of str): Placement constraints.\n            preferences (list of str): Placement preferences.\n            platforms (list of tuple): A list of platforms constraints\n                expressed as ``(arch, os)`` tuples\n            container_labels (dict): Labels to apply to the container.\n            endpoint_spec (EndpointSpec): Properties that can be configured to\n                access and load balance a service. Default: ``None``.\n            env (list of str): Environment variables, in the form\n                ``KEY=val``.\n            hostname (string): Hostname to set on the container.\n            isolation (string): Isolation technology used by the service's\n                containers. Only used for Windows containers.\n            labels (dict): Labels to apply to the service.\n            log_driver (str): Log driver to use for containers.\n            log_driver_options (dict): Log driver options.\n            mode (ServiceMode): Scheduling mode for the service.\n                Default:``None``\n            mounts (list of str): Mounts for the containers, in the form\n                ``source:target:options``, where options is either\n                ``ro`` or ``rw``.\n            name (str): Name to give to the service.\n            networks (list of str): List of network names or IDs to attach\n                the service to. Default: ``None``.\n            resources (Resources): Resource limits and reservations.\n            restart_policy (RestartPolicy): Restart policy for containers.\n            secrets (list of :py:class:`docker.types.SecretReference`): List\n                of secrets accessible to containers for this service.\n            stop_grace_period (int): Amount of time to wait for\n                containers to terminate before forcefully killing them.\n            update_config (UpdateConfig): Specification for the update strategy\n                of the service. Default: ``None``\n            rollback_config (RollbackConfig): Specification for the rollback\n                strategy of the service. Default: ``None``\n            user (str): User to run commands as.\n            workdir (str): Working directory for commands to run.\n            tty (boolean): Whether a pseudo-TTY should be allocated.\n            groups (:py:class:`list`): A list of additional groups that the\n                container process will run as.\n            open_stdin (boolean): Open ``stdin``\n            read_only (boolean): Mount the container's root filesystem as read\n                only.\n            stop_signal (string): Set signal to stop the service's containers\n            healthcheck (Healthcheck): Healthcheck\n                configuration for this service.\n            hosts (:py:class:`dict`): A set of host to IP mappings to add to\n                the container's `hosts` file.\n            dns_config (DNSConfig): Specification for DNS\n                related configurations in resolver configuration file.\n            configs (:py:class:`list`): List of :py:class:`ConfigReference`\n                that will be exposed to the service.\n            privileges (Privileges): Security options for the service's\n                containers.\n\n        Returns:\n            :py:class:`Service`: The created service.\n\n        Raises:\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n        \"\"\"\n        kwargs['image'] = image\n        kwargs['command'] = command\n        create_kwargs = _get_create_service_kwargs('create', kwargs)\n        service_id = self.client.api.create_service(**create_kwargs)\n        return self.get(service_id)\n\n    def get(self, service_id, insert_defaults=None):\n        \"\"\"\n        Get a service.\n\n        Args:\n            service_id (str): The ID of the service.\n            insert_defaults (boolean): If true, default values will be merged\n                into the output.\n\n        Returns:\n            :py:class:`Service`: The service.\n\n        Raises:\n            :py:class:`docker.errors.NotFound`\n                If the service does not exist.\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n            :py:class:`docker.errors.InvalidVersion`\n                If one of the arguments is not supported with the current\n                API version.\n        \"\"\"\n        return self.prepare_model(\n            self.client.api.inspect_service(service_id, insert_defaults)\n        )\n\n    def list(self, **kwargs):\n        \"\"\"\n        List services.\n\n        Args:\n            filters (dict): Filters to process on the nodes list. Valid\n                filters: ``id``, ``name`` , ``label`` and ``mode``.\n                Default: ``None``.\n\n        Returns:\n            list of :py:class:`Service`: The services.\n\n        Raises:\n            :py:class:`docker.errors.APIError`\n                If the server returns an error.\n        \"\"\"\n        return [\n            self.prepare_model(s)\n            for s in self.client.api.services(**kwargs)\n        ]\n\n\n# kwargs to copy straight over to ContainerSpec\nCONTAINER_SPEC_KWARGS = [\n    'args',\n    'command',\n    'configs',\n    'dns_config',\n    'env',\n    'groups',\n    'healthcheck',\n    'hostname',\n    'hosts',\n    'image',\n    'isolation',\n    'labels',\n    'mounts',\n    'open_stdin',\n    'privileges',\n    'read_only',\n    'secrets',\n    'stop_grace_period',\n    'stop_signal',\n    'tty',\n    'user',\n    'workdir',\n]\n\n# kwargs to copy straight over to TaskTemplate\nTASK_TEMPLATE_KWARGS = [\n    'networks',\n    'resources',\n    'restart_policy',\n]\n\n# kwargs to copy straight over to create_service\nCREATE_SERVICE_KWARGS = [\n    'name',\n    'labels',\n    'mode',\n    'update_config',\n    'endpoint_spec',\n]\n\nPLACEMENT_KWARGS = [\n    'constraints',\n    'preferences',\n    'platforms',\n]\n\n\ndef _get_create_service_kwargs(func_name, kwargs):\n    # Copy over things which can be copied directly\n    create_kwargs = {}\n    for key in copy.copy(kwargs):\n        if key in CREATE_SERVICE_KWARGS:\n            create_kwargs[key] = kwargs.pop(key)\n    container_spec_kwargs = {}\n    for key in copy.copy(kwargs):\n        if key in CONTAINER_SPEC_KWARGS:\n            container_spec_kwargs[key] = kwargs.pop(key)\n    task_template_kwargs = {}\n    for key in copy.copy(kwargs):\n        if key in TASK_TEMPLATE_KWARGS:\n            task_template_kwargs[key] = kwargs.pop(key)\n\n    if 'container_labels' in kwargs:\n        container_spec_kwargs['labels'] = kwargs.pop('container_labels')\n\n    placement = {}\n    for key in copy.copy(kwargs):\n        if key in PLACEMENT_KWARGS:\n            placement[key] = kwargs.pop(key)\n    placement = Placement(**placement)\n    task_template_kwargs['placement'] = placement\n\n    if 'log_driver' in kwargs:\n        task_template_kwargs['log_driver'] = {\n            'Name': kwargs.pop('log_driver'),\n            'Options': kwargs.pop('log_driver_options', {})\n        }\n\n    if func_name == 'update':\n        if 'force_update' in kwargs:\n            task_template_kwargs['force_update'] = kwargs.pop('force_update')\n\n        # fetch the current spec by default if updating the service\n        # through the model\n        fetch_current_spec = kwargs.pop('fetch_current_spec', True)\n        create_kwargs['fetch_current_spec'] = fetch_current_spec\n\n    # All kwargs should have been consumed by this point, so raise\n    # error if any are left\n    if kwargs:\n        raise create_unexpected_kwargs_error(func_name, kwargs)\n\n    container_spec = ContainerSpec(**container_spec_kwargs)\n    task_template_kwargs['container_spec'] = container_spec\n    create_kwargs['task_template'] = TaskTemplate(**task_template_kwargs)\n    return create_kwargs\n", "path": "docker/models/services.py"}]}