Skip to content

Commit

Permalink
Updates to 'faas-cli template pull'
Browse files Browse the repository at this point in the history
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 <ems5311@gmail.com>
  • Loading branch information
ericstoekl committed Nov 9, 2017
1 parent d77e292 commit 927400a
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 40 deletions.
107 changes: 69 additions & 38 deletions commands/fetch_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -72,53 +81,48 @@ 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 {
// Process only directories inside "template" at root
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
}
}
}
}
Expand All @@ -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...")
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions stack/language_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

func Test_ParseYAMLDataForLanguageTemplate(t *testing.T) {
dataProvider := []struct {
langTemplateTest := []struct {
Input string
Expected *LanguageTemplate
}{
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 10 additions & 0 deletions template/go-armhf/function/handler.go
Original file line number Diff line number Diff line change
@@ -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))
}
19 changes: 19 additions & 0 deletions template/go-armhf/main.go
Original file line number Diff line number Diff line change
@@ -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))
}
5 changes: 5 additions & 0 deletions template/node-armhf/build.sh
Original file line number Diff line number Diff line change
@@ -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 .

5 changes: 5 additions & 0 deletions template/node-armhf/function/handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"use strict"

module.exports = (context, callback) => {
callback(undefined, {status: "done"});
}
12 changes: 12 additions & 0 deletions template/node-armhf/function/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
31 changes: 31 additions & 0 deletions template/node-armhf/index.js
Original file line number Diff line number Diff line change
@@ -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);
};
15 changes: 15 additions & 0 deletions template/node-armhf/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
2 changes: 2 additions & 0 deletions template/python-armhf/function/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def handle(st):
print(st)
Empty file.
15 changes: 15 additions & 0 deletions template/python-armhf/index.py
Original file line number Diff line number Diff line change
@@ -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)
Empty file.

0 comments on commit 927400a

Please sign in to comment.