You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by mr...@apache.org on 2019/04/30 21:28:25 UTC

[incubator-openwhisk-runtime-nodejs] branch master updated: Adding Knative Specific Dockerfile (#125)

This is an automated email from the ASF dual-hosted git repository.

mrutkowski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-runtime-nodejs.git


The following commit(s) were added to refs/heads/master by this push:
     new 27246e2  Adding Knative Specific Dockerfile (#125)
27246e2 is described below

commit 27246e2f2d1f1c512c597d8fe91942e83031c0a4
Author: Priti Desai <pd...@us.ibm.com>
AuthorDate: Tue Apr 30 14:28:21 2019 -0700

    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
---
 core/nodejs10Action/knative/Dockerfile    |  38 ++++++++++
 core/nodejs8Action/knative/Dockerfile     |  37 ++++++++++
 core/nodejsActionBase/buildtemplate.yaml  |  16 ++++-
 core/nodejsActionBase/platform/knative.js | 111 ++++++++++++++++++------------
 core/nodejsActionBase/src/service.js      |   9 +++
 5 files changed, 165 insertions(+), 46 deletions(-)

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 @@ spec:
   - 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 @@ spec:
     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 @@ function removeInitData(body) {
     }
 }
 
+
 /**
- * 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 @@ function preProcessRequest(req){
 
         // 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 @@ function postProcessResponse(req, result, res) {
         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 @@ function PlatformKnativeImpl(platformFactory) {
             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 @@ function PlatformKnativeImpl(platformFactory) {
                     }
                 });
             } 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 @@ function PlatformKnativeImpl(platformFactory) {
                     }
                 });
             } 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 @@ function PlatformKnativeImpl(platformFactory) {
                     }
                 });
             }
-
         } 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 @@ function NodeActionService(config) {
     }
 
     /**
+     * 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