From 91b227d133a33e2dd0b8cc06bba33bf389ff7e3f Mon Sep 17 00:00:00 2001
From: Karol Duleba <mr.fuxi@gmail.com>
Date: Wed, 9 Sep 2015 22:30:36 +0100
Subject: [PATCH] Allow to extend service using shorthand notation. Closes
 #1989

Signed-off-by: Karol Duleba <mr.fuxi@gmail.com>
---
 compose/config/config.py                      | 10 ++++++---
 compose/config/fields_schema.json             | 21 ++++++++++++-------
 .../extends/verbose-and-shorthand.yml         | 15 +++++++++++++
 tests/unit/config/config_test.py              | 20 ++++++++++++++++++
 4 files changed, 56 insertions(+), 10 deletions(-)
 create mode 100644 tests/fixtures/extends/verbose-and-shorthand.yml

diff --git a/compose/config/config.py b/compose/config/config.py
index 94c5ab95a..55c717f42 100644
--- a/compose/config/config.py
+++ b/compose/config/config.py
@@ -275,15 +275,19 @@ class ServiceLoader(object):
         self.service_dict['environment'] = env
 
     def validate_and_construct_extends(self):
+        extends = self.service_dict['extends']
+        if not isinstance(extends, dict):
+            extends = {'service': extends}
+
         validate_extends_file_path(
             self.service_name,
-            self.service_dict['extends'],
+            extends,
             self.filename
         )
         self.extended_config_path = self.get_extended_config_path(
-            self.service_dict['extends']
+            extends
         )
-        self.extended_service_name = self.service_dict['extends']['service']
+        self.extended_service_name = extends['service']
 
         full_extended_config = pre_process_config(
             load_yaml(self.extended_config_path)
diff --git a/compose/config/fields_schema.json b/compose/config/fields_schema.json
index 6fce299cb..07b17cb22 100644
--- a/compose/config/fields_schema.json
+++ b/compose/config/fields_schema.json
@@ -57,14 +57,21 @@
         },
 
         "extends": {
-          "type": "object",
+          "oneOf": [
+            {
+              "type": "string"
+            },
+            {
+              "type": "object",
 
-          "properties": {
-            "service": {"type": "string"},
-            "file": {"type": "string"}
-          },
-          "required": ["service"],
-          "additionalProperties": false
+              "properties": {
+                "service": {"type": "string"},
+                "file": {"type": "string"}
+              },
+              "required": ["service"],
+              "additionalProperties": false
+            }
+          ]
         },
 
         "extra_hosts": {"$ref": "#/definitions/list_or_dict"},
diff --git a/tests/fixtures/extends/verbose-and-shorthand.yml b/tests/fixtures/extends/verbose-and-shorthand.yml
new file mode 100644
index 000000000..d38163027
--- /dev/null
+++ b/tests/fixtures/extends/verbose-and-shorthand.yml
@@ -0,0 +1,15 @@
+base:
+  image: busybox
+  environment:
+    - "BAR=1"
+
+verbose:
+  extends:
+    service: base
+  environment:
+    - "FOO=1"
+
+shorthand:
+  extends: base
+  environment:
+    - "FOO=2"
diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py
index 2dfa764df..2c3c5a3a1 100644
--- a/tests/unit/config/config_test.py
+++ b/tests/unit/config/config_test.py
@@ -1115,6 +1115,26 @@ class ExtendsTest(unittest.TestCase):
         dicts = load_from_filename('tests/fixtures/extends/valid-common-config.yml')
         self.assertEqual(dicts[0]['environment'], {'FOO': '1'})
 
+    def test_extended_service_with_verbose_and_shorthand_way(self):
+        services = load_from_filename('tests/fixtures/extends/verbose-and-shorthand.yml')
+        self.assertEqual(service_sort(services), service_sort([
+            {
+                'name': 'base',
+                'image': 'busybox',
+                'environment': {'BAR': '1'},
+            },
+            {
+                'name': 'verbose',
+                'image': 'busybox',
+                'environment': {'BAR': '1', 'FOO': '1'},
+            },
+            {
+                'name': 'shorthand',
+                'image': 'busybox',
+                'environment': {'BAR': '1', 'FOO': '2'},
+            },
+        ]))
+
 
 @pytest.mark.xfail(IS_WINDOWS_PLATFORM, reason='paths use slash')
 class ExpandPathTest(unittest.TestCase):