Proper handling of ssl certificate validation (#21)

diff --git a/.travis.yml b/.travis.yml
index 051e253..d3a6dcc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,7 +22,7 @@
   - docker
 env:
   global:
-    - IGNORE_CERTS=true
+    - __OW_IGNORE_CERTS=true
     - REDIS=redis://172.17.0.1:6379
 before_install:
   - ./travis/scancode.sh
diff --git a/README.md b/README.md
index 238b23e..f12aadb 100644
--- a/README.md
+++ b/README.md
@@ -186,6 +186,27 @@
 is progressing. Redis entries are deleted after completion and, as an added
 safety, expire after twenty-four hours.
 
+# OpenWhisk SSL configuration
+
+Additional configuration is required when using an OpenWhisk instance with
+self-signed certificates to disable SSL certificate validation. The input
+parameter object must contain a parameter of type dictionary named `$composer`.
+This dictionary must contain a dictionary named `openwhisk`. The `openwhisk`
+dictionary must contain a field named `ignore_certs` with value `true`:
+```json
+{
+    "$composer": {
+        "openwhisk": {
+            "ignore_certs": true
+        }
+    },
+    ...
+}
+```
+
+This explicit SSL configuration is currently only necessary when using parallel
+combinators or the `async` combinator.
+
 # Disclaimer
 
 Apache OpenWhisk Composer is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Apache Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF.
diff --git a/conductor.js b/conductor.js
index ac595a4..fab979b 100644
--- a/conductor.js
+++ b/conductor.js
@@ -41,6 +41,7 @@
   // try to extract apihost and key first from whisk property file file and then from process.env
   let apihost
   let apikey
+  let ignorecerts
 
   try {
     const wskpropsPath = process.env.WSK_CONFIG_FILE || path.join(os.homedir(), '.wskprops')
@@ -60,8 +61,9 @@
 
   if (process.env.__OW_API_HOST) apihost = process.env.__OW_API_HOST
   if (process.env.__OW_API_KEY) apikey = process.env.__OW_API_KEY
+  if (process.env.__OW_IGNORE_CERTS) ignorecerts = process.env.__OW_IGNORE_CERTS
 
-  const wsk = openwhisk(Object.assign({ apihost, api_key: apikey }, options))
+  const wsk = openwhisk(Object.assign({ apihost, api_key: apikey, ignore_certs: ignorecerts }, options))
   wsk.compositions = new Compositions(wsk)
   return wsk
 }
@@ -134,7 +136,7 @@
     const stack = [{ marker: true }].concat(p.s.stack)
     const barrierId = uuid()
     console.log(`barrierId: ${barrierId}, spawning: ${array.length}`)
-    if (!wsk) wsk = openwhisk({ ignore_certs: true })
+    if (!wsk) wsk = openwhisk(p.s.openwhisk)
     if (!db) db = createRedisClient(p)
     return db.lpushAsync(live(barrierId), 42) // push marker
       .then(() => db.expireAsync(live(barrierId), expiration))
@@ -296,7 +298,7 @@
     async ({ p, node, index, inspect, step }) {
       p.params.$composer = { state: p.s.state, stack: [{ marker: true }].concat(p.s.stack), redis: p.s.redis }
       p.s.state = index + node.return
-      if (!wsk) wsk = openwhisk({ ignore_certs: true })
+      if (!wsk) wsk = openwhisk(p.s.openwhisk)
       return wsk.actions.invoke({ name: process.env.__OW_ACTION_NAME, params: p.params })
         .then(response => ({ method: 'async', activationId: response.activationId, sessionId: p.s.session }), error => {
           console.error(error) // invoke failed
diff --git a/docs/COMBINATORS.md b/docs/COMBINATORS.md
index 9a157d2..336d7cc 100644
--- a/docs/COMBINATORS.md
+++ b/docs/COMBINATORS.md
@@ -424,6 +424,9 @@
 
 ## Async
 
+The `async` combinator may require an SSL configuration as discussed
+[here](../README.md#openwhisk-ssl-configuration).
+
 `composer.async(composition_1, composition_2, ...)` runs a sequence of
 compositions asynchronously. It invokes the sequence but does not wait for it to
 execute. It immediately returns a dictionary that includes a field named
@@ -439,6 +442,9 @@
 Parallel combinators require access to a Redis instance as discussed
 [here](../README.md#parallel-compositions-with-redis).
 
+Parallel combinators may require an SSL configuration as discussed
+[here](../README.md#openwhisk-ssl-configuration).
+
 `composer.parallel(composition_1, composition_2, ...)` and its synonymous
 `composer.par(composition_1, composition_2, ...)` invoke a series of
 compositions (possibly empty) in parallel.
@@ -468,6 +474,9 @@
 Parallel combinators require access to a Redis instance as discussed
 [here](../README.md#parallel-compositions-with-redis).
 
+Parallel combinators may require an SSL configuration as discussed
+[here](../README.md#openwhisk-ssl-configuration).
+
 `composer.map(composition_1, composition_2, ...)` makes multiple parallel
 invocations of a sequence of compositions.
 
diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md
index dabe630..db45108 100644
--- a/docs/COMMANDS.md
+++ b/docs/COMMANDS.md
@@ -110,6 +110,10 @@
 used in its place. If neither is available, the `deploy` command extracts the
 `APIHOST` key from the whisk property file for the current user.
 
+If the `--insecure` flag is set or the environment variable `__OW_IGNORE_CERTS`
+is set to `true`, the `deploy` command ignores SSL certificates validation
+failures.
+
 If the `--auth` flag is absent, the environment variable `__OW_API_KEY` is used
 in its place. If neither is available, the `deploy` command extracts the `AUTH`
 key from the whisk property file for the current user.
diff --git a/package.json b/package.json
index 058f1f0..4069188 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,7 @@
   ],
   "dependencies": {
     "minimist": "^1.2.0",
-    "openwhisk": "^3.11.0",
+    "openwhisk": "^3.18.0",
     "terser": "^3.8.2"
   },
   "devDependencies": {
diff --git a/test/conductor.js b/test/conductor.js
index 90b936e..157d885 100644
--- a/test/conductor.js
+++ b/test/conductor.js
@@ -23,7 +23,7 @@
 const composer = require('../composer')
 const conductor = require('../conductor')
 const name = 'TestAction'
-const wsk = conductor({ ignore_certs: process.env.IGNORE_CERTS && process.env.IGNORE_CERTS !== 'false' && process.env.IGNORE_CERTS !== '0' })
+const wsk = conductor()
 
 // deploy action
 const define = action => wsk.actions.delete(action.name).catch(() => { }).then(() => wsk.actions.create(action))
@@ -37,6 +37,9 @@
 const redis = process.env.REDIS ? { uri: process.env.REDIS } : false
 if (process.env.REDIS && process.env.REDIS_CA) redis.ca = process.env.REDIS_CA
 
+// openwhisk configuration
+const openwhisk = process.env.__OW_IGNORE_CERTS ? { ignore_certs: true } : {}
+
 describe('composer', function () {
   let n, x, y // dummy variables
 
@@ -63,7 +66,7 @@
       })
 
       it('action must return activationId', function () {
-        return invoke(composer.async('isNotOne'), { n: 1 }).then(activation => assert.ok(activation.response.result.activationId))
+        return invoke(composer.async('isNotOne'), { n: 1, $composer: { openwhisk } }).then(activation => assert.ok(activation.response.result.activationId))
       })
 
       it('action name must parse to fully qualified', function () {
@@ -321,17 +324,17 @@
       describe('parallel', function () {
         const test = redis ? it : it.skip
         test('parallel', function () {
-          return invoke(composer.parallel('TripleAndIncrement', 'DivideByTwo'), { n: 42, $composer: { redis } })
+          return invoke(composer.parallel('TripleAndIncrement', 'DivideByTwo'), { n: 42, $composer: { redis, openwhisk } })
             .then(activation => assert.deepStrictEqual(activation.response.result, { value: [{ n: 127 }, { n: 21 }] }))
         })
 
         test('par', function () {
-          return invoke(composer.par('DivideByTwo', 'TripleAndIncrement', 'isEven'), { n: 42, $composer: { redis } })
+          return invoke(composer.par('DivideByTwo', 'TripleAndIncrement', 'isEven'), { n: 42, $composer: { redis, openwhisk } })
             .then(activation => assert.deepStrictEqual(activation.response.result, { value: [{ n: 21 }, { n: 127 }, { value: true }] }))
         })
 
         test('map', function () {
-          return invoke(composer.map('TripleAndIncrement', 'DivideByTwo'), { value: [{ n: 3 }, { n: 5 }, { n: 7 }], $composer: { redis } })
+          return invoke(composer.map('TripleAndIncrement', 'DivideByTwo'), { value: [{ n: 3 }, { n: 5 }, { n: 7 }], $composer: { redis, openwhisk } })
             .then(activation => assert.deepStrictEqual(activation.response.result, { value: [{ n: 5 }, { n: 8 }, { n: 11 }] }))
         })
       })