Repository: deis/deis

Issue:
[bug] Unable to start application with ' (quote) in env variable
Our customer's bug:
**STR**

``` bash
git clone https://github.com/deis/example-python-flask && cd example-python-flask
deis create
git push deis master
echo "MAILER_SUBJECT=ISP cron can't create booklet" > .env
deis config:push
```

**Actual result**
Creating config... .o.
Client return 504 error, after ~30 min(balancer config)
_Controller log_

``` log
Dec 14 03:01:10 deis-v1121-4.novalocal sh[18564]: INFO Tagging Docker image 10.21.12.123:5000/forecast:git-1c89b116 as forecast:git-1c89b116
Dec 14 03:01:10 deis-v1121-4.novalocal sh[18564]: INFO Building Docker image 10.21.12.123:5000/forecast:v4
Dec 14 03:01:10 deis-v1121-4.novalocal sh[18564]: INFO forecast: 500 Server Error: Internal Server Error ("Syntax error - can't find = in "cron". Must be of the form: name=value")
Dec 14 03:01:10 deis-v1121-4.novalocal sh[18564]: INFO [forecast]: 500 Server Error: Internal Server Error ("Syntax error - can't find = in "cron". Must be of the form: name=value")
```

_deis info_

``` bash
=== forecast Application
updated:  2015-12-14T02:57:36UTC
uuid:     01eacd72-1950-4ec1-a301-4212b59572b6
created:  2015-12-14T02:50:35UTC
url:      forecast.web.2gis.local
owner:    v.reyder
id:       forecast

=== forecast Processes
--- web:
web.1 up (v3)
web.1 down (v4)

=== forecast Domains
```

_Actually config is set_

``` bash
$ deis config
=== forecast Config
MAILER_SUBJECT      ISP cron can't create booklet
```

_Registry_

``` bash
s3cmd ls s3://devregistry/registry/repositories/library/forecast/
2015-12-14 02:57     11780   s3://devregistry/registry/repositories/library/forecast/_index_images
2015-12-14 02:56        64   s3://devregistry/registry/repositories/library/forecast/tag_git-1c89b116
2015-12-14 02:57        64   s3://devregistry/registry/repositories/library/forecast/tag_v3
2015-12-14 02:56       147   s3://devregistry/registry/repositories/library/forecast/taggit-1c89b116_json
2015-12-14 02:57       147   s3://devregistry/registry/repositories/library/forecast/tagv3_json
```

**Important: Only v3 tags presents and v4 is not exist**

**Expected result**
Config is set, application is up, and proper tags create in registry.



Discussion:
Also if set MY_VAR=can't all will works normally. 

Can confirm; all roads lead to the registry lib (or Docker, one of the two)

From the `docker build` logic I could reproduce this:

```
><> docker build .
Sending build context to Docker daemon 109.6 kB
Error response from daemon: Syntax error - can't find = in "compute'". Must be of the form: name=value
><> cat Dockerfile 
FROM busybox
ENV POWERED_BY='can\'t compute'
```

Essentially we just need to escape these better.


Resolving patch:
diff --git a/controller/registry/dockerclient.py b/controller/registry/dockerclient.py
--- a/controller/registry/dockerclient.py
+++ b/controller/registry/dockerclient.py
@@ -55,8 +55,8 @@ def publish_release(self, source, config, target, deis_registry):
     def build(self, source, config, repo, tag):
         """Add a "last-mile" layer of environment config to a Docker image for deis-registry."""
         check_blacklist(repo)
-        env = ' '.join("{}='{}'".format(
-            k, v.encode('unicode-escape').replace("'", "\\'")) for k, v in config.viewitems())
+        env = ' '.join('{}="{}"'.format(
+            k, v.encode('unicode-escape').replace('"', '\\"')) for k, v in config.viewitems())
         dockerfile = "FROM {}\nENV {}".format(source, env)
         f = io.BytesIO(dockerfile.encode('utf-8'))
         target_repo = "{}/{}:{}".format(self.registry, repo, tag)


Test patch:
diff --git a/controller/registry/tests.py b/controller/registry/tests.py
--- a/controller/registry/tests.py
+++ b/controller/registry/tests.py
@@ -56,7 +56,7 @@ def test_build(self, mock_client):
         self.assertDictContainsSubset(args, kwargs)
         # test that the fileobj arg to "docker build" contains a correct Dockerfile
         f = kwargs['fileobj']
-        self.assertEqual(f.read(), "FROM ozzy/embryo:git-f3a8020\nENV POWERED_BY='Deis'")
+        self.assertEqual(f.read(), "FROM ozzy/embryo:git-f3a8020\nENV POWERED_BY=\"Deis\"")
         # Test that blacklisted image names can't be built
         with self.assertRaises(PermissionDenied):
             self.client.build('deis/controller:v1.11.1', {}, 'deis/controller', 'v1.11.1')