Skip to content

Commit 949e450

Browse files
authored
Merge pull request #9 from alexellis/push_file_alias
Add 'push' action and -f for URLs
2 parents 20c5ea4 + 6e0867f commit 949e450

File tree

7 files changed

+150
-94
lines changed

7 files changed

+150
-94
lines changed

MANUAL_CLI.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
### Manual CLI options
2+
3+
In addition to YAML file support, you can use the CLI to build and deploy individual functions as follows:
4+
5+
#### Worked example with Node.js
6+
7+
So if you want to write in another language, just prepare a Dockerfile and build an image manually, like in the [FaaS samples](https://github.com/alexellis/faas/tree/master/sample-functions).
8+
9+
**Build a FaaS function in NodeJS from a template:**
10+
11+
This will generate a Docker image for a Node.js function using the code in `/samples/info`.
12+
13+
* The `faas-cli` can accept a `-lang` option of `python` or `node` and is `node` by default.
14+
15+
```
16+
$ ./faas-cli -action=build \
17+
-image=alexellis2/node_info \
18+
-name=node_info \
19+
-handler=./sample/node_info
20+
21+
Building: alexellis2/node_info with Docker. Please wait..
22+
...
23+
Image: alexellis2/node_info built.
24+
```
25+
26+
You can customise the code by editing the handler.js file and changing the `-handler` parameter. You can also edit the packages.json file, which will be used during the build to make sure all your dependencies are available at runtime.
27+
28+
For example:
29+
30+
```
31+
"use strict"
32+
33+
module.exports = (context, callback) => {
34+
console.log("echo - " + context);
35+
36+
callback(undefined, {status: "done"});
37+
}
38+
```
39+
40+
The CLI will then build a Docker image containing the FaaS watchdog and a bootstrap file to invoke your NodeJS function.
41+
42+
**Deploy the Docker image as a FaaS function:**
43+
44+
Now we can deploy the image as a named function called `node_info`.
45+
46+
```
47+
$ ./faas-cli -action=deploy \
48+
-image=alexellis2/node_info \
49+
-name=node_info
50+
51+
200 OK
52+
53+
URL: http://localhost:8080/function/node_info
54+
```
55+
56+
> This tool can be used to deploy any Docker image as a FaaS function, as long as it includes the watchdog binary as the `CMD` or `ENTRYPOINT` of the image.
57+
58+
*Deploy remotely*
59+
60+
You can deploy to a remote FaaS instance as along as you push the image to the Docker Hub, or another accessible Docker registry. Specify your remote gateway with the following flag: `-gateway=http://remote-site.com:8080`

README.md

Lines changed: 11 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,15 @@ This url_ping function is defined in the samples/url__ping folder makes use of P
3333
* Build the files in the .yml file:
3434

3535
```
36-
$ ./faas-cli -action build -yaml ./samples.yml
36+
$ ./faas-cli -action build -f ./samples.yml
37+
```
38+
39+
> `-f` specifies the file or URL to download your YAML file from. The long version of the `-f` flag is: `-yaml`.
40+
41+
You can also download over HTTP/s:
42+
43+
```
44+
$ ./faas-cli -action build -f https://github.com/alexellis/faas-cli/blob/master/samples.yml
3745
```
3846

3947
Docker along with a Python template will be used to build an image named alexellis2/faas-urlping.
@@ -43,7 +51,7 @@ Docker along with a Python template will be used to build an image named alexell
4351
Now you can use the following command to deploy your function(s):
4452

4553
```
46-
$ ./faas-cli -action deploy -yaml ./samples.yml
54+
$ ./faas-cli -action deploy -f ./samples.yml
4755
```
4856

4957
* Possible entries for functions are documented below:
@@ -78,7 +86,7 @@ $ curl --data-binary @README.md http://localhost:8080/function/node_info
7886
$ uname -a | curl http://localhost:8080/function/node_info --data-binary @-
7987
```
8088

81-
*Read on for manual CLI instructions.*
89+
> For further instructions on the manual CLI flags (without using a YAML file) read [manual_cli.md](https://github.com/alexellis/faas-cli/blob/master/MANUAL_CLI.md)
8290
8391
### Installation / pre-requirements
8492

@@ -118,64 +126,3 @@ $ go build
118126
This project is part of the FaaS project licensed under the MIT License.
119127

120128
For more details see the [Contributing guide](https://github.com/alexellis/faas-cli/blob/master/CONTRIBUTING.md).
121-
122-
### Manual CLI options
123-
124-
*Update: read-on for YAML support.*
125-
126-
#### Worked example with Node.js
127-
128-
So if you want to write in another language, just prepare a Dockerfile and build an image manually, like in the [FaaS samples](https://github.com/alexellis/faas/tree/master/sample-functions).
129-
130-
**Build a FaaS function in NodeJS from a template:**
131-
132-
This will generate a Docker image for a Node.js function using the code in `/samples/info`.
133-
134-
* The `faas-cli` can accept a `-lang` option of `python` or `node` and is `node` by default.
135-
136-
```
137-
$ ./faas-cli -action=build \
138-
-image=alexellis2/node_info \
139-
-name=node_info \
140-
-handler=./sample/node_info
141-
142-
Building: alexellis2/node_info with Docker. Please wait..
143-
...
144-
Image: alexellis2/node_info built.
145-
```
146-
147-
You can customise the code by editing the handler.js file and changing the `-handler` parameter. You can also edit the packages.json file, which will be used during the build to make sure all your dependencies are available at runtime.
148-
149-
For example:
150-
151-
```
152-
"use strict"
153-
154-
module.exports = (context, callback) => {
155-
console.log("echo - " + context);
156-
157-
callback(undefined, {status: "done"});
158-
}
159-
```
160-
161-
The CLI will then build a Docker image containing the FaaS watchdog and a bootstrap file to invoke your NodeJS function.
162-
163-
**Deploy the Docker image as a FaaS function:**
164-
165-
Now we can deploy the image as a named function called `node_info`.
166-
167-
```
168-
$ ./faas-cli -action=deploy \
169-
-image=alexellis2/node_info \
170-
-name=node_info
171-
172-
200 OK
173-
174-
URL: http://localhost:8080/function/node_info
175-
```
176-
177-
> This tool can be used to deploy any Docker image as a FaaS function, as long as it includes the watchdog binary as the `CMD` or `ENTRYPOINT` of the image.
178-
179-
*Deploy remotely*
180-
181-
You can deploy to a remote FaaS instance as along as you push the image to the Docker Hub, or another accessible Docker registry. Specify your remote gateway with the following flag: `-gateway=http://remote-site.com:8080`

app.go

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@ import (
2020
"bytes"
2121
"os"
2222

23+
"net/url"
24+
2325
"github.com/alexellis/faas/gateway/requests"
2426
)
2527

28+
const providerName = "faas"
29+
const defaultNetwork = "func_functions"
30+
2631
func main() {
2732
// var handler string
2833
var handler string
@@ -36,6 +41,7 @@ func main() {
3641
var replace bool
3742
var nocache bool
3843
var yamlFile string
44+
var yamlFileShort string
3945

4046
flag.StringVar(&handler, "handler", "", "handler for function, i.e. handler.js")
4147
flag.StringVar(&image, "image", "", "Docker image name to build")
@@ -48,27 +54,44 @@ func main() {
4854
flag.BoolVar(&nocache, "no-cache", false, "do not use Docker's build cache")
4955

5056
flag.StringVar(&yamlFile, "yaml", "", "use a yaml file for a set of functions")
57+
flag.StringVar(&yamlFileShort, "f", "", "use a yaml file for a set of functions (same as -yaml)")
5158

5259
flag.Parse()
5360

61+
// support short-argument -f
62+
if len(yamlFile) == 0 && len(yamlFileShort) > 0 {
63+
yamlFile = yamlFileShort
64+
}
65+
5466
var services Services
5567
if len(yamlFile) > 0 {
56-
fileData, err := ioutil.ReadFile(yamlFile)
57-
if err != nil {
58-
fmt.Printf("Error: %s\n", err.Error())
59-
return
68+
var err error
69+
var fileData []byte
70+
urlParsed, err := url.Parse(yamlFile)
71+
if err == nil && len(urlParsed.Scheme) > 0 {
72+
fmt.Println("Parsed: " + urlParsed.String())
73+
fileData, err = fetchYaml(urlParsed)
74+
if err != nil {
75+
fmt.Println(err.Error())
76+
return
77+
}
78+
} else {
79+
fileData, err = ioutil.ReadFile(yamlFile)
80+
if err != nil {
81+
fmt.Printf("Error: %s\n", err.Error())
82+
return
83+
}
6084
}
85+
6186
err = yaml.Unmarshal(fileData, &services)
6287
if err != nil {
6388
fmt.Printf("Error with YAML file: %s\n", err.Error())
6489
return
6590
}
66-
67-
if services.Provider.Name != "faas" {
68-
fmt.Println("'faas' is the only valid provider for the faas-cli.")
91+
if services.Provider.Name != providerName {
92+
fmt.Printf("'%s' is the only valid provider for this tool - found: %s.\n", providerName, services.Provider.Name)
6993
return
7094
}
71-
7295
}
7396

7497
if len(action) == 0 {
@@ -103,12 +126,16 @@ func main() {
103126
break
104127
case "deploy":
105128
if len(services.Functions) > 0 {
129+
if len(services.Provider.Network) == 0 {
130+
services.Provider.Network = defaultNetwork
131+
}
132+
106133
for k, function := range services.Functions {
107134
function.Name = k
108135
// fmt.Println(k, function)
109136
fmt.Printf("Deploying: %s.\n", function.Name)
110137

111-
deployFunction(function.FProcess, services.Provider.GatewayURL, function.Name, function.Image, function.Language, replace, function.Environment)
138+
deployFunction(function.FProcess, services.Provider.GatewayURL, function.Name, function.Image, function.Language, replace, function.Environment, services.Provider.Network)
112139
}
113140
} else {
114141
if len(image) == 0 {
@@ -119,17 +146,28 @@ func main() {
119146
fmt.Println("Give a -name for your function as it will be deployed on FaaS")
120147
return
121148
}
122-
var envs map[string]string
123-
deployFunction(fprocess, gateway, functionName, image, language, replace, envs)
149+
150+
deployFunction(fprocess, gateway, functionName, image, language, replace, map[string]string{}, defaultNetwork)
124151
}
125152
break
153+
case "push":
154+
if len(services.Functions) > 0 {
155+
for k, function := range services.Functions {
156+
function.Name = k
157+
fmt.Printf("Pushing: %s to remote repository.\n", function.Name)
158+
pushImage(function.Image)
159+
}
160+
} else {
161+
fmt.Println("The '-action push' flag only works with a YAML file.")
162+
return
163+
}
126164
default:
127-
fmt.Println("-action must be 'build' or 'deploy'.")
165+
fmt.Println("-action must be 'build', 'deploy' or 'push'.")
128166
break
129167
}
130168
}
131169

132-
func deployFunction(fprocess string, gateway string, functionName string, image string, language string, replace bool, envVars map[string]string) {
170+
func deployFunction(fprocess string, gateway string, functionName string, image string, language string, replace bool, envVars map[string]string, network string) {
133171

134172
// Need to alter Gateway to allow nil/empty string as fprocess, to avoid this repetition.
135173
fprocessTemplate := "node index.js"
@@ -144,10 +182,11 @@ func deployFunction(fprocess string, gateway string, functionName string, image
144182
deleteFunction(gateway, functionName)
145183
}
146184

185+
// TODO: allow registry auth to be specified or read from local Docker credentials store
147186
req := requests.CreateFunctionRequest{
148187
EnvProcess: fprocessTemplate,
149188
Image: image,
150-
Network: "func_functions",
189+
Network: "func_functions", // todo: specify network as an override
151190
Service: functionName,
152191
EnvVars: envVars,
153192
}
@@ -177,7 +216,8 @@ func deleteFunction(gateway string, functionName string) {
177216
delRes, delErr := c.Do(req)
178217

179218
if delErr != nil {
180-
fmt.Println(delErr.Error())
219+
fmt.Printf("Error removing existing function: %s, gateway=%s, functionName=%s\n", delErr.Error(), gateway, functionName)
220+
return
181221
}
182222
switch delRes.StatusCode {
183223
case 200:
@@ -187,6 +227,10 @@ func deleteFunction(gateway string, functionName string) {
187227
}
188228
}
189229

230+
func pushImage(image string) {
231+
execBuild("./", []string{"docker", "push", image})
232+
}
233+
190234
func buildImage(image string, handler string, functionName string, language string, nocache bool) {
191235

192236
switch language {
@@ -278,3 +322,20 @@ func execBuild(tempPath string, builder []string) {
278322
targetCmd.Start()
279323
targetCmd.Wait()
280324
}
325+
326+
func fetchYaml(address *url.URL) ([]byte, error) {
327+
req, err := http.NewRequest("GET", address.String(), nil)
328+
if err != nil {
329+
return nil, err
330+
}
331+
c := http.Client{}
332+
res, err := c.Do(req)
333+
if err != nil {
334+
return nil, err
335+
}
336+
defer res.Body.Close()
337+
338+
resBytes, err := ioutil.ReadAll(res.Body)
339+
340+
return resBytes, err
341+
}

sample/hmac.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
provider:
22
name: faas
3-
gateway: http://localhost:80
3+
gateway: http://localhost:8080
44

55
functions:
66
hmac:

samples.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
provider:
22
name: faas
33
gateway: http://localhost:8080
4+
network: "func_functions" # this is optional and defaults to func_functions
45

56
functions:
67
url_ping:

schema.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package main
44
type Provider struct {
55
Name string `yaml:"name"`
66
GatewayURL string `yaml:"gateway"`
7+
Network string `yaml:"network"`
78
}
89

910
// Function as deployed or built on FaaS

test.yml

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)