Repository: docker/docker-py

Issue:
Unable to set 'Hostname' on a service.
It is currently not possible to set the Hostname on a Service through the API. The only way to define it currently is through using the Low Level API.

Hostname is defined as part of a Service within the ContainerSpec map, with the key 'Hostname' (Spec['TaskTemplate']['ContainerSpec']['Hostname'])

I'm currently running into this issue when attempting to clone a manually created service that has Hostname name defined and have no way (other than diverting to the Low Level API which complicates everything) to define it on the cloned container.



Discussion:

Resolving patch:
diff --git a/docker/models/services.py b/docker/models/services.py
--- a/docker/models/services.py
+++ b/docker/models/services.py
@@ -96,6 +96,7 @@ def create(self, image, command=None, **kwargs):
                 access and load balance a service. Default: ``None``.
             env (list of str): Environment variables, in the form
                 ``KEY=val``.
+            hostname (string): Hostname to set on the container.
             labels (dict): Labels to apply to the service.
             log_driver (str): Log driver to use for containers.
             log_driver_options (dict): Log driver options.
@@ -176,6 +177,7 @@ def list(self, **kwargs):
     'command',
     'args',
     'env',
+    'hostname',
     'workdir',
     'user',
     'labels',
diff --git a/docker/types/services.py b/docker/types/services.py
--- a/docker/types/services.py
+++ b/docker/types/services.py
@@ -70,6 +70,7 @@ class ContainerSpec(dict):
         image (string): The image name to use for the container.
         command (string or list):  The command to be run in the image.
         args (:py:class:`list`): Arguments to the command.
+        hostname (string): The hostname to set on the container.
         env (dict): Environment variables.
         dir (string): The working directory for commands to run in.
         user (string): The user inside the container.
@@ -82,9 +83,9 @@ class ContainerSpec(dict):
         secrets (list of py:class:`SecretReference`): List of secrets to be
             made available inside the containers.
     """
-    def __init__(self, image, command=None, args=None, env=None, workdir=None,
-                 user=None, labels=None, mounts=None, stop_grace_period=None,
-                 secrets=None):
+    def __init__(self, image, command=None, args=None, hostname=None, env=None,
+                 workdir=None, user=None, labels=None, mounts=None,
+                 stop_grace_period=None, secrets=None):
         self['Image'] = image
 
         if isinstance(command, six.string_types):
@@ -92,6 +93,8 @@ def __init__(self, image, command=None, args=None, env=None, workdir=None,
         self['Command'] = command
         self['Args'] = args
 
+        if hostname is not None:
+            self['Hostname'] = hostname
         if env is not None:
             if isinstance(env, dict):
                 self['Env'] = format_environment(env)


Test patch:
diff --git a/tests/unit/models_services_test.py b/tests/unit/models_services_test.py
--- a/tests/unit/models_services_test.py
+++ b/tests/unit/models_services_test.py
@@ -9,6 +9,7 @@ def test_get_create_service_kwargs(self):
             'command': 'true',
             'name': 'somename',
             'labels': {'key': 'value'},
+            'hostname': 'test_host',
             'mode': 'global',
             'update_config': {'update': 'config'},
             'networks': ['somenet'],
@@ -47,6 +48,6 @@ def test_get_create_service_kwargs(self):
             'Options': {'foo': 'bar'}
         }
         assert set(task_template['ContainerSpec'].keys()) == set([
-            'Image', 'Command', 'Args', 'Env', 'Dir', 'User', 'Labels',
-            'Mounts', 'StopGracePeriod'
+            'Image', 'Command', 'Args', 'Hostname', 'Env', 'Dir', 'User',
+            'Labels', 'Mounts', 'StopGracePeriod'
         ])