Skip to content
This repository has been archived by the owner on Dec 15, 2021. It is now read-only.

Commit

Permalink
Merge pull request #235 from andresmgot/multiVersion
Browse files Browse the repository at this point in the history
Support different runtime versions
  • Loading branch information
andresmgot authored Jul 31, 2017
2 parents 0f9ac68 + 6dab6fd commit 77cb32c
Show file tree
Hide file tree
Showing 13 changed files with 161 additions and 41 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:6.10-alpine
FROM node:6-alpine

ARG TARGET=http-trigger
RUN apk add --no-cache git
Expand Down
12 changes: 12 additions & 0 deletions docker/runtime/nodejs/Dockerfile.8
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM node:8-alpine

ARG TARGET=http-trigger
RUN apk add --no-cache git

ADD lib/helper.js /lib/
ADD ${TARGET}/kubeless.js /
ADD ${TARGET}/package.json /

RUN npm install

CMD ["node", "/kubeless.js"]
128 changes: 88 additions & 40 deletions pkg/utils/k8sutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"io/ioutil"
"log"
"os"
"regexp"
"strings"

"github.com/Sirupsen/logrus"
Expand Down Expand Up @@ -51,12 +52,14 @@ import (
)

const (
pythonRuntime = "bitnami/kubeless-python@sha256:6789266df0c97333f76e23efd58cf9c7efe24fa3e83b5fc826fd5cc317699b55"
pythonPubsubRuntime = "bitnami/kubeless-event-consumer@sha256:5ce469529811acf49c4d20bcd8a675be7aa029b43cf5252a8c9375b170859d83"
nodejsRuntime = "bitnami/kubeless-nodejs@sha256:6e86ed023f25cbe410d6085ca16ee14a9fb4b1df10034c05375d5e24e0c59acb"
nodejsPubsubRuntime = "bitnami/kubeless-nodejs-event-consumer@sha256:b027bfef5f99c3be68772155a1feaf1f771ab9a3c7bb49bef2e939d6b766abec"
rubyRuntime = "jbianquettibitnami/kubeless-ruby@sha256:9ea43e4e1570b46ae272e9f81a0ea4736e4956ee2ee67d8def29287a1d7153fe"
pubsubFunc = "PubSub"
python27Http = "bitnami/kubeless-python@sha256:6789266df0c97333f76e23efd58cf9c7efe24fa3e83b5fc826fd5cc317699b55"
python27Pubsub = "bitnami/kubeless-event-consumer@sha256:5ce469529811acf49c4d20bcd8a675be7aa029b43cf5252a8c9375b170859d83"
node6Http = "bitnami/kubeless-nodejs@sha256:6e86ed023f25cbe410d6085ca16ee14a9fb4b1df10034c05375d5e24e0c59acb"
node6Pubsub = "bitnami/kubeless-nodejs-event-consumer@sha256:b027bfef5f99c3be68772155a1feaf1f771ab9a3c7bb49bef2e939d6b766abec"
node8Http = "bitnami/kubeless-nodejs@sha256:29077c5131ab63c557507596958510e9d2011dc0e88b968371c531ba3b41c47f"
node8Pubsub = "bitnami/kubeless-nodejs-event-consumer@sha256:4d005c9c0b462750d9ab7f1305897e7a01143fe869d3b722ed3330560f9c7fb5"
ruby24Http = "jbianquettibitnami/kubeless-ruby@sha256:9ea43e4e1570b46ae272e9f81a0ea4736e4956ee2ee67d8def29287a1d7153fe"
pubsubFunc = "PubSub"
)

// GetClient returns a k8s clientset to the request from inside of cluster
Expand Down Expand Up @@ -168,6 +171,81 @@ func GetFunction(funcName, ns string) (spec.Function, error) {
return f, nil
}

// GetFunctionData given a runtime returns an Image ID, the dependencies filename and the function filename
func GetFunctionData(runtime, ftype, modName string) (imageName, depName, fileName string, err error) {
err = nil
imageName = ""
depName = ""
fileName = ""

type runtimeVersion struct {
version string
httpImage string
pubsubImage string
}

python27 := runtimeVersion{version: "2.7", httpImage: python27Http, pubsubImage: python27Pubsub}
python := []runtimeVersion{python27}

node6 := runtimeVersion{version: "6", httpImage: node6Http, pubsubImage: node6Pubsub}
node8 := runtimeVersion{version: "8", httpImage: node8Http, pubsubImage: node8Pubsub}
node := []runtimeVersion{node6, node8}

ruby24 := runtimeVersion{version: "2.4", httpImage: ruby24Http, pubsubImage: ""}
ruby := []runtimeVersion{ruby24}

runtimeID := regexp.MustCompile("[a-zA-Z]+").FindString(runtime)
version := regexp.MustCompile("[0-9.]+").FindString(runtime)

var versionsDef []runtimeVersion
switch {
case runtimeID == "python":
fileName = modName + ".py"
versionsDef = python
depName = "requirements.txt"
case runtimeID == "nodejs":
fileName = modName + ".js"
versionsDef = node
depName = "package.json"
case runtimeID == "ruby":
fileName = modName + ".rb"
versionsDef = ruby
depName = "Gemfile"
default:
err = errors.New("The given runtime is not valid")
return
}
imageNameEnvVar := ""
if ftype == pubsubFunc {
imageNameEnvVar = strings.ToUpper(runtime) + "_PUBSUB_RUNTIME"
} else {
imageNameEnvVar = strings.ToUpper(runtime) + "_RUNTIME"
}
if imageName = os.Getenv(imageNameEnvVar); imageName == "" {
rVersion := runtimeVersion{"", "", ""}
for i := range versionsDef {
if versionsDef[i].version == version {
rVersion = versionsDef[i]
break
}
}
if ftype == pubsubFunc {
if rVersion.pubsubImage == "" {
err = errors.New("The given runtime and version does not have a valid image for event based functions")
} else {
imageName = rVersion.pubsubImage
}
} else {
if rVersion.httpImage == "" {
err = errors.New("The given runtime and version does not have a valid image for http based functions")
} else {
imageName = rVersion.httpImage
}
}
}
return
}

