github.com/amrnt/deis@v1.3.1/controller/api/tests/test_container.py (about)

     1  """
     2  Unit tests for the Deis api app.
     3  
     4  Run the tests with "./manage.py test api"
     5  """
     6  
     7  from __future__ import unicode_literals
     8  
     9  import json
    10  import mock
    11  import requests
    12  
    13  from django.contrib.auth.models import User
    14  from django.test import TransactionTestCase
    15  from django_fsm import TransitionNotAllowed
    16  from rest_framework.authtoken.models import Token
    17  
    18  from api.models import App, Build, Container, Release
    19  
    20  
    21  def mock_import_repository_task(*args, **kwargs):
    22      resp = requests.Response()
    23      resp.status_code = 200
    24      resp._content_consumed = True
    25      return resp
    26  
    27  
    28  class ContainerTest(TransactionTestCase):
    29      """Tests creation of containers on nodes"""
    30  
    31      fixtures = ['tests.json']
    32  
    33      def setUp(self):
    34          self.user = User.objects.get(username='autotest')
    35          self.token = Token.objects.get(user=self.user).key
    36  
    37      def test_container_state_good(self):
    38          """Test that the finite state machine transitions with a good scheduler"""
    39          url = '/v1/apps'
    40          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
    41          self.assertEqual(response.status_code, 201)
    42          app_id = response.data['id']
    43          app = App.objects.get(id=app_id)
    44          user = User.objects.get(username='autotest')
    45          build = Build.objects.create(owner=user, app=app, image="qwerty")
    46          # create an initial release
    47          release = Release.objects.create(version=2,
    48                                           owner=user,
    49                                           app=app,
    50                                           config=app.config_set.latest(),
    51                                           build=build)
    52          # create a container
    53          c = Container.objects.create(owner=user,
    54                                       app=app,
    55                                       release=release,
    56                                       type='web',
    57                                       num=1)
    58          self.assertEqual(c.state, 'initialized')
    59          # test an illegal transition
    60          self.assertRaises(TransitionNotAllowed, lambda: c.start())
    61          c.create()
    62          self.assertEqual(c.state, 'created')
    63          c.start()
    64          self.assertEqual(c.state, 'up')
    65          c.destroy()
    66          self.assertEqual(c.state, 'destroyed')
    67  
    68      def test_container_state_protected(self):
    69          """Test that you cannot directly modify the state"""
    70          url = '/v1/apps'
    71          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
    72          self.assertEqual(response.status_code, 201)
    73          app_id = response.data['id']
    74          app = App.objects.get(id=app_id)
    75          user = User.objects.get(username='autotest')
    76          build = Build.objects.create(owner=user, app=app, image="qwerty")
    77          # create an initial release
    78          release = Release.objects.create(version=2,
    79                                           owner=user,
    80                                           app=app,
    81                                           config=app.config_set.latest(),
    82                                           build=build)
    83          # create a container
    84          c = Container.objects.create(owner=user,
    85                                       app=app,
    86                                       release=release,
    87                                       type='web',
    88                                       num=1)
    89          self.assertRaises(AttributeError, lambda: setattr(c, 'state', 'up'))
    90  
    91      def test_container_api_heroku(self):
    92          url = '/v1/apps'
    93          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
    94          self.assertEqual(response.status_code, 201)
    95          app_id = response.data['id']
    96          # should start with zero
    97          url = "/v1/apps/{app_id}/containers".format(**locals())
    98          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
    99          self.assertEqual(response.status_code, 200)
   100          self.assertEqual(len(response.data['results']), 0)
   101          # post a new build
   102          url = "/v1/apps/{app_id}/builds".format(**locals())
   103          body = {'image': 'autotest/example', 'sha': 'a'*40,
   104                  'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})}
   105          response = self.client.post(url, json.dumps(body), content_type='application/json',
   106                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   107          self.assertEqual(response.status_code, 201)
   108          # scale up
   109          url = "/v1/apps/{app_id}/scale".format(**locals())
   110          # test setting one proc type at a time
   111          body = {'web': 4}
   112          response = self.client.post(url, json.dumps(body), content_type='application/json',
   113                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   114          self.assertEqual(response.status_code, 204)
   115          body = {'worker': 2}
   116          response = self.client.post(url, json.dumps(body), content_type='application/json',
   117                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   118          self.assertEqual(response.status_code, 204)
   119          url = "/v1/apps/{app_id}/containers".format(**locals())
   120          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   121          self.assertEqual(response.status_code, 200)
   122          self.assertEqual(len(response.data['results']), 6)
   123          url = "/v1/apps/{app_id}".format(**locals())
   124          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   125          self.assertEqual(response.status_code, 200)
   126          # ensure the structure field is up-to-date
   127          self.assertEqual(response.data['structure']['web'], 4)
   128          self.assertEqual(response.data['structure']['worker'], 2)
   129          # test listing/retrieving container info
   130          url = "/v1/apps/{app_id}/containers/web".format(**locals())
   131          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   132          self.assertEqual(response.status_code, 200)
   133          self.assertEqual(len(response.data['results']), 4)
   134          num = response.data['results'][0]['num']
   135          url = "/v1/apps/{app_id}/containers/web/{num}".format(**locals())
   136          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   137          self.assertEqual(response.status_code, 200)
   138          self.assertEqual(response.data['num'], num)
   139          # scale down
   140          url = "/v1/apps/{app_id}/scale".format(**locals())
   141          # test setting two proc types at a time
   142          body = {'web': 2, 'worker': 1}
   143          response = self.client.post(url, json.dumps(body), content_type='application/json',
   144                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   145          self.assertEqual(response.status_code, 204)
   146          url = "/v1/apps/{app_id}/containers".format(**locals())
   147          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   148          self.assertEqual(response.status_code, 200)
   149          self.assertEqual(len(response.data['results']), 3)
   150          self.assertEqual(max(c['num'] for c in response.data['results']), 2)
   151          url = "/v1/apps/{app_id}".format(**locals())
   152          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   153          self.assertEqual(response.status_code, 200)
   154          # ensure the structure field is up-to-date
   155          self.assertEqual(response.data['structure']['web'], 2)
   156          self.assertEqual(response.data['structure']['worker'], 1)
   157          # scale down to 0
   158          url = "/v1/apps/{app_id}/scale".format(**locals())
   159          body = {'web': 0, 'worker': 0}
   160          response = self.client.post(url, json.dumps(body), content_type='application/json',
   161                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   162          self.assertEqual(response.status_code, 204)
   163          url = "/v1/apps/{app_id}/containers".format(**locals())
   164          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   165          self.assertEqual(response.status_code, 200)
   166          self.assertEqual(len(response.data['results']), 0)
   167          url = "/v1/apps/{app_id}".format(**locals())
   168          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   169          self.assertEqual(response.status_code, 200)
   170  
   171      @mock.patch('requests.post', mock_import_repository_task)
   172      def test_container_api_docker(self):
   173          url = '/v1/apps'
   174          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   175          self.assertEqual(response.status_code, 201)
   176          app_id = response.data['id']
   177          # should start with zero
   178          url = "/v1/apps/{app_id}/containers".format(**locals())
   179          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   180          self.assertEqual(response.status_code, 200)
   181          self.assertEqual(len(response.data['results']), 0)
   182          # post a new build
   183          url = "/v1/apps/{app_id}/builds".format(**locals())
   184          body = {'image': 'autotest/example', 'dockerfile': "FROM busybox\nCMD /bin/true"}
   185          response = self.client.post(url, json.dumps(body), content_type='application/json',
   186                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   187          self.assertEqual(response.status_code, 201)
   188          # scale up
   189          url = "/v1/apps/{app_id}/scale".format(**locals())
   190          body = {'cmd': 6}
   191          response = self.client.post(url, json.dumps(body), content_type='application/json',
   192                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   193          self.assertEqual(response.status_code, 204)
   194          url = "/v1/apps/{app_id}/containers".format(**locals())
   195          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   196          self.assertEqual(response.status_code, 200)
   197          self.assertEqual(len(response.data['results']), 6)
   198          url = "/v1/apps/{app_id}".format(**locals())
   199          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   200          self.assertEqual(response.status_code, 200)
   201          # test listing/retrieving container info
   202          url = "/v1/apps/{app_id}/containers/cmd".format(**locals())
   203          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   204          self.assertEqual(response.status_code, 200)
   205          self.assertEqual(len(response.data['results']), 6)
   206          # scale down
   207          url = "/v1/apps/{app_id}/scale".format(**locals())
   208          body = {'cmd': 3}
   209          response = self.client.post(url, json.dumps(body), content_type='application/json',
   210                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   211          self.assertEqual(response.status_code, 204)
   212          url = "/v1/apps/{app_id}/containers".format(**locals())
   213          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   214          self.assertEqual(response.status_code, 200)
   215          self.assertEqual(len(response.data['results']), 3)
   216          self.assertEqual(max(c['num'] for c in response.data['results']), 3)
   217          url = "/v1/apps/{app_id}".format(**locals())
   218          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   219          self.assertEqual(response.status_code, 200)
   220          # scale down to 0
   221          url = "/v1/apps/{app_id}/scale".format(**locals())
   222          body = {'cmd': 0}
   223          response = self.client.post(url, json.dumps(body), content_type='application/json',
   224                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   225          self.assertEqual(response.status_code, 204)
   226          url = "/v1/apps/{app_id}/containers".format(**locals())
   227          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   228          self.assertEqual(response.status_code, 200)
   229          self.assertEqual(len(response.data['results']), 0)
   230          url = "/v1/apps/{app_id}".format(**locals())
   231          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   232          self.assertEqual(response.status_code, 200)
   233  
   234      @mock.patch('requests.post', mock_import_repository_task)
   235      def test_container_release(self):
   236          url = '/v1/apps'
   237          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   238          self.assertEqual(response.status_code, 201)
   239          app_id = response.data['id']
   240          # should start with zero
   241          url = "/v1/apps/{app_id}/containers".format(**locals())
   242          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   243          self.assertEqual(response.status_code, 200)
   244          self.assertEqual(len(response.data['results']), 0)
   245          # post a new build
   246          url = "/v1/apps/{app_id}/builds".format(**locals())
   247          body = {'image': 'autotest/example', 'sha': 'a'*40,
   248                  'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})}
   249          response = self.client.post(url, json.dumps(body), content_type='application/json',
   250                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   251          self.assertEqual(response.status_code, 201)
   252          # scale up
   253          url = "/v1/apps/{app_id}/scale".format(**locals())
   254          body = {'web': 1}
   255          response = self.client.post(url, json.dumps(body), content_type='application/json',
   256                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   257          self.assertEqual(response.status_code, 204)
   258          url = "/v1/apps/{app_id}/containers".format(**locals())
   259          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   260          self.assertEqual(response.status_code, 200)
   261          self.assertEqual(len(response.data['results']), 1)
   262          self.assertEqual(response.data['results'][0]['release'], 'v2')
   263          # post a new build
   264          url = "/v1/apps/{app_id}/builds".format(**locals())
   265          body = {'image': 'autotest/example'}
   266          response = self.client.post(url, json.dumps(body), content_type='application/json',
   267                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   268          self.assertEqual(response.status_code, 201)
   269          self.assertEqual(response.data['image'], body['image'])
   270          url = "/v1/apps/{app_id}/containers".format(**locals())
   271          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   272          self.assertEqual(response.status_code, 200)
   273          self.assertEqual(len(response.data['results']), 1)
   274          self.assertEqual(response.data['results'][0]['release'], 'v3')
   275          # post new config
   276          url = "/v1/apps/{app_id}/config".format(**locals())
   277          body = {'values': json.dumps({'KEY': 'value'})}
   278          response = self.client.post(url, json.dumps(body), content_type='application/json',
   279                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   280          self.assertEqual(response.status_code, 201)
   281          url = "/v1/apps/{app_id}/containers".format(**locals())
   282          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   283          self.assertEqual(response.status_code, 200)
   284          self.assertEqual(len(response.data['results']), 1)
   285          self.assertEqual(response.data['results'][0]['release'], 'v4')
   286  
   287      def test_container_errors(self):
   288          url = '/v1/apps'
   289          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   290          self.assertEqual(response.status_code, 201)
   291          app_id = response.data['id']
   292          # create a release so we can scale
   293          app = App.objects.get(id=app_id)
   294          user = User.objects.get(username='autotest')
   295          build = Build.objects.create(owner=user, app=app, image="qwerty")
   296          # create an initial release
   297          Release.objects.create(version=2,
   298                                 owner=user,
   299                                 app=app,
   300                                 config=app.config_set.latest(),
   301                                 build=build)
   302          url = "/v1/apps/{app_id}/scale".format(**locals())
   303          body = {'web': 'not_an_int'}
   304          response = self.client.post(url, json.dumps(body), content_type='application/json',
   305                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   306          self.assertEqual(response.status_code, 400)
   307          self.assertEqual(response.data, {'detail': 'Invalid scaling format'})
   308          body = {'invalid': 1}
   309          response = self.client.post(url, json.dumps(body), content_type='application/json',
   310                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   311          self.assertContains(response, 'Container type invalid', status_code=400)
   312  
   313      def test_container_str(self):
   314          """Test the text representation of a container."""
   315          url = '/v1/apps'
   316          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   317          self.assertEqual(response.status_code, 201)
   318          app_id = response.data['id']
   319          # post a new build
   320          url = "/v1/apps/{app_id}/builds".format(**locals())
   321          body = {'image': 'autotest/example', 'sha': 'a'*40,
   322                  'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})}
   323          response = self.client.post(url, json.dumps(body), content_type='application/json',
   324                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   325          self.assertEqual(response.status_code, 201)
   326          # scale up
   327          url = "/v1/apps/{app_id}/scale".format(**locals())
   328          body = {'web': 4, 'worker': 2}
   329          response = self.client.post(url, json.dumps(body), content_type='application/json',
   330                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   331          self.assertEqual(response.status_code, 204)
   332          # should start with zero
   333          url = "/v1/apps/{app_id}/containers".format(**locals())
   334          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   335          self.assertEqual(response.status_code, 200)
   336          self.assertEqual(len(response.data['results']), 6)
   337          uuid = response.data['results'][0]['uuid']
   338          container = Container.objects.get(uuid=uuid)
   339          self.assertEqual(container.short_name(),
   340                           "{}.{}.{}".format(container.app, container.type, container.num))
   341          self.assertEqual(str(container),
   342                           "{}.{}.{}".format(container.app, container.type, container.num))
   343  
   344      def test_container_command_format(self):
   345          # regression test for https://github.com/deis/deis/pull/1285
   346          url = '/v1/apps'
   347          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   348          self.assertEqual(response.status_code, 201)
   349          app_id = response.data['id']
   350          # post a new build
   351          url = "/v1/apps/{app_id}/builds".format(**locals())
   352          body = {'image': 'autotest/example', 'sha': 'a'*40,
   353                  'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})}
   354          response = self.client.post(url, json.dumps(body), content_type='application/json',
   355                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   356          self.assertEqual(response.status_code, 201)
   357          # scale up
   358          url = "/v1/apps/{app_id}/scale".format(**locals())
   359          body = {'web': 1}
   360          response = self.client.post(url, json.dumps(body), content_type='application/json',
   361                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   362          self.assertEqual(response.status_code, 204)
   363          url = "/v1/apps/{app_id}/containers".format(**locals())
   364          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   365          # verify that the container._command property got formatted
   366          self.assertEqual(response.status_code, 200)
   367          self.assertEqual(len(response.data['results']), 1)
   368          uuid = response.data['results'][0]['uuid']
   369          container = Container.objects.get(uuid=uuid)
   370          self.assertNotIn('{c_type}', container._command)
   371  
   372      def test_container_scale_errors(self):
   373          url = '/v1/apps'
   374          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   375          self.assertEqual(response.status_code, 201)
   376          app_id = response.data['id']
   377          # should start with zero
   378          url = "/v1/apps/{app_id}/containers".format(**locals())
   379          response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   380          self.assertEqual(response.status_code, 200)
   381          self.assertEqual(len(response.data['results']), 0)
   382          # post a new build
   383          url = "/v1/apps/{app_id}/builds".format(**locals())
   384          body = {'image': 'autotest/example', 'sha': 'a'*40,
   385                  'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})}
   386          response = self.client.post(url, json.dumps(body), content_type='application/json',
   387                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   388          self.assertEqual(response.status_code, 201)
   389          # scale to a negative number
   390          url = "/v1/apps/{app_id}/scale".format(**locals())
   391          body = {'web': -1}
   392          response = self.client.post(url, json.dumps(body), content_type='application/json',
   393                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   394          self.assertEqual(response.status_code, 400)
   395          # scale to something other than a number
   396          url = "/v1/apps/{app_id}/scale".format(**locals())
   397          body = {'web': 'one'}
   398          response = self.client.post(url, json.dumps(body), content_type='application/json',
   399                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   400          self.assertEqual(response.status_code, 400)
   401          # scale to something other than a number
   402          url = "/v1/apps/{app_id}/scale".format(**locals())
   403          body = {'web': [1]}
   404          response = self.client.post(url, json.dumps(body), content_type='application/json',
   405                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   406          self.assertEqual(response.status_code, 400)
   407          # scale up to an integer as a sanity check
   408          url = "/v1/apps/{app_id}/scale".format(**locals())
   409          body = {'web': 1}
   410          response = self.client.post(url, json.dumps(body), content_type='application/json',
   411                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   412          self.assertEqual(response.status_code, 204)
   413  
   414      def test_admin_can_manage_other_containers(self):
   415          """If a non-admin user creates a container, an administrator should be able to
   416          manage it.
   417          """
   418          user = User.objects.get(username='autotest2')
   419          token = Token.objects.get(user=user).key
   420          url = '/v1/apps'
   421          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(token))
   422          self.assertEqual(response.status_code, 201)
   423          app_id = response.data['id']
   424          # post a new build
   425          url = "/v1/apps/{app_id}/builds".format(**locals())
   426          body = {'image': 'autotest/example', 'sha': 'a'*40,
   427                  'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})}
   428          response = self.client.post(url, json.dumps(body), content_type='application/json',
   429                                      HTTP_AUTHORIZATION='token {}'.format(token))
   430          self.assertEqual(response.status_code, 201)
   431          # login as admin, scale up
   432          url = "/v1/apps/{app_id}/scale".format(**locals())
   433          body = {'web': 4, 'worker': 2}
   434          response = self.client.post(url, json.dumps(body), content_type='application/json',
   435                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   436          self.assertEqual(response.status_code, 204)
   437  
   438      def test_scale_without_build_should_error(self):
   439          """A user should not be able to scale processes unless a build is present."""
   440          app_id = 'autotest'
   441          url = '/v1/apps'
   442          body = {'cluster': 'autotest', 'id': app_id}
   443          response = self.client.post(url, json.dumps(body), content_type='application/json',
   444                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   445          url = '/v1/apps/{app_id}/scale'.format(**locals())
   446          body = {'web': '1'}
   447          response = self.client.post(url, json.dumps(body), content_type='application/json',
   448                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   449          self.assertEqual(response.status_code, 400)
   450          self.assertEqual(response.data, {'detail': 'No build associated with this release'})
   451  
   452      def test_command_good(self):
   453          """Test the default command for each container workflow"""
   454          url = '/v1/apps'
   455          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   456          self.assertEqual(response.status_code, 201)
   457          app_id = response.data['id']
   458          app = App.objects.get(id=app_id)
   459          user = User.objects.get(username='autotest')
   460          # Heroku Buildpack app
   461          build = Build.objects.create(owner=user,
   462                                       app=app,
   463                                       image="qwerty",
   464                                       procfile={'web': 'node server.js',
   465                                                 'worker': 'node worker.js'},
   466                                       sha='african-swallow',
   467                                       dockerfile='')
   468          # create an initial release
   469          release = Release.objects.create(version=2,
   470                                           owner=user,
   471                                           app=app,
   472                                           config=app.config_set.latest(),
   473                                           build=build)
   474          # create a container
   475          c = Container.objects.create(owner=user,
   476                                       app=app,
   477                                       release=release,
   478                                       type='web',
   479                                       num=1)
   480          # use `start web` for backwards compatibility with slugrunner
   481          self.assertEqual(c._command, 'start web')
   482          c.type = 'worker'
   483          self.assertEqual(c._command, 'start worker')
   484          # switch to docker image app
   485          build.sha = None
   486          c.type = 'web'
   487          self.assertEqual(c._command, "bash -c 'node server.js'")
   488          # switch to dockerfile app
   489          build.sha = 'european-swallow'
   490          build.dockerfile = 'dockerdockerdocker'
   491          self.assertEqual(c._command, "bash -c 'node server.js'")
   492          c.type = 'cmd'
   493          self.assertEqual(c._command, '')
   494          # ensure we can override the cmd process type in a Procfile
   495          build.procfile['cmd'] = 'node server.js'
   496          self.assertEqual(c._command, "bash -c 'node server.js'")
   497          c.type = 'worker'
   498          self.assertEqual(c._command, "bash -c 'node worker.js'")
   499          c.release.build.procfile = None
   500          # for backwards compatibility if no Procfile is supplied
   501          self.assertEqual(c._command, 'start worker')
   502  
   503      def test_run_command_good(self):
   504          """Test the run command for each container workflow"""
   505          url = '/v1/apps'
   506          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   507          self.assertEqual(response.status_code, 201)
   508          app_id = response.data['id']
   509          app = App.objects.get(id=app_id)
   510          user = User.objects.get(username='autotest')
   511          # dockerfile + procfile worflow
   512          build = Build.objects.create(owner=user,
   513                                       app=app,
   514                                       image="qwerty",
   515                                       procfile={'web': 'node server.js',
   516                                                 'worker': 'node worker.js'},
   517                                       dockerfile='foo',
   518                                       sha='somereallylongsha')
   519          # create an initial release
   520          release = Release.objects.create(version=2,
   521                                           owner=user,
   522                                           app=app,
   523                                           config=app.config_set.latest(),
   524                                           build=build)
   525          # create a container
   526          c = Container.objects.create(owner=user,
   527                                       app=app,
   528                                       release=release,
   529                                       type='web',
   530                                       num=1)
   531          rc, output = c.run('echo hi')
   532          self.assertEqual(rc, 0)
   533          self.assertEqual(json.loads(output)['entrypoint'], '/bin/bash')
   534          # docker image workflow
   535          build.dockerfile = None
   536          build.sha = None
   537          rc, output = c.run('echo hi')
   538          self.assertEqual(json.loads(output)['entrypoint'], '/bin/bash')
   539          # procfile workflow
   540          build.sha = 'somereallylongsha'
   541          rc, output = c.run('echo hi')
   542          self.assertEqual(json.loads(output)['entrypoint'], '/runner/init')
   543  
   544      def test_scale_with_unauthorized_user_returns_403(self):
   545          """An unauthorized user should not be able to access an app's resources.
   546  
   547          If an unauthorized user is trying to scale an app he or she does not have access to, it
   548          should return a 403. Currently, it returns a 404. FIXME!
   549          """
   550          url = '/v1/apps'
   551          response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token))
   552          self.assertEqual(response.status_code, 201)
   553          app_id = response.data['id']
   554          # post a new build
   555          url = "/v1/apps/{app_id}/builds".format(**locals())
   556          body = {'image': 'autotest/example', 'sha': 'a'*40,
   557                  'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})}
   558          response = self.client.post(url, json.dumps(body), content_type='application/json',
   559                                      HTTP_AUTHORIZATION='token {}'.format(self.token))
   560          unauthorized_user = User.objects.get(username='autotest2')
   561          unauthorized_token = Token.objects.get(user=unauthorized_user).key
   562          # scale up with unauthorized user
   563          url = "/v1/apps/{app_id}/scale".format(**locals())
   564          body = {'web': 4}
   565          response = self.client.post(url, json.dumps(body), content_type='application/json',
   566                                      HTTP_AUTHORIZATION='token {}'.format(unauthorized_token))
   567          self.assertEqual(response.status_code, 403)