This is an automated email from the ASF dual-hosted git repository. alexkli pushed a commit to branch dotenv in repository https://gitbox.apache.org/repos/asf/openwhisk-wskdebug.git
commit 3333b2ec074d0f43df68582fe7c0e2adbb996cce Author: Alexander Klimetschek <[email protected]> AuthorDate: Wed Jul 15 23:31:12 2020 -0700 support openwhisk credentials stored in .env file and Adobe I/O Runtime variable names --- .gitignore | 2 + package-lock.json | 11 ++++ package.json | 2 + src/debugger.js | 4 ++ src/wskprops.js | 28 +++++---- test/wskprops.test.js | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 195 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 73ce74b..aa36988 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ coverage build coverage.lcov test-results + +.env \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a39ebd8..ec6eb41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -824,6 +824,11 @@ "esutils": "^2.0.2" } }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2353,6 +2358,12 @@ } } }, + "mock-fs": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.12.0.tgz", + "integrity": "sha512-/P/HtrlvBxY4o/PzXY9cCNBrdylDNxg7gnrv2sMNxj+UJ2m8jSpl0/A6fuJeNAWr99ZvGWH8XCbE0vmnM5KupQ==", + "dev": true + }, "mock-require": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz", diff --git a/package.json b/package.json index f53afc3..cac0c22 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "clone": "^2.1.2", "debug": "^4.1.1", "dockerode": "^3.2.0", + "dotenv": "^8.2.0", "fetch-retry": "^3.1.0", "fs-extra": "^8.1.0", "get-port": "^5.1.1", @@ -69,6 +70,7 @@ "eslint-plugin-mocha": "^6.3.0", "mocha": "^7.1.0", "mocha-multi-reporters": "^1.1.7", + "mock-fs": "^4.12.0", "mock-require": "^3.0.3", "nock": "^12.0.2", "nyc": "^15.0.0", diff --git a/src/debugger.js b/src/debugger.js index e3b282e..f2e1596 100644 --- a/src/debugger.js +++ b/src/debugger.js @@ -54,6 +54,10 @@ class Debugger { this.actionName = argv.action; this.wskProps = wskprops.get(); + if (Object.keys(this.wskProps).length === 0) { + log.error(`Error: Missing openwhisk credentials. Found no ~/.wskprops file or WSK_* environment variable.`); + process.exit(1); + } if (argv.ignoreCerts) { this.wskProps.ignore_certs = true; } diff --git a/src/wskprops.js b/src/wskprops.js index 4ea744d..310b2bb 100644 --- a/src/wskprops.js +++ b/src/wskprops.js @@ -23,6 +23,7 @@ const log = require('./log'); +const dotenv = require('dotenv'); const path = require('path'); const fs = require('fs-extra'); @@ -58,35 +59,42 @@ function getWskProps() { return wskProps; } +function getAioEnvProps() { + const envProps = {}; + // do first, as OW_* ones later shall take precedence + if (process.env.AIO_runtime_auth) { + envProps.apihost = "https://adobeioruntime.net"; + envProps.auth = process.env.AIO_runtime_auth; + envProps.namespace = process.env.AIO_runtime_namespace; + log.verbose(`Using openwhisk credential from AIO_runtime_auth environment variable`); + } + return envProps; +} + function getWskEnvProps() { const envProps = {}; - let authEnvVar; ENV_PARAMS.forEach((envName) => { if (process.env[envName]) { const key = envName.slice(3).toLowerCase(); envProps[key] = process.env[envName]; if (key === "auth" || key === "api_key") { - authEnvVar = envName; + log.verbose(`Using openwhisk credential from ${envName} environment variable`); } } }); - if (authEnvVar) { - log.verbose(`Using openwhisk credential from ${authEnvVar} environment variable`); - } return envProps; } module.exports = { get() { - const props = Object.assign(getWskProps(), getWskEnvProps()); + // load .env file if present + dotenv.config(); + + const props = Object.assign(getAioEnvProps(), getWskProps(), getWskEnvProps()); if (props.auth) { props.api_key = props.auth; delete props.auth; } - if (Object.keys(props).length === 0) { - log.error(`Error: Missing openwhisk credentials. Found no ~/.wskprops file or WSK_* environment variable.`); - process.exit(1); - } return props; }, ENV_PARAMS, diff --git a/test/wskprops.test.js b/test/wskprops.test.js new file mode 100644 index 0000000..fe2af13 --- /dev/null +++ b/test/wskprops.test.js @@ -0,0 +1,158 @@ +/* + * 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 wskprops = require('../src/wskprops'); +const assert = require('assert'); +const mockFs = require('mock-fs'); +const os = require('os'); + +function resetEnvVars() { + delete process.env.OW_AUTH; + delete process.env.OW_NAMESPACE; + delete process.env.OW_APIHOST; + delete process.env.WSK_CONFIG_FILE; + delete process.env.AIO_runtime_auth; + delete process.env.AIO_runtime_namespace; +} + +describe('wskprops', function() { + + beforeEach(function() { + resetEnvVars(); + }); + + afterEach(function() { + resetEnvVars(); + mockFs.restore(); + }); + + it("should read WSK_CONFIG_FILE", async function() { + process.env.WSK_CONFIG_FILE = "test/wskprops"; + + const props = wskprops.get(); + assert.strictEqual(props.apihost, "https://example.com"); + assert.strictEqual(props.namespace, "test"); + assert.strictEqual(props.api_key, "super-secret-key"); + }); + + it("should read ~/.wskprops", async function() { + mockFs({ + [`${os.homedir()}/.wskprops`]: +`APIHOST=https://home-wskprops +NAMESPACE=home-wskprops-namespace +AUTH=home-wskprops-auth` + }); + + const props = wskprops.get(); + assert.strictEqual(props.apihost, "https://home-wskprops"); + assert.strictEqual(props.namespace, "home-wskprops-namespace"); + assert.strictEqual(props.api_key, "home-wskprops-auth"); + }); + + it("should read OW_* vars", async function() { + process.env.OW_APIHOST = "https://ow_apihost"; + process.env.OW_NAMESPACE = "ow_namespace"; + process.env.OW_AUTH = "ow_auth"; + + const props = wskprops.get(); + assert.strictEqual(props.apihost, "https://ow_apihost"); + assert.strictEqual(props.namespace, "ow_namespace"); + assert.strictEqual(props.api_key, "ow_auth"); + }); + + it("should give OW_* vars precedence over WSK_CONFIG_FILE", async function() { + process.env.WSK_CONFIG_FILE = "test/wskprops"; + + process.env.OW_APIHOST = "https://ow_apihost"; + process.env.OW_NAMESPACE = "ow_namespace"; + process.env.OW_AUTH = "ow_auth"; + + const props = wskprops.get(); + assert.strictEqual(props.apihost, "https://ow_apihost"); + assert.strictEqual(props.namespace, "ow_namespace"); + assert.strictEqual(props.api_key, "ow_auth"); + }); + + it("should read AIO_* vars", async function() { + process.env.AIO_runtime_namespace = "aio_namespace"; + process.env.AIO_runtime_auth = "aio_auth"; + + const props = wskprops.get(); + assert.strictEqual(props.apihost, "https://adobeioruntime.net"); + assert.strictEqual(props.namespace, "aio_namespace"); + assert.strictEqual(props.api_key, "aio_auth"); + }); + + it("should give WSK_CONFIG_FILE precedence over AIO_* vars", async function() { + process.env.WSK_CONFIG_FILE = "test/wskprops"; + process.env.AIO_runtime_namespace = "aio_namespace"; + process.env.AIO_runtime_auth = "aio_auth"; + + const props = wskprops.get(); + assert.strictEqual(props.apihost, "https://example.com"); + assert.strictEqual(props.namespace, "test"); + assert.strictEqual(props.api_key, "super-secret-key"); + }); + + it("should give OW_* precedence over AIO_* vars", async function() { + process.env.AIO_runtime_namespace = "aio_namespace"; + process.env.AIO_runtime_auth = "aio_auth"; + + process.env.OW_APIHOST = "https://ow_apihost"; + process.env.OW_NAMESPACE = "ow_namespace"; + process.env.OW_AUTH = "ow_auth"; + + const props = wskprops.get(); + assert.strictEqual(props.apihost, "https://ow_apihost"); + assert.strictEqual(props.namespace, "ow_namespace"); + assert.strictEqual(props.api_key, "ow_auth"); + }); + + it("should read AIO_* from .env", async function() { + mockFs({ + ".env": +`AIO_runtime_namespace=aio_namespace +AIO_runtime_auth=aio_auth` + }); + + const props = wskprops.get(); + assert.strictEqual(props.apihost, "https://adobeioruntime.net"); + assert.strictEqual(props.namespace, "aio_namespace"); + assert.strictEqual(props.api_key, "aio_auth"); + }); + + it("should read WSK_CONFIG_FILE from .env", async function() { + mockFs({ + ".env": "WSK_CONFIG_FILE=mywskprops", + "mywskprops": +`APIHOST=https://example.com +NAMESPACE=test +AUTH=super-secret-key` + }); + + const props = wskprops.get(); + assert.strictEqual(props.apihost, "https://example.com"); + assert.strictEqual(props.namespace, "test"); + assert.strictEqual(props.api_key, "super-secret-key"); + }); + +});
