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