// EnsureK8sResources creates/updates k8s objects (deploy, svc, configmap) for the function
func EnsureK8sResources(ns, name string, funcObj *spec.Function, client kubernetes.Interface) error {
str := strings.Split(funcObj.Spec.Handler, ".")
Expand All @@ -177,40 +255,10 @@ func EnsureK8sResources(ns, name string, funcObj *spec.Function, client kubernet
funcHandler := str[1]
modName := str[0]
fileName := modName
imageName, depName, fileName, err := GetFunctionData(funcObj.Spec.Runtime, funcObj.Spec.Type, modName)

imageName := ""
depName := ""
switch {
case strings.Contains(funcObj.Spec.Runtime, "python"):
fileName = modName + ".py"
if imageName = os.Getenv("PYTHON_RUNTIME"); imageName == "" {
imageName = pythonRuntime
}
if funcObj.Spec.Type == pubsubFunc {
if imageName = os.Getenv("PYTHON_PUBSUB_RUNTIME"); imageName == "" {
imageName = pythonPubsubRuntime
}
}
depName = "requirements.txt"
case strings.Contains(funcObj.Spec.Runtime, "go"):
fileName = modName + ".go"
case strings.Contains(funcObj.Spec.Runtime, "nodejs"):
fileName = modName + ".js"
if imageName = os.Getenv("NODEJS_RUNTIME"); imageName == "" {
imageName = nodejsRuntime
}
if funcObj.Spec.Type == pubsubFunc {
if imageName = os.Getenv("NODEJS_PUBSUB_RUNTIME"); imageName == "" {
imageName = nodejsPubsubRuntime
}
}
depName = "package.json"
case strings.Contains(funcObj.Spec.Runtime, "ruby"):
fileName = modName + ".rb"
if imageName = os.Getenv("RUBY_RUNTIME"); imageName == "" {
imageName = rubyRuntime
}
depName = "Gemfile"
if err != nil {
return err
}

//add configmap
Expand Down Expand Up @@ -252,7 +300,7 @@ func EnsureK8sResources(ns, name string, funcObj *spec.Function, client kubernet
},
}

_, err := client.Core().ConfigMaps(ns).Create(configMap)
_, err = client.Core().ConfigMaps(ns).Create(configMap)
if err != nil && k8sErrors.IsAlreadyExists(err) {
data, _ := json.Marshal(configMap)
_, err = client.Core().ConfigMaps(ns).Patch(configMap.Name, types.StrategicMergePatchType, data)
Expand Down
60 changes: 60 additions & 0 deletions pkg/utils/k8sutil_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package utils

import (
"os"
"testing"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -73,3 +74,62 @@ func TestDeleteK8sResources(t *testing.T) {
t.Errorf("failed to delete service")
}
}

func check(runtime, ftype, fname string, values []string, t *testing.T) {
imageName, depName, fileName, err := GetFunctionData(runtime, ftype, fname)
if err != nil {
t.Fatalf("Retrieving the image returned err: %v", err)
}
if imageName == "" {
t.Fatalf("Retrieving the image returned an empty Image ID")
}
if depName != values[0] {
t.Fatalf("Retrieving the image returned a wrong dependencies file. Received " + depName + " while expecting " + values[0])
}
if fileName != values[1] {
t.Fatalf("Retrieving the image returned a wrong file name. Received " + fileName + " while expecting " + values[1])
}
}
func TestGetFunctionData(t *testing.T) {

expectedValues := []string{"requirements.txt", "test.py"}
check("python2.7", "HTTP", "test", expectedValues, t)
check("python2.7", "PubSub", "test", expectedValues, t)

expectedValues = []string{"package.json", "test.js"}
check("nodejs6", "HTTP", "test", expectedValues, t)
check("nodejs6", "PubSub", "test", expectedValues, t)
check("nodejs8", "HTTP", "test", expectedValues, t)
check("nodejs8", "PubSub", "test", expectedValues, t)

expectedValues = []string{"Gemfile", "test.rb"}
check("ruby2.4", "HTTP", "test", expectedValues, t)

_, _, _, err := GetFunctionData("unexistent", "HTTP", "test")
if err == nil {
t.Fatalf("Retrieving data for 'unexistent' should return an error")
}

expectedImageName := "ruby-test-image"
os.Setenv("RUBY_RUNTIME", expectedImageName)
imageR, _, _, errR := GetFunctionData("ruby", "HTTP", "test")
if errR != nil {
t.Fatalf("Retrieving the image returned err: %v", err)
}
if imageR != expectedImageName {
t.Fatalf("Expecting " + imageR + " to be set to " + expectedImageName)
}
os.Unsetenv("RUBY_RUNTIME")

expectedImageName = "ruby-pubsub-test-image"
os.Setenv("RUBY_PUBSUB_RUNTIME", "ruby-pubsub-test-image")
imageR, _, _, errR = GetFunctionData("ruby", "PubSub", "test")
if errR != nil {
t.Fatalf("Retrieving the image returned err: %v", err)
}
if imageR != expectedImageName {
t.Fatalf("Expecting " + imageR + " to be set to " + expectedImageName)
}
os.Unsetenv("RUBY_PUBSUB_RUNTIME")

}

0 comments on commit 77cb32c

Please sign in to comment.