This is an automated email from the ASF dual-hosted git repository.
berstler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git
The following commit(s) were added to refs/heads/master by this push:
new cd409cd API GW V2 updates (#2436)
cd409cd is described below
commit cd409cd664a9ae66c2298a410ba4236cc16437e8
Author: Mark Deuser <[email protected]>
AuthorDate: Wed Jul 12 12:46:04 2017 -0400
API GW V2 updates (#2436)
- Do not enforce need for gwUrl parameter in api gw actions
- Enable http redirect forwarding
- Add automated test to validate rejection of api creation with invalid
auth key
---
core/routemgmt/common/apigw-utils.js | 3 ++
core/routemgmt/common/utils.js | 5 ++
core/routemgmt/createApi/createApi.js | 5 +-
core/routemgmt/deleteApi/deleteApi.js | 56 +++++++++----------
core/routemgmt/getApi/getApi.js | 62 +++++++++++-----------
.../actions/test/ApiGwRoutemgmtActionTests.scala | 8 +--
.../scala/whisk/core/cli/test/ApiGwTests.scala | 35 +++++++++++-
tools/cli/go-whisk-cli/commands/action.go | 7 ++-
.../go-whisk-cli/wski18n/resources/en_US.all.json | 8 +--
9 files changed, 115 insertions(+), 74 deletions(-)
diff --git a/core/routemgmt/common/apigw-utils.js
b/core/routemgmt/common/apigw-utils.js
index c903f0d..5f60e1c 100644
--- a/core/routemgmt/common/apigw-utils.js
+++ b/core/routemgmt/common/apigw-utils.js
@@ -39,6 +39,7 @@ function addApiToGateway(gwInfo, spaceGuid, swaggerApi,
apiId) {
console.log('addApiToGateway: ');
try {
var options = {
+ followAllRedirects: true,
url: gwInfo.gwUrl+'/'+encodeURIComponent(spaceGuid) + '/apis',
json: swaggerApi, // Use of json automatically sets header:
'Content-Type': 'application/json'
};
@@ -98,6 +99,7 @@ function addApiToGateway(gwInfo, spaceGuid, swaggerApi,
apiId) {
*/
function deleteApiFromGateway(gwInfo, spaceGuid, apiId) {
var options = {
+ followAllRedirects: true,
url:
gwInfo.gwUrl+'/'+encodeURIComponent(spaceGuid)+'/apis/'+encodeURIComponent(apiId),
agentOptions: {rejectUnauthorized: false},
headers: {
@@ -150,6 +152,7 @@ function getApis(gwInfo, spaceGuid, bpOrApiName) {
}
}
var options = {
+ followAllRedirects: true,
url: gwInfo.gwUrl+'/'+encodeURIComponent(spaceGuid)+'/apis',
headers: {
'Accept': 'application/json'
diff --git a/core/routemgmt/common/utils.js b/core/routemgmt/common/utils.js
index 2f626b0..6e3c3a7 100644
--- a/core/routemgmt/common/utils.js
+++ b/core/routemgmt/common/utils.js
@@ -37,6 +37,7 @@ var utils2 = require('./apigw-utils.js');
function createTenant(gwInfo, namespace, tenantInstance) {
var instance = tenantInstance || 'openwhisk'; // Default to a fixed
instance so all openwhisk tenants have a common instance
var options = {
+ followAllRedirects: true,
url: gwInfo.gwUrl+'/tenants',
headers: {
'Accept': 'application/json'
@@ -93,6 +94,7 @@ function getTenants(gwInfo, ns, tenantInstance) {
var qs = qsNsOnly;
if (tenantInstance) qs = qsNsAndInstance;
var options = {
+ followAllRedirects: true,
url: gwInfo.gwUrl+'/tenants',
qs: qs,
headers: {
@@ -174,6 +176,7 @@ function addApiToGateway(gwInfo, tenantId, swaggerApi,
gwApiId) {
gwApi.tenantId = tenantId;
var options = {
+ followAllRedirects: true,
url: gwInfo.gwUrl+'/apis',
headers: {
'Accept': 'application/json'
@@ -232,6 +235,7 @@ function addApiToGateway(gwInfo, tenantId, swaggerApi,
gwApiId) {
*/
function deleteApiFromGateway(gwInfo, gwApiId) {
var options = {
+ followAllRedirects: true,
url: gwInfo.gwUrl+'/apis/'+gwApiId,
agentOptions: {rejectUnauthorized: false},
headers: {
@@ -285,6 +289,7 @@ function getApis(gwInfo, tenantId, bpOrApiName) {
}
}
var options = {
+ followAllRedirects: true,
url: gwInfo.gwUrl+'/tenants/'+tenantId+'/apis',
headers: {
'Accept': 'application/json'
diff --git a/core/routemgmt/createApi/createApi.js
b/core/routemgmt/createApi/createApi.js
index 4a72dcb..e71b50a 100644
--- a/core/routemgmt/createApi/createApi.js
+++ b/core/routemgmt/createApi/createApi.js
@@ -66,6 +66,7 @@ function main(message) {
};
// Replace the CLI provided namespace values with the controller provided
namespace value
+ // If __ow_user is not set, the namespace values are left alone
if (message.accesstoken) {
utils2.updateNamespace(message.apidoc, message.__ow_user);
} else {
@@ -234,12 +235,12 @@ function validateArgs(message) {
return 'Internal error. A message parameter was not supplied.';
}
- if (!message.gwUrl) {
+ if (!message.gwUrl && !message.gwUrlV2) {
return 'gwUrl is required.';
}
if (!message.__ow_user) {
- return '__ow_user is required.';
+ return 'A valid auth key is required.';
}
if(!message.apidoc) {
diff --git a/core/routemgmt/deleteApi/deleteApi.js
b/core/routemgmt/deleteApi/deleteApi.js
index b29d95a..4fd4c5e 100644
--- a/core/routemgmt/deleteApi/deleteApi.js
+++ b/core/routemgmt/deleteApi/deleteApi.js
@@ -15,37 +15,39 @@
* limitations under the License.
*/
- /**
- * Delete an API Gateway to action mapping document from the database:
- * https://docs.cloudant.com/document.html#delete
- *
- * Parameters (all as fields in the message JSON object)
- * gwUrlV2 Required when accesstoken is provided. The V2 API
Gateway base path (i.e. http://gw.com)
- * gwUrl Required. The API Gateway base path (i.e.
http://gw.com)
- * gwUser Optional. The API Gateway authentication
- * gwPwd Optional. The API Gateway authentication
- * namespace Required if __ow_user not specified. Namespace of
API author
- * __ow_user Required. Namespace of API author
- * accesstoken Optional. Dynamic API GW auth. Overrides
gwUser/gwPwd
- * spaceguid Optional. Namespace unique id.
- * tenantInstance Optional. Instance identifier used when creating
the specific API GW Tenant
- * basepath Required. Base path or API name of the API
- * relpath Optional. Delete just this relative path from the
API. Required if operation is specified
- * operation Optional. Delete just this relpath's operation from
the API.
- *
- * NOTE: The package containing this action will be bound to the following
values:
- * gwUrl, gwAuth
- * As such, the caller to this action should normally avoid explicitly
setting
- * these values
- */
+/**
+ *
+ * Delete an API Gateway to action mapping document from the database:
+ * https://docs.cloudant.com/document.html#delete
+ *
+ * Parameters (all as fields in the message JSON object)
+ * gwUrlV2 Required when accesstoken is provided. The V2 API
Gateway base path (i.e. http://gw.com)
+ * gwUrl Required. The API Gateway base path (i.e.
http://gw.com)
+ * gwUser Optional. The API Gateway authentication
+ * gwPwd Optional. The API Gateway authentication
+ * __ow_user Optional. Set to the authenticated API authors's
namespace when valid authentication is supplied.
+ * namespace Required if __ow_user not specified. Namespace of
API author
+ * accesstoken Optional. Dynamic API GW auth. Overrides
gwUser/gwPwd
+ * spaceguid Optional. Namespace unique id.
+ * tenantInstance Optional. Instance identifier used when creating the
specific API GW Tenant
+ * basepath Required. Base path or API name of the API
+ * relpath Optional. Delete just this relative path from the
API. Required if operation is specified
+ * operation Optional. Delete just this relpath's operation from
the API.
+ *
+ * NOTE: The package containing this action will be bound to the following
values:
+ * gwUrl, gwAuth
+ * As such, the caller to this action should normally avoid explicitly
setting
+ * these values
+ **/
var utils = require('./utils.js');
var utils2 = require('./apigw-utils.js');
var _ = require('lodash');
function main(message) {
+ //console.log('message: '+JSON.stringify(message)); // ONLY FOR
TEMPORARY/LOCAL DEBUG; DON'T ENABLE PERMANENTLY
var badArgMsg = validateArgs(message);
if (badArgMsg) {
- return Promise.reject(utils2.makeErrorResponseObject(badArgMsg),
(message.__ow_method != undefined));
+ return Promise.reject(utils2.makeErrorResponseObject(badArgMsg,
(message.__ow_method != undefined)));
}
var gwInfo = {
@@ -211,12 +213,12 @@ function validateArgs(message) {
return 'Internal error. A message parameter was not supplied.';
}
- if (!message.gwUrl) {
+ if (!message.gwUrl && !message.gwUrlV2) {
return 'gwUrl is required.';
}
- if (!message.__ow_user) {
- return '__ow_user is required.';
+ if (!message.__ow_user && !message.namespace) {
+ return 'Invalid authentication.';
}
if (!message.basepath) {
diff --git a/core/routemgmt/getApi/getApi.js b/core/routemgmt/getApi/getApi.js
index 53ffc4e..04766ed 100644
--- a/core/routemgmt/getApi/getApi.js
+++ b/core/routemgmt/getApi/getApi.js
@@ -15,33 +15,33 @@
* limitations under the License.
*/
- /**
- * Retrieve API configuration from the API Gateway:
- *
- * Parameters (all as fields in the message JSON object)
- * gwUrlV2 Required when accesstoken is provided. The V2 API
Gateway base path (i.e. http://gw.com)
- * gwUrl Required. The API Gateway base path (i.e.
http://gw.com)
- * gwUser Optional. The API Gateway authentication
- * gwPwd Optional. The API Gateway authentication
- * namespace Optional. Input value is now overwritten by
__ow_user. Namespace of API author
- * __ow_user Required. Namespace of API author
- * tenantInstance Optional. Instance identifier used when creating
the specific API GW Tenant
- * accesstoken Optional. Dynamic API GW auth. Overrides
gwUser/gwPwd
- * spaceguid Optional. Namespace unique id.
- * basepath Optional. Base path or API name of the API.
- * If not provided, all APIs for the
namespace are returned
- * relpath Optional. Must be defined with 'operation'.
Filters API result to path/operation
- * operation Optional. Must be defined with 'relpath'. Filters
API result to path/operation
- * outputFormat Optional. Defaults to 'swagger'. Possible values:
- * 'apigw' = return API as obtained from the
API Gateway
- * 'swagger' = return API as swagger
compliant JSON
- *
- * NOTE: The package containing this action will be bound to the following
values:
- * gwUrl, gwAuth
- * As such, the caller to this action should normally avoid explicitly
setting
- * these values
- */
-var request = require('request');
+/**
+ *
+ * Retrieve API configuration from the API Gateway:
+ *
+ * Parameters (all as fields in the message JSON object)
+ * gwUrlV2 Required when accesstoken is provided. The V2 API
Gateway base path (i.e. http://gw.com)
+ * gwUrl Required. The API Gateway base path (i.e.
http://gw.com)
+ * gwUser Optional. The API Gateway authentication
+ * gwPwd Optional. The API Gateway authentication
+ * __ow_user Optional. Set to the authenticated API authors's
namespace when valid authentication is supplied.
+ * namespace Required if __ow_user not specified. Namespace of
API author
+ * tenantInstance Optional. Instance identifier used when creating the
specific API GW Tenant
+ * accesstoken Optional. Dynamic API GW auth. Overrides
gwUser/gwPwd
+ * spaceguid Optional. Namespace unique id.
+ * basepath Optional. Base path or API name of the API.
+ * If not provided, all APIs for the
namespace are returned
+ * relpath Optional. Must be defined with 'operation'. Filters
API result to path/operation
+ * operation Optional. Must be defined with 'relpath'. Filters
API result to path/operation
+ * outputFormat Optional. Defaults to 'swagger'. Possible values:
+ * 'apigw' = return API as obtained from the
API Gateway
+ * 'swagger' = return API as swagger
compliant JSON
+ *
+ * NOTE: The package containing this action will be bound to the following
values:
+ * gwUrl, gwAuth
+ * As such, the caller to this action should normally avoid explicitly
setting
+ * these values
+ **/
var utils = require('./utils.js');
var utils2 = require('./apigw-utils.js');
@@ -154,12 +154,12 @@ function validateArgs(message) {
return 'Internal error. A message parameter was not supplied.';
}
- if (!message.__ow_user) {
- return '__ow_user is required.';
+ if (!message.gwUrl && !message.gwUrlV2) {
+ return 'gwUrl is required.';
}
- if (!message.gwUrl) {
- return 'gwUrl is required.';
+ if (!message.__ow_user && !message.namespace) {
+ return 'Invalid authentication.';
}
if (message.outputFormat && !(message.outputFormat.toLowerCase() === 'apigw'
|| message.outputFormat.toLowerCase() === 'swagger')) {
diff --git
a/tests/src/test/scala/whisk/core/apigw/actions/test/ApiGwRoutemgmtActionTests.scala
b/tests/src/test/scala/whisk/core/apigw/actions/test/ApiGwRoutemgmtActionTests.scala
index 5e8b5dd..5a0f97c 100644
---
a/tests/src/test/scala/whisk/core/apigw/actions/test/ApiGwRoutemgmtActionTests.scala
+++
b/tests/src/test/scala/whisk/core/apigw/actions/test/ApiGwRoutemgmtActionTests.scala
@@ -427,10 +427,10 @@ class ApiGwRoutemgmtActionTests
it should "reject routemgmt actions that are invoked with not enough
parameters" in {
val invalidArgs = Seq(
//getApi
- ("/whisk.system/routemgmt/getApi", ANY_ERROR_EXIT, "__ow_user is
required", Seq()),
+ ("/whisk.system/routemgmt/getApi", ANY_ERROR_EXIT, "Invalid
authentication.", Seq()),
//deleteApi
- ("/whisk.system/routemgmt/deleteApi", ANY_ERROR_EXIT, "__ow_user
is required", Seq("-p", "basepath", "/ApiGwRoutemgmtActionTests_bp")),
+ ("/whisk.system/routemgmt/deleteApi", ANY_ERROR_EXIT, "Invalid
authentication.", Seq("-p", "basepath", "/ApiGwRoutemgmtActionTests_bp")),
("/whisk.system/routemgmt/deleteApi", ANY_ERROR_EXIT, "basepath is
required", Seq("-p", "__ow_user", "_")),
("/whisk.system/routemgmt/deleteApi", ANY_ERROR_EXIT, "When
specifying an operation, the path is required",
Seq("-p", "__ow_user", "_", "-p", "basepath",
"/ApiGwRoutemgmtActionTests_bp", "-p", "operation", "get")),
@@ -560,10 +560,10 @@ class ApiGwRoutemgmtActionTests
it should "reject apimgmt actions that are invoked with not enough
parameters" in {
val invalidArgs = Seq(
//getApi
- ("/whisk.system/apimgmt/getApi", ANY_ERROR_EXIT, "__ow_user is
required", Seq()),
+ ("/whisk.system/apimgmt/getApi", ANY_ERROR_EXIT, "Invalid
authentication.", Seq()),
//deleteApi
- ("/whisk.system/apimgmt/deleteApi", ANY_ERROR_EXIT, "__ow_user is
required", Seq("-p", "basepath", "/ApiGwRoutemgmtActionTests_bp")),
+ ("/whisk.system/apimgmt/deleteApi", ANY_ERROR_EXIT, "Invalid
authentication.", Seq("-p", "basepath", "/ApiGwRoutemgmtActionTests_bp")),
("/whisk.system/apimgmt/deleteApi", ANY_ERROR_EXIT, "basepath is
required", Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN")),
("/whisk.system/apimgmt/deleteApi", ANY_ERROR_EXIT, "When
specifying an operation, the path is required",
Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p",
"basepath", "/ApiGwRoutemgmtActionTests_bp", "-p", "operation", "get")),
diff --git a/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
b/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
index 9c8e239..14970db 100644
--- a/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
@@ -43,7 +43,7 @@ class ApiGwTests
with WskTestHelpers
with BeforeAndAfterAll {
- implicit val wskprops = WskProps()
+ implicit var wskprops = WskProps()
val wsk = new Wsk
val (cliuser, clinamespace) = WskAdmin.getUser(wskprops.authKey)
@@ -1194,7 +1194,7 @@ class ApiGwTests
it should "reject creation of an API from invalid YAML formatted API
configuration file" in {
val testName = "CLI_APIGWTEST22"
- val testbasepath = "/bp"
+ val testbasepath = "/"+testName+"_bp"
val swaggerPath = TestUtils.getTestApiGwFilename(s"local.api.bad.yaml")
try {
var rr = apiCreate(swagger = Some(swaggerPath), expectedExitCode =
ANY_ERROR_EXIT)
@@ -1245,4 +1245,35 @@ class ApiGwTests
val deleteresult = apiDelete(basepathOrApiName = testbasepath,
expectedExitCode = DONTCARE_EXIT)
}
}
+
+ it should "reject creation of an API with invalid auth key" in {
+ val testName = "CLI_APIGWTEST24"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ val wskpropsBackup = wskprops
+ try {
+ // Create the action for the API.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file),
expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ // Set an invalid auth key
+ wskprops = WskProps(authKey = "bad-auth-key")
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname),
+ expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include("The supplied authentication is invalid")
+ } finally {
+ wskprops = wskpropsBackup
+ val finallydeleteActionResult = wsk.action.delete(name =
actionName, expectedExitCode = DONTCARE_EXIT)
+ var deleteresult = apiDelete(basepathOrApiName = testbasepath,
expectedExitCode = DONTCARE_EXIT)
+ }
+ }
}
diff --git a/tools/cli/go-whisk-cli/commands/action.go
b/tools/cli/go-whisk-cli/commands/action.go
index dccd42d..20a7926 100644
--- a/tools/cli/go-whisk-cli/commands/action.go
+++ b/tools/cli/go-whisk-cli/commands/action.go
@@ -857,9 +857,12 @@ func isWebAction(client *whisk.Client, qname
QualifiedName) error {
if err != nil {
whisk.Debug(whisk.DbgError, "client.Actions.Get(%s) error: %s\n",
fullActionName, err)
whisk.Debug(whisk.DbgError, "Unable to obtain action '%s' for web
action validation\n", fullActionName)
- err = errors.New(wski18n.T("API action '{{.name}}' does not exist",
map[string]interface{}{"name": fullActionName}))
+ errMsg := wski18n.T("Unable to get action '{{.name}}': {{.err}}",
+ map[string]interface{}{"name": fullActionName, "err": err})
+ err = whisk.MakeWskErrorFromWskError(errors.New(errMsg), err,
whisk.EXITCODE_ERR_NETWORK, whisk.DISPLAY_MSG,
+ whisk.NO_DISPLAY_USAGE)
} else {
- err = errors.New(wski18n.T("API action '{{.name}}' is not a web
action. Issue 'wsk action update {{.name}} --web true' to convert the action to
a web action.",
+ err = errors.New(wski18n.T("Action '{{.name}}' is not a web action.
Issue 'wsk action update {{.name}} --web true' to convert the action to a web
action.",
map[string]interface{}{"name": fullActionName}))
weVal := getValue(action.Annotations, "web-export")
if (weVal == nil) {
diff --git a/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json
b/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json
index 765f961..ec409ad 100644
--- a/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json
+++ b/tools/cli/go-whisk-cli/wski18n/resources/en_US.all.json
@@ -1384,12 +1384,8 @@
"translation": "Invalid argument '{{.arg}}' for --web flag. Valid input
consist of 'yes', 'true', 'raw', 'false', or 'no'."
},
{
- "id": "API action '{{.name}}' does not exist",
- "translation": "API action '{{.name}}' does not exist"
- },
- {
- "id": "API action '{{.name}}' is not a web action. Issue 'wsk action
update {{.name}} --web true' to convert the action to a web action.",
- "translation": "API action '{{.name}}' is not a web action. Issue 'wsk
action update {{.name}} --web true' to convert the action to a web action."
+ "id": "Action '{{.name}}' is not a web action. Issue 'wsk action update
{{.name}} --web true' to convert the action to a web action.",
+ "translation": "Action '{{.name}}' is not a web action. Issue 'wsk action
update {{.name}} --web true' to convert the action to a web action."
},
{
"id": "Invalid configuration. The x-openwhisk stanza is missing.",
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].