Adding Knative Specific Dockerfile (#125)
* adding dockerfile for knative
* knative dockerfile
* knative dockerfile
* knative dockerfile
* adding dockerfile for knative
* adding dockerfile for knative
* Fix Knative dedicated function request pre-processing logic #253
* adding logic to read code from remote file
* knative dockerfile
* knative dockerfile
* knative dockerfile
* knative dockerfile
* fixing functin signature
* adding isInitialized
* updating knative docker file
* updating knative docker file
* addressing condition where no value/activation data given
* updating knative dockerfile
* updating knative dockerfile
diff --git a/core/nodejs10Action/knative/Dockerfile b/core/nodejs10Action/knative/Dockerfile
new file mode 100644
index 0000000..0ed61de
--- /dev/null
+++ b/core/nodejs10Action/knative/Dockerfile
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+FROM node:10.15.2-stretch
+RUN apt-get update && apt-get install -y \
+ imagemagick \
+ graphicsmagick \
+ unzip \
+ && rm -rf /var/lib/apt/lists/*
+WORKDIR /nodejsAction
+# COPY source code "*.js" from nodejsActionBase to current working dir
+RUN mkdir /src /platform
+COPY ./core/nodejsActionBase/*.js ./
+COPY ./core/nodejsActionBase/src/*.js ./src/
+COPY ./core/nodejsActionBase/platform/*.js ./platform/
+COPY . .
+# COPY the package.json to root container, so we can install npm packages a level up from user's packages,
+# so user's packages take precedence
+COPY ./core/nodejs10Action/package.json /
+RUN cd / && npm install --no-package-lock \
+ && npm cache clean --force
+EXPOSE 8080
+# The flag --experimental-worker is to be use with care as it's an expirimental feature more info here https://nodejs.org/docs/latest-v10.x/api/worker_threads.html
+CMD node --experimental-worker --expose-gc app.js
diff --git a/core/nodejs8Action/knative/Dockerfile b/core/nodejs8Action/knative/Dockerfile
new file mode 100644
index 0000000..27565be
--- /dev/null
+++ b/core/nodejs8Action/knative/Dockerfile
@@ -0,0 +1,37 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+FROM node:8.15.1
+RUN apt-get update && apt-get install -y \
+ imagemagick \
+ graphicsmagick \
+ unzip \
+ && rm -rf /var/lib/apt/lists/*
+WORKDIR /nodejsAction
+# COPY source code "*.js" from nodejsActionBase to current working dir
+RUN mkdir /src /platform
+COPY ./core/nodejsActionBase/*.js /
+COPY ./core/nodejsActionBase/src/*.js /src/
+COPY ./core/nodejsActionBase/platform/*.js /platform/
+COPY . .
+# COPY the package.json to root container, so we can install npm packages a level up from user's packages, so user's packages take precedence
+COPY ./core/nodejs8Action/package.json /
+# COPY source code "*.js" from nodejsActionBase to current working dir
+RUN cd / && npm install --no-package-lock \
+ && npm cache clean --force
+EXPOSE 8080
+CMD node --expose-gc app.js
diff --git a/core/nodejsActionBase/buildtemplate.yaml b/core/nodejsActionBase/buildtemplate.yaml
index ab80f99..0760ced 100644
--- a/core/nodejsActionBase/buildtemplate.yaml
+++ b/core/nodejsActionBase/buildtemplate.yaml
@@ -37,6 +37,9 @@
- name: OW_ACTION_RAW
description: flag to indicate raw HTTP handling, interpret and process an incoming HTTP body directly
default: "false"
+ - name: OW_PROJECT_URL
+ description: Location to local/remote file storage or public/private GitHub repo from where JavaScript source code needs to be evaluated
+ default: ""
steps:
- name: add-ow-env-to-dockerfile
image: "gcr.io/kaniko-project/executor:debug"
@@ -45,11 +48,22 @@
args:
- -c
- |
+ if [ -z ${OW_PROJECT_URL} ]; then
+ OW_ACTION_CODE="${OW_ACTION_CODE}"
+ else
+ TEMPDIR="knative-"$((1 + RANDOM % 100))
+ TEMPFILE=`basename "${OW_PROJECT_URL}"`
+ mkdir $TEMPDIR
+ cd $TEMPDIR
+ wget -O $TEMPFILE "${OW_PROJECT_URL}"
+ OW_ACTION_CODE=`cat $TEMPFILE`
+ cd ..
+ fi
cat <<EOF >> ${DOCKERFILE}
ENV __OW_RUNTIME_DEBUG "${OW_RUNTIME_DEBUG}"
ENV __OW_RUNTIME_PLATFORM "${OW_RUNTIME_PLATFORM}"
ENV __OW_ACTION_NAME "${OW_ACTION_NAME}"
- ENV __OW_ACTION_CODE "${OW_ACTION_CODE}"
+ ENV __OW_ACTION_CODE "$OW_ACTION_CODE"
ENV __OW_ACTION_MAIN "${OW_ACTION_MAIN}"
ENV __OW_ACTION_BINARY "${OW_ACTION_BINARY}"
ENV __OW_HTTP_METHODS "${OW_HTTP_METHODS}"
diff --git a/core/nodejsActionBase/platform/knative.js b/core/nodejsActionBase/platform/knative.js
index 1fc68a8..c8f37d8 100644
--- a/core/nodejsActionBase/platform/knative.js
+++ b/core/nodejsActionBase/platform/knative.js
@@ -75,64 +75,70 @@
}
}
+
/**
- * Pre-process the incoming
+ * Create request init data from the process environment
*/
-function preProcessInitData(env, initdata, valuedata, activationdata) {
+function createInitDataFromEnvironment(env) {
try {
- // Set defaults to use INIT data not provided on the request
- // Look first to the process (i.e., Container's) environment variables.
- var main = (typeof env.__OW_ACTION_MAIN === 'undefined') ? "main" : env.__OW_ACTION_MAIN;
+ var initdata = {};
+ initdata.main = (typeof env.__OW_ACTION_MAIN === 'undefined') ? "main" : env.__OW_ACTION_MAIN;
// TODO: Throw error if CODE is NOT defined!
- var code = (typeof env.__OW_ACTION_CODE === 'undefined') ? "" : env.__OW_ACTION_CODE;
- var binary = (typeof env.__OW_ACTION_BINARY === 'undefined') ? false : env.__OW_ACTION_BINARY.toLowerCase() === "true";
+ initdata.code = (typeof env.__OW_ACTION_CODE === 'undefined') ? "" : env.__OW_ACTION_CODE;
+ initdata.binary = (typeof env.__OW_ACTION_BINARY === 'undefined') ? false : env.__OW_ACTION_BINARY.toLowerCase() === "true";
// TODO: default to empty?
- var actionName = (typeof env.__OW_ACTION_NAME === 'undefined') ? "" : env.__OW_ACTION_NAME;
- var raw = (typeof env.__OW_ACTION_RAW === 'undefined') ? false : env.__OW_ACTION_RAW.toLowerCase() === "true";
+ initdata.actionName = (typeof env.__OW_ACTION_NAME === 'undefined') ? "" : env.__OW_ACTION_NAME;
+ initdata.raw = (typeof env.__OW_ACTION_RAW === 'undefined') ? false : env.__OW_ACTION_RAW.toLowerCase() === "true";
+
+ return initdata;
+
+ } catch(e){
+ console.error(e);
+ throw("Unable to process Initialization data: " + e.message);
+ }
+}
+/**
+ * Pre-process the init data from the request
+ */
+function preProcessInitData(initdata, valuedata, activationdata) {
+ try {
// Look for init data within the request (i.e., "stem cell" runtime, where code is injected by request)
if (typeof(initdata) !== "undefined") {
- if (initdata.name && typeof initdata.name === 'string') {
- actionName = initdata.name;
- }
+
if (initdata.main && typeof initdata.main === 'string') {
- main = initdata.main;
+ valuedata.main = initdata.main;
}
if (initdata.code && typeof initdata.code === 'string') {
- code = initdata.code;
+ valuedata.code = initdata.code;
}
if (initdata.binary) {
if (typeof initdata.binary === 'boolean') {
- binary = initdata.binary;
+ valuedata.binary = initdata.binary;
} else {
throw ("Invalid Init. data; expected boolean for key 'binary'.");
}
}
- if (initdata.raw ) {
+ if (initdata.raw) {
if (typeof initdata.raw === 'boolean') {
- raw = initdata.raw;
+ valuedata.raw = initdata.raw;
} else {
throw ("Invalid Init. data; expected boolean for key 'raw'.");
}
}
- }
- // Move the init data to the request body under the "value" key.
- // This will allow us to reuse the "openwhisk" /init route handler function
- valuedata.main = main;
- valuedata.code = code;
- valuedata.binary = binary;
- valuedata.raw = raw;
-
- // Action name is a special case, as we have a key collision on "name" between init. data and request
- // param. data (as they both appear within "body.value") so we must save it to its final location
- // as the default Action name as part of the activation data
- // NOTE: if action name is not present in the action data, we will set it regardless even if an empty string
- if (typeof(activationdata) !== "undefined" ) {
- if (typeof(activationdata.action_name) === "undefined" ||
- (typeof(activationdata.action_name) === "string" && activationdata.action_name.length == 0)){
- activationdata.action_name = actionName;
+ // Action name is a special case, as we have a key collision on "name" between init. data and request
+ // param. data (as they both appear within "body.value") so we must save it to its final location
+ // as the default Action name as part of the activation data
+ if (initdata.name && typeof initdata.name === 'string') {
+ if (typeof (activationdata) !== "undefined") {
+ if (typeof (activationdata.action_name) === "undefined" ||
+ (typeof (activationdata.action_name) === "string" &&
+ activationdata.action_name.length === 0)) {
+ activationdata.action_name = initdata.name;
+ }
+ }
}
}
@@ -241,10 +247,10 @@
// process initialization (i.e., "init") data
if (hasInitData(req)) {
- preProcessInitData(env, initData, valueData, activationData);
+ preProcessInitData(initData, valueData, activationData);
}
- if( hasActivationData(req)) {
+ if(hasActivationData(req)) {
// process HTTP request header and body to make it available to function as parameter data
preProcessHTTPContext(req, valueData);
@@ -325,7 +331,7 @@
delete body['binary'];
}
- //When the content-type is defined, check if the response is binary data or
+ // When the content-type is defined, check if the response is binary data or
// plain text and decode the plain text using a base64 decoder whenever needed.
// Should the body fail to decoded correctly, return an error to the caller.
if (contentTypeInHeader && headers[CONTENT_TYPE].lastIndexOf("image", 0) === 0) {
@@ -384,10 +390,15 @@
if (hasInitData(req) && !isStemCell(process.env))
throw ("Cannot initialize a runtime with a dedicated function.");
- if(hasInitData(req) && hasActivationData(req)){
+ // If this is a dedicated, uninitialized runtime, then copy INIT data from env. into the request
+ if( !isStemCell(process.env) && !service.initialized()){
+ let body = req.body || {};
+ body.init = createInitDataFromEnvironment(process.env);
+ }
- // Process request and process env. variables to provide them in the manner
- // an OpenWhisk Action expects them, as well as enable additional Http features.
+ // Different pre-processing logic based upon request data needed due Promise behavior
+ if(hasInitData(req) && hasActivationData(req)){
+ // Request has both Init and Run (activation) data
preProcessRequest(req);
// Invoke the OW "init" entrypoint
service.initCode(req).then(function () {
@@ -407,9 +418,7 @@
}
});
} else if(hasInitData(req)){
-
- // Process request and process env. variables to provide them in the manner
- // an OpenWhisk Action expects them, as well as enable additional Http features.
+ // Request has ONLY Init data
preProcessRequest(req);
// Invoke the OW "init" entrypoint
service.initCode(req).then(function (result) {
@@ -424,8 +433,21 @@
}
});
} else if(hasActivationData(req)){
- // Process request and process env. variables to provide them in the manner
- // an OpenWhisk Action expects them, as well as enable additional Http features.
+ // Request has ONLY Run (activation) data
+ preProcessRequest(req);
+ // Invoke the OW "run" entrypoint
+ service.runCode(req).then(function (result) {
+ postProcessResponse(req, result, res)
+ }).catch(function (error) {
+ console.error(error);
+ if (typeof error.code === "number" && typeof error.response !== "undefined") {
+ res.status(error.code).json(error.response);
+ } else {
+ console.error("[wrapEndpoint]", "invalid errored promise", JSON.stringify(error));
+ res.status(500).json({ error: "Internal error during function execution." });
+ }
+ });
+ } else {
preProcessRequest(req);
// Invoke the OW "run" entrypoint
service.runCode(req).then(function (result) {
@@ -440,7 +462,6 @@
}
});
}
-
} catch (e) {
res.status(500).json({error: "internal error during request processing."})
}
diff --git a/core/nodejsActionBase/src/service.js b/core/nodejsActionBase/src/service.js
index f84caec..e6beae6 100644
--- a/core/nodejsActionBase/src/service.js
+++ b/core/nodejsActionBase/src/service.js
@@ -56,6 +56,15 @@
}
/**
+ * Indicates if we have been initialized which is determined by if we have
+ * created a NodeActionRunner.
+ * @returns {boolean}
+ */
+ this.initialized = function isInitialized(){
+ return (typeof userCodeRunner !== 'undefined');
+ };
+
+ /**
* Starts the server.
*
* @param app express app