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

alexkli pushed a commit to branch concurrency-not-supported
in repository https://gitbox.apache.org/repos/asf/openwhisk-wskdebug.git

commit c24b163a4f0158317f83a07f2afa89a9d586b4a6
Author: Alexander Klimetschek <[email protected]>
AuthorDate: Tue Mar 31 01:19:41 2020 -0700

    Concurrency error when using wskdebug with IBM Cloud Functions #7
    
    if concurrency > 1 is not supported, fallback to activation db agent
---
 src/agentmgr.js       |  21 ++++++-
 test/agentmgr.test.js | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++
 test/test.js          |   5 ++
 3 files changed, 187 insertions(+), 3 deletions(-)

diff --git a/src/agentmgr.js b/src/agentmgr.js
index 8b59f28..15ea202 100644
--- a/src/agentmgr.js
+++ b/src/agentmgr.js
@@ -158,7 +158,7 @@ class AgentMgr {
                 console.log("This OpenWhisk does not support action 
concurrency. Debugging will be a bit slower. Consider using '--ngrok' which 
might be a faster option.");
 
                 agentName = "polling activation db";
-                agentCode = await this.getPollingActivationRecordAgent();
+                agentCode = await this.getPollingActivationDbAgent();
             }
         }
 
@@ -185,7 +185,17 @@ class AgentMgr {
             });
         }
 
