From 927400ade0ea85d0f3104a5b4c61292bc8769454 Mon Sep 17 00:00:00 2001 From: Eric Stoekl Date: Thu, 9 Nov 2017 08:34:50 -0800 Subject: [PATCH] Updates to 'faas-cli template pull' 1) Improve readability by adding canExpandTemplateData() function, which returns an enum telling what to do with the data found in the zip archive. 2) Add function data to ARMHF directories in template/ dir 3) More descriptive naming for test array in language_template_test.go and checkLanguage() function changed to templateFolderExists() Signed-off-by: Eric Stoekl --- commands/fetch_templates.go | 107 +++++++++++------- stack/language_template_test.go | 4 +- template/go-armhf/function/handler.go | 10 ++ template/go-armhf/main.go | 19 ++++ template/node-armhf/build.sh | 5 + template/node-armhf/function/handler.js | 5 + template/node-armhf/function/package.json | 12 ++ template/node-armhf/index.js | 31 +++++ template/node-armhf/package.json | 15 +++ template/python-armhf/function/handler.py | 2 + .../python-armhf/function/requirements.txt | 0 template/python-armhf/index.py | 15 +++ template/python-armhf/requirements.txt | 0 13 files changed, 185 insertions(+), 40 deletions(-) create mode 100644 template/go-armhf/function/handler.go create mode 100644 template/go-armhf/main.go create mode 100755 template/node-armhf/build.sh create mode 100644 template/node-armhf/function/handler.js create mode 100644 template/node-armhf/function/package.json create mode 100644 template/node-armhf/index.js create mode 100644 template/node-armhf/package.json create mode 100644 template/python-armhf/function/handler.py create mode 100644 template/python-armhf/function/requirements.txt create mode 100644 template/python-armhf/index.py create mode 100644 template/python-armhf/requirements.txt diff --git a/commands/fetch_templates.go b/commands/fetch_templates.go index 2c5b2ebe5..f351f2189 100644 --- a/commands/fetch_templates.go +++ b/commands/fetch_templates.go @@ -25,6 +25,15 @@ const ( rootLanguageDirSplitCount = 3 ) +type ExtractAction int + +const ( + ShouldExtractData ExtractAction = iota + NewTemplateFound + DirectoryAlreadyExists + SkipWritingData +) + // fetchTemplates fetch code templates from GitHub master zip file. func fetchTemplates(templateURL string, overwrite bool) error { @@ -72,7 +81,6 @@ func expandTemplatesFromZip(archive string, overwrite bool) ([]string, []string, for _, z := range zipFile.File { var rc io.ReadCloser - var isDirectory bool relativePath := z.Name[strings.Index(z.Name, "/")+1:] if strings.Index(relativePath, "template/") != 0 { @@ -80,45 +88,41 @@ func expandTemplatesFromZip(archive string, overwrite bool) ([]string, []string, continue } - if pathSplit := strings.Split(relativePath, "/"); len(pathSplit) > 2 { - language := pathSplit[1] + action, language, isDirectory := canExpandTemplateData(availableLanguages, relativePath) - // We know that this path is a directory if the last character is a "/" - isDirectory = relativePath[len(relativePath)-1:] == "/" + var expandFromZip bool - // Check if this is the root directory for a language (at ./template/lang) - if len(pathSplit) == rootLanguageDirSplitCount && isDirectory { - // template/language/ + switch action { - if !canWriteLanguage(&availableLanguages, language, overwrite) { - existingLanguages = append(existingLanguages, language) - continue - } - fetchedLanguages = append(fetchedLanguages, language) - } else { - // template/language/* + case ShouldExtractData: + expandFromZip = true + case NewTemplateFound: + expandFromZip = true + fetchedLanguages = append(fetchedLanguages, language) + case DirectoryAlreadyExists: + expandFromZip = false + existingLanguages = append(existingLanguages, language) + case SkipWritingData: + expandFromZip = false + default: + return nil, nil, errors.New(fmt.Sprintf("Don't know what to do when extracting zip: %s", archive)) - if !canWriteLanguage(&availableLanguages, language, overwrite) { - continue - } - } - } else { - // template/ - continue } - if rc, err = z.Open(); err != nil { - break - } + if expandFromZip { + if rc, err = z.Open(); err != nil { + break + } - if err = createPath(relativePath, z.Mode()); err != nil { - break - } + if err = createPath(relativePath, z.Mode()); err != nil { + break + } - // If relativePath is just a directory, then skip expanding it. - if len(relativePath) > 1 && !isDirectory { - if err = writeFile(rc, z.UncompressedSize64, relativePath, z.Mode()); err != nil { - return nil, nil, err + // If relativePath is just a directory, then skip expanding it. + if len(relativePath) > 1 && !isDirectory { + if err = writeFile(rc, z.UncompressedSize64, relativePath, z.Mode()); err != nil { + return nil, nil, err + } } } } @@ -127,6 +131,33 @@ func expandTemplatesFromZip(archive string, overwrite bool) ([]string, []string, return existingLanguages, fetchedLanguages, nil } +// canExpandTemplateData() takes the map of available languages, and the +// path to a file in the zip archive. Returns what we should do with the file +// in form of ExtractAction enum, the language name, and whether it is a directory +func canExpandTemplateData(availableLanguages map[string]bool, relativePath string) (ExtractAction, string, bool) { + if pathSplit := strings.Split(relativePath, "/"); len(pathSplit) > 2 { + language := pathSplit[1] + + // We know that this path is a directory if the last character is a "/" + isDirectory := relativePath[len(relativePath)-1:] == "/" + + // Check if this is the root directory for a language (at ./template/lang) + if len(pathSplit) == rootLanguageDirSplitCount && isDirectory { + if !canWriteLanguage(availableLanguages, language, overwrite) { + return DirectoryAlreadyExists, language, isDirectory + } + return NewTemplateFound, language, isDirectory + } else { + if !canWriteLanguage(availableLanguages, language, overwrite) { + return SkipWritingData, language, isDirectory + } + return ShouldExtractData, language, isDirectory + } + } + // template/ + return SkipWritingData, "", true +} + // removeArchive removes the given file func removeArchive(archive string) error { log.Printf("Cleaning up zip file...") @@ -208,21 +239,21 @@ func createPath(relativePath string, perms os.FileMode) error { // canWriteLanguage() tells whether the language can be expanded from the zip or not. // availableLanguages map keeps track of which languages we know to be okay to copy. // overwrite flag will allow to force copy the language template -func canWriteLanguage(availableLanguages *map[string]bool, language string, overwrite bool) bool { +func canWriteLanguage(availableLanguages map[string]bool, language string, overwrite bool) bool { canWrite := false if availableLanguages != nil && len(language) > 0 { - if _, found := (*availableLanguages)[language]; found { - return (*availableLanguages)[language] + if _, found := availableLanguages[language]; found { + return availableLanguages[language] } - canWrite = checkLanguage(language, overwrite) - (*availableLanguages)[language] = canWrite + canWrite = templateFolderExists(language, overwrite) + availableLanguages[language] = canWrite } return canWrite } // Takes a language input (e.g. "node"), tells whether or not it is OK to download -func checkLanguage(language string, overwrite bool) bool { +func templateFolderExists(language string, overwrite bool) bool { dir := templateDirectory + language if _, err := os.Stat(dir); err == nil && !overwrite { // The directory template/language/ exists diff --git a/stack/language_template_test.go b/stack/language_template_test.go index 081112332..13f21b341 100644 --- a/stack/language_template_test.go +++ b/stack/language_template_test.go @@ -10,7 +10,7 @@ import ( ) func Test_ParseYAMLDataForLanguageTemplate(t *testing.T) { - dataProvider := []struct { + langTemplateTest := []struct { Input string Expected *LanguageTemplate }{ @@ -42,7 +42,7 @@ fprocess: python index.py }, } - for k, i := range dataProvider { + for k, i := range langTemplateTest { t.Run(fmt.Sprintf("%d", k), func(t *testing.T) { if actual, err := ParseYAMLDataForLanguageTemplate([]byte(i.Input)); err != nil { t.Errorf("test failed, %s", err) diff --git a/template/go-armhf/function/handler.go b/template/go-armhf/function/handler.go new file mode 100644 index 000000000..0c3f15fb1 --- /dev/null +++ b/template/go-armhf/function/handler.go @@ -0,0 +1,10 @@ +package function + +import ( + "fmt" +) + +// Handle a serverless request +func Handle(req []byte) string { + return fmt.Sprintf("Hello, Go. You said: %s", string(req)) +} diff --git a/template/go-armhf/main.go b/template/go-armhf/main.go new file mode 100644 index 000000000..2816215c9 --- /dev/null +++ b/template/go-armhf/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" + + "handler/function" +) + +func main() { + input, err := ioutil.ReadAll(os.Stdin) + if err != nil { + log.Fatalf("Unable to read standard input: %s", err.Error()) + } + + fmt.Println(function.Handle(input)) +} diff --git a/template/node-armhf/build.sh b/template/node-armhf/build.sh new file mode 100755 index 000000000..3e0953991 --- /dev/null +++ b/template/node-armhf/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "Building functions/base:node-6.9.1-alpine" +docker build -t functions/base:node-6.9.1-alpine . + diff --git a/template/node-armhf/function/handler.js b/template/node-armhf/function/handler.js new file mode 100644 index 000000000..73344f435 --- /dev/null +++ b/template/node-armhf/function/handler.js @@ -0,0 +1,5 @@ +"use strict" + +module.exports = (context, callback) => { + callback(undefined, {status: "done"}); +} diff --git a/template/node-armhf/function/package.json b/template/node-armhf/function/package.json new file mode 100644 index 000000000..3d3a3e456 --- /dev/null +++ b/template/node-armhf/function/package.json @@ -0,0 +1,12 @@ +{ + "name": "function", + "version": "1.0.0", + "description": "", + "main": "handler.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/template/node-armhf/index.js b/template/node-armhf/index.js new file mode 100644 index 000000000..1ad234276 --- /dev/null +++ b/template/node-armhf/index.js @@ -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. + +"use strict" + +let getStdin = require('get-stdin'); + +let handler = require('./function/handler'); + +getStdin().then(val => { + handler(val, (err, res) => { + if (err) { + return console.error(err); + } + if(isArray(res) || isObject(res)) { + console.log(JSON.stringify(res)); + } else { + process.stdout.write(res); + } + }); +}).catch(e => { + console.error(e.stack); +}); + +let isArray = (a) => { + return (!!a) && (a.constructor === Array); +}; + +let isObject = (a) => { + return (!!a) && (a.constructor === Object); +}; diff --git a/template/node-armhf/package.json b/template/node-armhf/package.json new file mode 100644 index 000000000..d35e9625d --- /dev/null +++ b/template/node-armhf/package.json @@ -0,0 +1,15 @@ +{ + "name": "NodejsBase", + "version": "1.0.0", + "description": "", + "main": "faas_index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "get-stdin": "^5.0.1" + } +} diff --git a/template/python-armhf/function/handler.py b/template/python-armhf/function/handler.py new file mode 100644 index 000000000..1f69d4f47 --- /dev/null +++ b/template/python-armhf/function/handler.py @@ -0,0 +1,2 @@ +def handle(st): + print(st) diff --git a/template/python-armhf/function/requirements.txt b/template/python-armhf/function/requirements.txt new file mode 100644 index 000000000..e69de29bb diff --git a/template/python-armhf/index.py b/template/python-armhf/index.py new file mode 100644 index 000000000..415dd8e2d --- /dev/null +++ b/template/python-armhf/index.py @@ -0,0 +1,15 @@ +# Copyright (c) Alex Ellis 2017. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. + +import sys +from function import handler + +def get_stdin(): + buf = "" + for line in sys.stdin: + buf = buf + line + return buf + +if(__name__ == "__main__"): + st = get_stdin() + handler.handle(st) diff --git a/template/python-armhf/requirements.txt b/template/python-armhf/requirements.txt new file mode 100644 index 000000000..e69de29bb