Repository: kubeless/kubeless (go)

Issue:
kubeless can not download function from url which contains &
**Is this a BUG REPORT or FEATURE REQUEST?**: BUG

**What happened**: I have created an azure blob storage and uploaded the function as a zip file. created the sas token and got a link which includes & signs. e.g. ``` azure.blob.com/function.zip?hi&hello=world``` 

**What you expected to happen**: that kubeless escapes the curl command inside the prepare container

**How to reproduce it (as minimally and precisely as possible)**: just use any URL with & in it

**Anything else we need to know?**:

**Environment**:
- Kubernetes version (use `kubectl version`):
- Kubeless version (use `kubeless version`):
- Cloud provider or physical cluster: azure

So this is really weird because I would expect the curl command to be escaped.

If I url encode the link and replace & with %26 this time I get resource is not found from azure and checksums wont match.

the arguments of the prepare container:
```bash
 Args:
      curl https://xxx.blob.core.windows.net/functions/function.zip?sp=r&st=2019-10-30T14:29:28Z&se=2019-12-01T22:29:28Z&spr=https&sv=2019-02-02&sr=b&sig=xxx%3D -L --silent --output /tmp/func.fromurl && echo '71aecbb768cc278a590c4dbc667531b385bf88eb56ab3dc77558dd52eace4cd8  /tmp/func.fromurl' > /tmp/func.sha256 && sha256sum -c /tmp/func.sha256 && unzip -o /tmp/func.fromurl -d /kubeless && cp /src/package.json /kubeless
```


logs from prepare container:
```bash
C:\src\deployment>kubectl logs fn-6b87fd859-fm28p -c prepare
sh: -L: not found
```

Pull request description:
Quote curl URL
**Issue Ref**: Fixes #1092
 
**Description**: 

Quote the URL when using the type `url` to avoid interpreting characters like `&`.

**TODOs**:
 - [X] Ready to review
 - [X] Automated Tests
 - [X] Docs


Patch:
diff --git a/pkg/utils/kubelessutil.go b/pkg/utils/kubelessutil.go
index 8014f1e5..dbaf2e64 100644
--- a/pkg/utils/kubelessutil.go
+++ b/pkg/utils/kubelessutil.go
@@ -80,7 +80,7 @@ func getProvisionContainer(function, checksum, fileName, handler, contentType, r
 		originFile = decodedFile
 	} else if strings.Contains(contentType, "url") {
 		fromURLFile := "/tmp/func.fromurl"
-		prepareCommand = appendToCommand(prepareCommand, fmt.Sprintf("curl %s -L --silent --output %s", function, fromURLFile))
+		prepareCommand = appendToCommand(prepareCommand, fmt.Sprintf("curl '%s' -L --silent --output %s", function, fromURLFile))
 		originFile = fromURLFile
 	} else if strings.Contains(contentType, "text") || contentType == "" {
 		// Assumming that function is plain text


Test patch:
diff --git a/pkg/utils/kubelessutil_test.go b/pkg/utils/kubelessutil_test.go
index 4e0ed53e..44ccaacd 100644
--- a/pkg/utils/kubelessutil_test.go
+++ b/pkg/utils/kubelessutil_test.go
@@ -921,7 +921,7 @@ func TestGetProvisionContainer(t *testing.T) {
 	if err != nil {
 		t.Errorf("Unexpected error: %s", err)
 	}
-	if !strings.HasPrefix(c.Args[0], "curl https://raw.githubusercontent.com/test/test/test/test.py -L --silent --output /tmp/func.fromurl") {
+	if !strings.HasPrefix(c.Args[0], "curl 'https://raw.githubusercontent.com/test/test/test/test.py' -L --silent --output /tmp/func.fromurl") {
 		t.Errorf("Unexpected command: %s", c.Args[0])
 	}
 
@@ -930,7 +930,7 @@ func TestGetProvisionContainer(t *testing.T) {
 	if err != nil {
 		t.Errorf("Unexpected error: %s", err)
 	}
-	if !strings.HasPrefix(c.Args[0], "curl https://raw.githubusercontent.com/test/test/test/test.py -L --silent --output /tmp/func.fromurl") {
+	if !strings.HasPrefix(c.Args[0], "curl 'https://raw.githubusercontent.com/test/test/test/test.py' -L --silent --output /tmp/func.fromurl") {
 		t.Errorf("Unexpected command: %s", c.Args[0])
 	}
 }