-        await this.pushAgent(action, agentCode, backupName);
+        try {
+            await this.pushAgent(action, agentCode, backupName);
+        } catch (e) {
+            // openwhisk does not support concurrent nodejs actions, try with 
another
+            if (e.statusCode === 400 && e.error && typeof e.error.error === 
"string" && e.error.error.includes("concurrency")) {
+                console.log(`The Openwhisk server does not support concurrent 
actions, using alternative agent. Consider using --ngrok for a possibly faster 
agent.`);
+                this.concurrency = false;
+                agentCode = await this.getPollingActivationDbAgent();
+                await this.pushAgent(action, agentCode, backupName);
+            }
+        }
 
         if (this.argv.verbose) {
             console.log(`Agent installed.`);
@@ -259,6 +269,11 @@ class AgentMgr {
                             const a = activations[0];
                             if (a.response && a.response.result && 
!this.activationsSeen[a.activationId]) {
                                 activation = a;
+                                if (!activation.response.success) {
+                                    throw {
+                                        error: activation
+                                    };
+                                }
                                 break;
                             }
                         }
@@ -389,7 +404,7 @@ class AgentMgr {
         return fs.readFileSync(`${__dirname}/../agent/agent-concurrency.js`, 
{encoding: 'utf8'});
     }
 
-    async getPollingActivationRecordAgent() {
+    async getPollingActivationDbAgent() {
         // this needs 2 helper actions in addition to the agent in place of 
the action
         await this.createHelperAction(`${this.actionName}_wskdebug_invoked`,   
`${__dirname}/../agent/echo.js`);
         await this.createHelperAction(`${this.actionName}_wskdebug_completed`, 
`${__dirname}/../agent/echo.js`);
diff --git a/test/agentmgr.test.js b/test/agentmgr.test.js
new file mode 100644
index 0000000..cf069b7
--- /dev/null
+++ b/test/agentmgr.test.js
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+/* eslint-env mocha */
+
+'use strict';
+
+const Debugger = require("../src/debugger");
+
+const test = require('./test');
+
+describe('agentmgr',  function() {
+    this.timeout(30000);
+
+    before(function() {
+        test.isDockerInstalled();
+    });
+
+    beforeEach(async function() {
+        await test.beforeEach();
+    });
+
+    afterEach(function() {
+        test.afterEach();
+    });
+
+    it("should use non-concurrrent agent if openwhisk does not support 
concurrency", async function() {
+        const action = "myaction";
+        const code = `const main = () => ({ msg: 'WRONG' });`;
+
+        test.mockAction(action, code);
+
+        test.mockCreateBackupAction(action);
+
+        // wskdebug overwriting the action with the agent
+        test.openwhiskNock()
+            .put(
+                `${test.openwhiskApiUrlActions()}/${action}?overwrite=true`,
+                body => body.annotations.some(v => v.key === "wskdebug" && 
v.value === true)
+            )
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(400, {
+                code: 'df940ccf1d076f103c3743685c25d2b2',
+                error: 'The request content was malformed:\nrequirement 
failed: concurrency 200 exceeds allowed threshold of 1'
+            });
+
+        // another wskdebug with non-concurrent action
+        test.openwhiskNock()
+            .put(
+                `${test.openwhiskApiUrlActions()}/${action}?overwrite=true`,
+                body => body.annotations.some(v => v.key === "wskdebug" && 
v.value === true)
+            )
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(200, test.nodejsActionDescription(action));
+
+        // helper actions for non-concurrent action
+        test.openwhiskNock()
+            
.put(`${test.openwhiskApiUrlActions()}/${action}_wskdebug_invoked?overwrite=true`)
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(200, test.nodejsActionDescription(action));
+        test.openwhiskNock()
+            
.put(`${test.openwhiskApiUrlActions()}/${action}_wskdebug_completed?overwrite=true`)
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(200, test.nodejsActionDescription(action));
+
+
+        // invocation
+        test.openwhiskNock()
+            .get(`${test.openwhiskApiUrl()}/activations`)
+            .query(query => query.name === `${action}_wskdebug_invoked`)
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(200, [{
+                activationId: "dummy-invoked",
+                response: {
+                    success: true,
+                    result: {
+                        $activationId: "1234567890"
+                    }
+                }
+            }]);
+
+        // completion of invocation
+        test.openwhiskNock()
+            
.post(`${test.openwhiskApiUrlActions()}/${action}_wskdebug_completed?blocking=true`,
 {
+                msg: "CORRECT",
+                $activationId: "1234567890"
+            })
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(200, test.nodejsActionDescription(action));
+
+        // abort polling
+        test.openwhiskNock()
+            .get(`${test.openwhiskApiUrl()}/activations`)
+            .query(query => query.name === `${action}_wskdebug_invoked`)
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(200, [{
+                activationId: "dummy-invoked-2",
+                response: {
+                    success: false,
+                    result: {
+                        error: {
+                            code: 43
+                        },
+                        $activationId: "99999999999"
+                    }
+                }
+            }]);
+
+        // shutdown/restore process
+        test.mockReadBackupAction(action, code);
+        test.mockRestoreAction(action, code);
+        test.mockRemoveBackupAction(action);
+        test.openwhiskNock()
+            
.get(`${test.openwhiskApiUrlActions()}/${action}_wskdebug_invoked?code=false`)
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(200, {});
+        test.openwhiskNock()
+            
.delete(`${test.openwhiskApiUrlActions()}/${action}_wskdebug_invoked`)
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(200, {});
+        test.openwhiskNock()
+            
.get(`${test.openwhiskApiUrlActions()}/${action}_wskdebug_completed?code=false`)
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(200, {});
+        test.openwhiskNock()
+            
.delete(`${test.openwhiskApiUrlActions()}/${action}_wskdebug_completed`)
+            .matchHeader("authorization", test.openwhiskApiAuthHeader())
+            .reply(200, {});
+
+
+        process.chdir("test/nodejs/plain-flat");
+        const argv = {
+            port: test.port,
+            action: "myaction",
+            sourcePath: `${process.cwd()}/action.js`,
+            invokeParams: '{ "key": "invocationOnSourceModification" }'
+        };
+
+        const dbgr = new Debugger(argv);
+        await dbgr.start();
+        dbgr.run();
+
+        // wait a bit
+        await test.sleep(500);
+
+        await dbgr.stop();
+
+        test.assertAllNocksInvoked();
+    });
+});
diff --git a/test/test.js b/test/test.js
index 5a266d7..34818e0 100644
--- a/test/test.js
+++ b/test/test.js
@@ -74,6 +74,10 @@ function openwhiskNock() {
     return openwhisk;
 }
 
+function openwhiskApiUrl() {
+    return `/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}`;
+}
+
 function openwhiskApiUrlActions() {
     return `/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}/actions`;
 }
@@ -522,6 +526,7 @@ module.exports = {
     mockActionDoubleInvocation,
     // advanced
     openwhiskNock,
+    openwhiskApiUrl,
     openwhiskApiUrlActions,
     openwhiskApiAuthHeader,
     mockAction,

Reply via email to