From 9543c0e96a4573cbfb887833ab9190fd16c01ef4 Mon Sep 17 00:00:00 2001 From: Eric Stoekl Date: Wed, 6 Sep 2017 23:12:34 -0700 Subject: [PATCH] Third party template support 1. Create `faas-cli addTemplate --url ` command 2. Add template.yml files for each language in the ./template dir. 3. Read fProcess from ./template/ in deploy command 4. Adds mock-server test for addTemplate.go 5. Remove fprocess variable hard-coding in proxy/deploy.go Signed-off-by: Eric Stoekl --- Dockerfile | 1 + commands/add_template.go | 52 ++++++++++++++++++++++++++++ commands/add_template_test.go | 54 ++++++++++++++++++++++++++++++ commands/deploy.go | 23 ++++++++++++- commands/list.go | 1 - proxy/deploy.go | 10 ++---- stack/language_template.go | 31 +++++++++++++++++ stack/schema.go | 5 +++ template/csharp/template.yml | 2 ++ template/node-armhf/template.yml | 2 ++ template/node/template.yml | 2 ++ template/python-armhf/template.yml | 2 ++ template/python/template.yml | 2 ++ template/ruby/template.yml | 2 ++ 14 files changed, 179 insertions(+), 10 deletions(-) create mode 100644 commands/add_template.go create mode 100644 commands/add_template_test.go create mode 100644 stack/language_template.go create mode 100644 template/csharp/template.yml create mode 100644 template/node-armhf/template.yml create mode 100644 template/node/template.yml create mode 100644 template/python-armhf/template.yml create mode 100644 template/python/template.yml create mode 100644 template/ruby/template.yml diff --git a/Dockerfile b/Dockerfile index 6f9462935..621277aca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ COPY . . RUN GIT_COMMIT=$(git rev-list -1 HEAD) \ && CGO_ENABLED=0 GOOS=linux go build --ldflags "-s -w -X github.com/alexellis/faas-cli/commands.GitCommit=${GIT_COMMIT}" -a -installsuffix cgo -o faas-cli . +RUN go test ./... FROM alpine:latest RUN apk --no-cache add ca-certificates diff --git a/commands/add_template.go b/commands/add_template.go new file mode 100644 index 000000000..655eb689d --- /dev/null +++ b/commands/add_template.go @@ -0,0 +1,52 @@ +// Copyright (c) Alex Ellis 2017. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +package commands + +import ( + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" +) + +// Flags that are to be added to commands + +var ( + URL string +) + +func init() { + // Setup flags that are used only by this command (variables defined above) + addTemplateCmd.Flags().StringVar(&URL, "url", "http://github.com/alexellis/faas-cli", "URL from which to pull git repo to grab 'template' dir from") + + faasCmd.AddCommand(addTemplateCmd) +} + +// addTemplateCmd represents the addTemplate command +var addTemplateCmd = &cobra.Command{ + Use: "add-template [--url URL]", + Short: "Downloads templates from the specified github repo", + Long: `Downloads the compressed github repo specified by [URL], and extracts the 'template' +directory from the root of the repo, if it exists.`, + Example: "faas-cli add-template --url https://github.com/alexellis/faas-cli", + Run: runAddTemplate, +} + +func runAddTemplate(cmd *cobra.Command, args []string) { + URL = strings.TrimRight(URL, "/") + URL = URL + "/archive/master.zip" + + err := os.Setenv("templateUrl", URL) + if err != nil { + fmt.Printf("Error setting templateUrl env var: %v\n", err) + os.Exit(1) + } + + err = fetchTemplates() + if err != nil { + fmt.Printf("Error getting templates from URL: %v\n", err) + os.Exit(1) + } +} diff --git a/commands/add_template_test.go b/commands/add_template_test.go new file mode 100644 index 000000000..2cfe81982 --- /dev/null +++ b/commands/add_template_test.go @@ -0,0 +1,54 @@ +// Copyright (c) Alex Ellis, Eric Stoekl 2017. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +package commands + +import ( + "testing" + "net/http" + "net/http/httptest" + "io/ioutil" + "os" + +) + +func Test_addTemplate(t *testing.T) { + const sampleMasterZipPath string = "../test/master.zip" + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + if _, err := os.Stat(sampleMasterZipPath); os.IsNotExist(err) { + t.Error(err) + } + + fileData, err := ioutil.ReadFile(sampleMasterZipPath) + if err != nil { + t.Error(err) + } + + w.Write(fileData) + })) + defer ts.Close() + + URL = ts.URL + faasCmd.SetArgs([]string{"add-template"}) + faasCmd.Execute() + + // Remove existing master.zip file if it exists + if _, err := os.Stat("master.zip"); err == nil { + t.Log("Found a master.zip file, removing it...") + + err := os.Remove("master.zip") + if err != nil { + t.Fatal(err) + } + } + + // Remove existing templates folder, if it exist + if _, err := os.Stat("template/"); err == nil { + t.Log("Found a template/ directory, removing it...") + + err := os.RemoveAll("template/") + if err != nil { + t.Fatal(err) + } + } +} diff --git a/commands/deploy.go b/commands/deploy.go index 784947785..c6187d4b6 100644 --- a/commands/deploy.go +++ b/commands/deploy.go @@ -95,11 +95,32 @@ func runDeploy(cmd *cobra.Command, args []string) { for k, function := range services.Functions { function.Name = k - fmt.Printf("Deploying: %s.\n", function.Name) if function.Constraints != nil { constraints = *function.Constraints } + // Get FProcess to use from the ./template/template.yml, if a template is being used + if function.Language != "" && function.Language != "Dockerfile" && function.Language != "dockerfile" { + pathToTemplateYAML := "./template/" + function.Language + "/template.yml" + if _, err := os.Stat(pathToTemplateYAML); os.IsNotExist(err) { + log.Fatalln(err.Error()) + return + } + + var langTemplate stack.LanguageTemplate + parsedLangTemplate, err := stack.ParseYAMLForLanguageTemplate(pathToTemplateYAML) + + if err != nil { + log.Fatalln(err.Error()) + return + } + + if parsedLangTemplate != nil { + langTemplate = *parsedLangTemplate + function.FProcess = langTemplate.FProcess + } + } + proxy.DeployFunction(function.FProcess, services.Provider.GatewayURL, function.Name, function.Image, function.Language, replace, function.Environment, services.Provider.Network, constraints) } } else { diff --git a/commands/list.go b/commands/list.go index 3480e5e64..310f35072 100644 --- a/commands/list.go +++ b/commands/list.go @@ -18,7 +18,6 @@ var ( func init() { // Setup flags that are used by multiple commands (variables defined in faas.go) - listCmd.Flags().StringVar(&fprocess, "fprocess", "", "Fprocess to be run by the watchdog") listCmd.Flags().StringVar(&gateway, "gateway", "http://localhost:8080", "Gateway URI") listCmd.Flags().StringVar(&handler, "handler", "", "Directory with handler for function, e.g. handler.js") listCmd.Flags().StringVar(&image, "image", "", "Docker image name to build") diff --git a/proxy/deploy.go b/proxy/deploy.go index 240c98605..720f25177 100644 --- a/proxy/deploy.go +++ b/proxy/deploy.go @@ -21,14 +21,8 @@ func DeployFunction(fprocess string, gateway string, functionName string, image var fprocessTemplate string if len(fprocess) > 0 { fprocessTemplate = fprocess - } else if language == "python" { - fprocessTemplate = "python index.py" - } else if language == "node" { - fprocessTemplate = "node index.js" - } else if language == "ruby" { - fprocessTemplate = "ruby index.rb" - } else if language == "csharp" { - fprocessTemplate = "dotnet ./root.dll" + } else { + fmt.Printf("Command to be invoked for function %s not found.\n", functionName) } gateway = strings.TrimRight(gateway, "/") diff --git a/stack/language_template.go b/stack/language_template.go new file mode 100644 index 000000000..5f08ce255 --- /dev/null +++ b/stack/language_template.go @@ -0,0 +1,31 @@ +// Copyright (c) Alex Ellis 2017. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +package stack + +import ( + "fmt" + "io/ioutil" + + yaml "gopkg.in/yaml.v2" +) + +// ParseYAML parse a YAML file into a LanguageTemplate struct. +func ParseYAMLForLanguageTemplate(yamlFile string) (*LanguageTemplate, error) { + var langTemplate LanguageTemplate + var err error + var fileData []byte + + fileData, err = ioutil.ReadFile(yamlFile) + if err != nil { + return nil, err + } + + err = yaml.Unmarshal(fileData, &langTemplate) + if err != nil { + fmt.Printf("Error with YAML file\n") + return nil, err + } + + return &langTemplate, err +} diff --git a/stack/schema.go b/stack/schema.go index 9d99268a3..06e144ef6 100644 --- a/stack/schema.go +++ b/stack/schema.go @@ -36,3 +36,8 @@ type Services struct { Functions map[string]Function `yaml:"functions,omitempty"` Provider Provider `yaml:"provider,omitempty"` } + +type LanguageTemplate struct { + Language string `yaml:"lang"` + FProcess string `yaml:"fprocess"` +} diff --git a/template/csharp/template.yml b/template/csharp/template.yml new file mode 100644 index 000000000..45caa67ed --- /dev/null +++ b/template/csharp/template.yml @@ -0,0 +1,2 @@ +language: csharp +fprocess: dotnet ./root.dll diff --git a/template/node-armhf/template.yml b/template/node-armhf/template.yml new file mode 100644 index 000000000..1117efa75 --- /dev/null +++ b/template/node-armhf/template.yml @@ -0,0 +1,2 @@ +language: node +fprocess: node index.js diff --git a/template/node/template.yml b/template/node/template.yml new file mode 100644 index 000000000..1117efa75 --- /dev/null +++ b/template/node/template.yml @@ -0,0 +1,2 @@ +language: node +fprocess: node index.js diff --git a/template/python-armhf/template.yml b/template/python-armhf/template.yml new file mode 100644 index 000000000..5487218c4 --- /dev/null +++ b/template/python-armhf/template.yml @@ -0,0 +1,2 @@ +language: python +fprocess: python index.py diff --git a/template/python/template.yml b/template/python/template.yml new file mode 100644 index 000000000..5487218c4 --- /dev/null +++ b/template/python/template.yml @@ -0,0 +1,2 @@ +language: python +fprocess: python index.py diff --git a/template/ruby/template.yml b/template/ruby/template.yml new file mode 100644 index 000000000..417bd6133 --- /dev/null +++ b/template/ruby/template.yml @@ -0,0 +1,2 @@ +language: ruby +fprocess: ruby index.rb