Repository: deis/deis

Issue:
fix(controller): give process types unique counters
Given that I have an app called usable-woodshed
and I have a `web` process type
and I have a `bob` process type
and I have one web process running
when I scale the bob process by 2
then I should see bob.1 and bob.2 running

/end gerrit syntax

If you follow that block, you'll instead end up with containers bob.2 and a bob.3. The container number should be unique across all process types for feature parity with Heroku.

```
$ fleetctl list-units
UNIT                        LOAD    ACTIVE  SUB DESC                    MACHINE
usable-woodshed_v2.bob.2-announce.service   -   -   -   usable-woodshed_v2.bob.2 announce   -
usable-woodshed_v2.bob.2-log.service        -   -   -   usable-woodshed_v2.bob.2 log        -
usable-woodshed_v2.bob.2.service        -   -   -   usable-woodshed_v2.bob.2        -
usable-woodshed_v2.bob.3-announce.service   -   -   -   usable-woodshed_v2.bob.3 announce   -
usable-woodshed_v2.bob.3-log.service        -   -   -   usable-woodshed_v2.bob.3 log        -
usable-woodshed_v2.bob.3.service        -   -   -   usable-woodshed_v2.bob.3        -
usable-woodshed_v2.web.1-announce.service   loaded  active  running usable-woodshed_v2.web.1 announce   ca9ebec6.../10.0.2.15
usable-woodshed_v2.web.1-log.service        loaded  active  running usable-woodshed_v2.web.1 log        ca9ebec6.../10.0.2.15
usable-woodshed_v2.web.1.service        loaded  active  running usable-woodshed_v2.web.1        ca9ebec6.../10.0.2.15
```



Discussion:
@bacongobbler this was definitely not intentional.


Resolving patch:
diff --git a/controller/api/models.py b/controller/api/models.py
--- a/controller/api/models.py
+++ b/controller/api/models.py
@@ -15,6 +15,7 @@
 from django.conf import settings
 from django.contrib.auth.models import User
 from django.db import models
+from django.db.models import Max
 from django.db.models.signals import post_delete
 from django.db.models.signals import post_save
 from django.utils.encoding import python_2_unicode_compatible
@@ -138,9 +139,6 @@ def scale(self, **kwargs):
         """Scale containers up or down to match requested."""
         requested_containers = self.structure.copy()
         release = self.release_set.latest()
-        # increment new container nums off the most recent container
-        all_containers = self.container_set.all().order_by('-created')
-        container_num = 1 if not all_containers else all_containers[0].num + 1
         msg = 'Containers scaled ' + ' '.join(
             "{}={}".format(k, v) for k, v in requested_containers.items())
         # iterate and scale by container type (web, worker, etc)
@@ -148,6 +146,9 @@ def scale(self, **kwargs):
         to_add, to_remove = [], []
         for container_type in requested_containers.keys():
             containers = list(self.container_set.filter(type=container_type).order_by('created'))
+            # increment new container nums off the most recent container
+            results = self.container_set.filter(type=container_type).aggregate(Max('num'))
+            container_num = results.get('num__max') or 0 + 1
             requested = requested_containers.pop(container_type)
             diff = requested - len(containers)
             if diff == 0:


Test patch:
diff --git a/controller/api/tests/test_container.py b/controller/api/tests/test_container.py
--- a/controller/api/tests/test_container.py
+++ b/controller/api/tests/test_container.py
@@ -151,6 +151,7 @@ def test_container_api(self):
         response = self.client.get(url)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(len(response.data['results']), 3)
+        self.assertEqual(max(c['num'] for c in response.data['results']), 2)
         url = "/api/apps/{app_id}".format(**locals())
         response = self.client.get(url)
         self.assertEqual(response.status_code, 200)