You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ra...@apache.org on 2020/09/08 17:18:01 UTC
[openwhisk] branch master updated: Force Delete Non-Empty Packages
(#4941)
This is an automated email from the ASF dual-hosted git repository.
rabbah pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk.git
The following commit(s) were added to refs/heads/master by this push:
new b70ea5a Force Delete Non-Empty Packages (#4941)
b70ea5a is described below
commit b70ea5a908dc086eab7828b8b0a8d5131705c82b
Author: Brendan Doyle <bj...@georgetown.edu>
AuthorDate: Tue Sep 8 10:17:42 2020 -0700
Force Delete Non-Empty Packages (#4941)
Co-authored-by: Brendan Doyle <br...@qualtrics.com>
---
.../src/main/resources/apiv1swagger.json | 11 ++++
.../openwhisk/core/controller/Packages.scala | 69 ++++++++++++++--------
docs/rest_api.md | 9 +++
.../core/controller/test/PackagesApiTests.scala | 26 +++++++-
4 files changed, 90 insertions(+), 25 deletions(-)
diff --git a/core/controller/src/main/resources/apiv1swagger.json b/core/controller/src/main/resources/apiv1swagger.json
index 9befc7c..390ca38 100644
--- a/core/controller/src/main/resources/apiv1swagger.json
+++ b/core/controller/src/main/resources/apiv1swagger.json
@@ -1440,6 +1440,17 @@
"description": "Name of package",
"required": true,
"type": "string"
+ },
+ {
+ "name": "force",
+ "in": "query",
+ "description": "Force delete a package if it contains entities removing everything in it. Default is false.",
+ "required": false,
+ "type": "string",
+ "enum": [
+ "true",
+ "false"
+ ]
}
],
"responses": {
diff --git a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Packages.scala b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Packages.scala
index ffc992f..c524e95 100644
--- a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Packages.scala
+++ b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Packages.scala
@@ -117,7 +117,8 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with ReferencedEntities {
}
/**
- * Deletes package/binding. If a package, may only be deleted if there are no entities in the package.
+ * Deletes package/binding. If a package, may only be deleted if there are no entities in the package
+ * or force parameter is set to true, which will delete all contents of the package before deleting.
*
* Responses are one of (Code, Message)
* - 200 WhiskPackage as JSON
@@ -126,29 +127,51 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with ReferencedEntities {
* - 500 Internal Server Error
*/
override def remove(user: Identity, entityName: FullyQualifiedEntityName)(implicit transid: TransactionId) = {
- deleteEntity(
- WhiskPackage,
- entityStore,
- entityName.toDocId,
- (wp: WhiskPackage) => {
- wp.binding map {
- // this is a binding, it is safe to remove
- _ =>
- Future.successful({})
- } getOrElse {
- // may only delete a package if all its ingredients are deleted already
- WhiskAction
- .listCollectionInNamespace(entityStore, wp.namespace.addPath(wp.name), skip = 0, limit = 0) flatMap {
- case Left(list) if (list.size != 0) =>
- Future failed {
- RejectRequest(
- Conflict,
- s"Package not empty (contains ${list.size} ${if (list.size == 1) "entity" else "entities"})")
- }
- case _ => Future.successful({})
+ parameter('force ? false) { force =>
+ deleteEntity(
+ WhiskPackage,
+ entityStore,
+ entityName.toDocId,
+ (wp: WhiskPackage) => {
+ wp.binding map {
+ // this is a binding, it is safe to remove
+ _ =>
+ Future.successful({})
+ } getOrElse {
+ // may only delete a package if all its ingredients are deleted already or force flag is set
+ WhiskAction
+ .listCollectionInNamespace(
+ entityStore,
+ wp.namespace.addPath(wp.name),
+ includeDocs = true,
+ skip = 0,
+ limit = 0) flatMap {
+ case Right(list) if list.nonEmpty && force =>
+ Future sequence {
+ list.map(action => {
+ WhiskAction.get(
+ entityStore,
+ wp.fullyQualifiedName(false)
+ .add(action.fullyQualifiedName(false).name)
+ .toDocId) flatMap { actionWithRevision =>
+ WhiskAction.del(entityStore, actionWithRevision.docinfo)
+ }
+ })
+ } flatMap { _ =>
+ Future.successful({})
+ }
+ case Right(list) if list.nonEmpty && !force =>
+ Future failed {
+ RejectRequest(
+ Conflict,
+ s"Package not empty (contains ${list.size} ${if (list.size == 1) "entity" else "entities"}). Set force param or delete package contents.")
+ }
+ case _ =>
+ Future.successful({})
+ }
}
- }
- })
+ })
+ }
}
/**
diff --git a/docs/rest_api.md b/docs/rest_api.md
index f7e7f4b..effbbd4 100644
--- a/docs/rest_api.md
+++ b/docs/rest_api.md
@@ -316,6 +316,15 @@ curl -u $AUTH https://$APIHOST/api/v1/namespaces/_/packages/iot?overwrite=true \
-d '{"namespace":"_","name":"iot"}'
```
+To force delete a package that contains entities, set the force parameter to true. Failure
+will return an error either for failure to delete an action within the package or the package itself.
+The package will not be attempted to be deleted until all actions are successfully deleted.
+
+```bash
+curl -u $AUTH https://$APIHOST/api/v1/namespaces/_/packages/iot?force=true \
+-X DELETE
+```
+
## Activations
To get the list of the last 3 activations use a HTTP request with a `GET` method, passing the query parameter `limit=3`
diff --git a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/PackagesApiTests.scala b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/PackagesApiTests.scala
index c52ba07..578e7ff 100644
--- a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/PackagesApiTests.scala
+++ b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/PackagesApiTests.scala
@@ -797,7 +797,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
}
}
- it should "reject delete non-empty package" in {
+ it should "delete package and its actions if force flag is set to true" in {
implicit val tid = transid()
val provider = WhiskPackage(namespace, aname())
val action = WhiskAction(provider.namespace.addPath(provider.name), aname(), jsDefault("??"))
@@ -811,10 +811,32 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
}
}
+ Delete(s"$collectionPath/${provider.name}?force=true") ~> Route.seal(routes(creds)) ~> check {
+ status should be(OK)
+ val response = responseAs[WhiskPackage]
+ response should be(provider)
+ }
+ }
+
+ it should "reject delete non-empty package if force flag is not set" in {
+ implicit val tid = transid()
+ val provider = WhiskPackage(namespace, aname())
+ val action = WhiskAction(provider.namespace.addPath(provider.name), aname(), jsDefault("??"))
+ put(entityStore, provider)
+ put(entityStore, action)
+ org.apache.openwhisk.utils.retry {
+ Get(s"$collectionPath/${provider.name}") ~> Route.seal(routes(creds)) ~> check {
+ status should be(OK)
+ val response = responseAs[JsObject]
+ response.fields("actions").asInstanceOf[JsArray].elements.length should be(1)
+ }
+ }
+
+ val exceptionString = "Package not empty (contains 1 entity). Set force param or delete package contents."
Delete(s"$collectionPath/${provider.name}") ~> Route.seal(routes(creds)) ~> check {
status should be(Conflict)
val response = responseAs[ErrorResponse]
- response.error should include("Package not empty (contains 1 entity)")
+ response.error should include(exceptionString)
response.code.id should not be empty
}
}