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 2017/08/11 01:42:16 UTC

[incubator-openwhisk] branch master updated: Allow FQN with 3 parts (namespace/package/action) to be input with/without leading slash in CLI (#2424)

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/incubator-openwhisk.git


The following commit(s) were added to refs/heads/master by this push:
     new c2c44c3  Allow FQN with 3 parts (namespace/package/action) to be input with/without leading slash in CLI (#2424)
c2c44c3 is described below

commit c2c44c3c45c46d42c4c8042c0288e094607dbd68
Author: Brandon Lee Underwood <Br...@ibm.com>
AuthorDate: Thu Aug 10 21:42:13 2017 -0400

    Allow FQN with 3 parts (namespace/package/action) to be input with/without leading slash in CLI (#2424)
---
 docs/reference.md                                  |  19 +-
 tests/src/test/scala/common/Wsk.scala              |   2 +-
 .../test/scala/system/basic/WskActionTests.scala   |   1 -
 .../whisk/core/cli/test/WskBasicUsageTests.scala   |  29 +++
 tools/cli/go-whisk-cli/commands/action.go          | 108 +++++------
 tools/cli/go-whisk-cli/commands/activation.go      |  10 +-
 tools/cli/go-whisk-cli/commands/api.go             |  23 ++-
 tools/cli/go-whisk-cli/commands/namespace.go       |  12 +-
 tools/cli/go-whisk-cli/commands/package.go         | 109 +++++------
 tools/cli/go-whisk-cli/commands/qualified_name.go  | 215 +++++++++++++++++++++
 tools/cli/go-whisk-cli/commands/rule.go            |  82 ++++----
 tools/cli/go-whisk-cli/commands/shared.go          |  13 --
 tools/cli/go-whisk-cli/commands/trigger.go         | 108 +++++------
 tools/cli/go-whisk-cli/commands/util.go            | 122 ------------
 14 files changed, 482 insertions(+), 371 deletions(-)

diff --git a/docs/reference.md b/docs/reference.md
index e5ec838..783b06e 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -19,7 +19,10 @@ You can create your own namespaces if you're entitled to do so. The `/whisk.syst
 ### Fully qualified names
 
 The fully qualified name of an entity is
-`/namespaceName[/packageName]/entityName`. Notice that `/` is used to delimit namespaces, packages, and entities. Also, namespaces must be prefixed with a `/`.
+`/namespaceName[/packageName]/entityName`. Notice that `/` is used to delimit namespaces, packages, and entities.
+
+If the fully qualified name has three parts:
+`/namespaceName/packageName/entityName`, then the namespace can be entered without a prefixed `/`; otherwise, namespaces must be prefixed with a `/`.
 
 For convenience, the namespace can be left off if it is the user's *default namespace*.
 
@@ -219,23 +222,23 @@ The following packages are available to be used in the Node.js 6.9.1 environment
 - [glob v7.1.1](https://www.npmjs.com/package/glob) - Match files using the patterns the shell uses, like stars and stuff.
 - [gm v1.23.0](https://www.npmjs.com/package/gm) - GraphicsMagick and ImageMagick for Node.
 - [lodash v4.17.2](https://www.npmjs.com/package/lodash) - The Lodash library exported as Node.js modules.
-- [log4js v0.6.38](https://www.npmjs.com/package/log4js) - This is a conversion of the log4js framework to work with Node. 
+- [log4js v0.6.38](https://www.npmjs.com/package/log4js) - This is a conversion of the log4js framework to work with Node.
 - [iconv-lite v0.4.15](https://www.npmjs.com/package/iconv-lite) - Pure JS character encoding conversion
 - [marked v0.3.6](https://www.npmjs.com/package/marked) - A full-featured markdown parser and compiler, written in JavaScript. Built for speed.
-- [merge v1.2.0](https://www.npmjs.com/package/merge) - Merge multiple objects into one, optionally creating a new cloned object. 
+- [merge v1.2.0](https://www.npmjs.com/package/merge) - Merge multiple objects into one, optionally creating a new cloned object.
 - [moment v2.17.0](https://www.npmjs.com/package/moment) - A lightweight JavaScript date library for parsing, validating, manipulating, and formatting dates.
 - [mongodb v2.2.11](https://www.npmjs.com/package/mongodb) - The official MongoDB driver for Node.js.
 - [mustache v2.3.0](https://www.npmjs.com/package/mustache) - mustache.js is an implementation of the mustache template system in JavaScript.
 - [nano v6.2.0](https://www.npmjs.com/package/nano) - minimalistic couchdb driver for Node.js.
-- [node-uuid v1.4.7](https://www.npmjs.com/package/node-uuid) - Deprecated UUID packaged. 
+- [node-uuid v1.4.7](https://www.npmjs.com/package/node-uuid) - Deprecated UUID packaged.
 - [nodemailer v2.6.4](https://www.npmjs.com/package/nodemailer) - Send e-mails from Node.js – easy as cake!
 - [oauth2-server v2.4.1](https://www.npmjs.com/package/oauth2-server) - Complete, compliant and well tested module for implementing an OAuth2 Server/Provider with express in Node.js.
 - [openwhisk v3.3.2](https://www.npmjs.com/package/openwhisk) - JavaScript client library for the OpenWhisk platform. Provides a wrapper around the OpenWhisk APIs.
 - [pkgcloud v1.4.0](https://www.npmjs.com/package/pkgcloud) - pkgcloud is a standard library for Node.js that abstracts away differences among multiple cloud providers.
 - [process v0.11.9](https://www.npmjs.com/package/process) - require('process'); just like any other module.
 - [pug v2.0.0-beta6](https://www.npmjs.com/package/pug) - Implements the Pug templating language.
-- [redis v2.6.3](https://www.npmjs.com/package/redis) - This is a complete and feature rich Redis client for Node.js. 
-- [request v2.79.0](https://www.npmjs.com/package/request) - Request is designed to be the simplest way possible to make HTTP calls. 
+- [redis v2.6.3](https://www.npmjs.com/package/redis) - This is a complete and feature rich Redis client for Node.js.
+- [request v2.79.0](https://www.npmjs.com/package/request) - Request is designed to be the simplest way possible to make HTTP calls.
 - [request-promise v4.1.1](https://www.npmjs.com/package/request-promise) - The simplified HTTP request client 'request' with Promise support. Powered by Bluebird.
 - [rimraf v2.5.4](https://www.npmjs.com/package/rimraf) - The UNIX command rm -rf for node.
 - [semver v5.3.0](https://www.npmjs.com/package/semver) - Supports semantic versioning.
@@ -251,10 +254,10 @@ The following packages are available to be used in the Node.js 6.9.1 environment
 - [uuid v3.0.0](https://www.npmjs.com/package/uuid) - Simple, fast generation of RFC4122 UUIDS.
 - [validator v6.1.0](https://www.npmjs.com/package/validator) - A library of string validators and sanitizers.
 - [watson-developer-cloud v2.29.0](https://www.npmjs.com/package/watson-developer-cloud) - Node.js client library to use the Watson Developer Cloud services, a collection of APIs that use cognitive computing to solve complex problems.
-- [when v3.7.7](https://www.npmjs.com/package/when) - When.js is a rock solid, battle-tested Promises/A+ and when() implementation, including a complete ES6 Promise shim. 
+- [when v3.7.7](https://www.npmjs.com/package/when) - When.js is a rock solid, battle-tested Promises/A+ and when() implementation, including a complete ES6 Promise shim.
 - [winston v2.3.0](https://www.npmjs.com/package/winston) - A multi-transport async logging library for node.js. "CHILL WINSTON! ... I put it in the logs."
 - [ws v1.1.1](https://www.npmjs.com/package/ws) - ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and server implementation.
-- [xml2js v0.4.17](https://www.npmjs.com/package/xml2js) - Simple XML to JavaScript object converter. It supports bi-directional conversion. 
+- [xml2js v0.4.17](https://www.npmjs.com/package/xml2js) - Simple XML to JavaScript object converter. It supports bi-directional conversion.
 - [xmlhttprequest v1.8.0](https://www.npmjs.com/package/xmlhttprequest) - node-XMLHttpRequest is a wrapper for the built-in http client to emulate the browser XMLHttpRequest object.
 - [yauzl v2.7.0](https://www.npmjs.com/package/yauzl) - yet another unzip library for node. For zipping.
 
diff --git a/tests/src/test/scala/common/Wsk.scala b/tests/src/test/scala/common/Wsk.scala
index 540857b..79a1054 100644
--- a/tests/src/test/scala/common/Wsk.scala
+++ b/tests/src/test/scala/common/Wsk.scala
@@ -111,7 +111,7 @@ trait FullyQualifiedNames {
      */
     def fqn(name: String)(implicit wp: WskProps) = {
         val sep = "/" // Namespace.PATHSEP
-        if (name.startsWith(sep)) name
+        if (name.startsWith(sep) || name.count(_ == sep(0)) == 2) name
         else s"$sep${wp.namespace}$sep$name"
     }
 
diff --git a/tests/src/test/scala/system/basic/WskActionTests.scala b/tests/src/test/scala/system/basic/WskActionTests.scala
index 05a1c83..90e92b9 100644
--- a/tests/src/test/scala/system/basic/WskActionTests.scala
+++ b/tests/src/test/scala/system/basic/WskActionTests.scala
@@ -355,5 +355,4 @@ class WskActionTests
                     activation.logs.get.mkString(" ") should include(s"hello $utf8")
             }
     }
-
 }
diff --git a/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala b/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
index 59ede45..c451bbd 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
@@ -91,6 +91,35 @@ class WskBasicUsageTests
         result.stderr should include regex ("""(?i)Run 'wsk --help' for usage""")
     }
 
+    it should "allow a 3 part Fully Qualified Name (FQN) without a leading '/'" in withAssetCleaner(wskprops) {
+        (wp, assetHelper) =>
+            val guestNamespace = wsk.namespace.whois()
+            val packageName = "packageName3ptFQN"
+            val actionName = "actionName3ptFQN"
+            val triggerName = "triggerName3ptFQN"
+            val ruleName = "ruleName3ptFQN"
+            val fullQualifiedName = s"${guestNamespace}/${packageName}/${actionName}"
+            // Used for action and rule creation below
+            assetHelper.withCleaner(wsk.pkg, packageName) {
+                (pkg, _) => pkg.create(packageName)
+            }
+            assetHelper.withCleaner(wsk.trigger, triggerName) {
+                (trigger, _) => trigger.create(triggerName)
+            }
+            // Test action and rule creation where action name is 3 part FQN w/out leading slash
+            assetHelper.withCleaner(wsk.action, fullQualifiedName) {
+                (action, _) => action.create(fullQualifiedName, defaultAction)
+            }
+            assetHelper.withCleaner(wsk.rule, ruleName) {
+                (rule, _) =>
+                    rule.create(ruleName, trigger = triggerName, action = fullQualifiedName)
+            }
+
+            wsk.action.invoke(fullQualifiedName).stdout should include(s"ok: invoked /$fullQualifiedName")
+            wsk.action.get(fullQualifiedName).stdout should include(s"ok: got action ${packageName}/${actionName}")
+    }
+
+
     behavior of "Wsk actions"
 
     it should "reject creating entities with invalid names" in withAssetCleaner(wskprops) {
diff --git a/tools/cli/go-whisk-cli/commands/action.go b/tools/cli/go-whisk-cli/commands/action.go
index 33fd5ab..3008a57 100644
--- a/tools/cli/go-whisk-cli/commands/action.go
+++ b/tools/cli/go-whisk-cli/commands/action.go
@@ -121,7 +121,7 @@ var actionInvokeCmd = &cobra.Command{
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
         var parameters interface{}
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
         var paramArgs []string
 
         if whiskErr := checkArgs(
@@ -133,11 +133,11 @@ var actionInvokeCmd = &cobra.Command{
                 return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
+        client.Namespace = qualifiedName.GetNamespace()
         paramArgs = flags.common.param
 
         if len(paramArgs) > 0 {
@@ -148,12 +148,12 @@ var actionInvokeCmd = &cobra.Command{
         if flags.action.result {flags.common.blocking = true}
 
         res, _, err := client.Actions.Invoke(
-            qualifiedName.entityName,
+            qualifiedName.GetEntityName(),
             parameters,
             flags.common.blocking,
             flags.action.result)
 
-        return handleInvocationResponse(qualifiedName, parameters, res, err)
+        return handleInvocationResponse(*qualifiedName, parameters, res, err)
     },
 }
 
@@ -164,29 +164,29 @@ func handleInvocationResponse(
     err error) (error) {
         if err == nil {
             printInvocationMsg(
-                qualifiedName.namespace,
-                qualifiedName.entityName,
+                qualifiedName.GetNamespace(),
+                qualifiedName.GetEntityName(),
                 getValueFromJSONResponse(ACTIVATION_ID, result),
                 result,
                 color.Output)
         } else {
             if !flags.common.blocking {
-                return handleInvocationError(err, qualifiedName.entityName, parameters)
+                return handleInvocationError(err, qualifiedName.GetEntityName(), parameters)
             } else {
                 if isBlockingTimeout(err) {
                     printBlockingTimeoutMsg(
-                        qualifiedName.namespace,
-                        qualifiedName.entityName,
+                        qualifiedName.GetNamespace(),
+                        qualifiedName.GetEntityName(),
                         getValueFromJSONResponse(ACTIVATION_ID, result))
                 } else if isApplicationError(err) {
                     printInvocationMsg(
-                        qualifiedName.namespace,
-                        qualifiedName.entityName,
+                        qualifiedName.GetNamespace(),
+                        qualifiedName.GetEntityName(),
                         getValueFromJSONResponse(ACTIVATION_ID, result),
                         result,
                         colorable.NewColorableStderr())
                 } else {
-                    return handleInvocationError(err, qualifiedName.entityName, parameters)
+                    return handleInvocationError(err, qualifiedName.GetEntityName(), parameters)
                 }
             }
         }
@@ -204,7 +204,7 @@ var actionGetCmd = &cobra.Command{
         var err error
         var field string
         var action *whisk.Action
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 2, "Action get", wski18n.T("An action name is required.")); whiskErr != nil {
             return whiskErr
@@ -218,29 +218,29 @@ var actionGetCmd = &cobra.Command{
             }
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
+        client.Namespace = qualifiedName.GetNamespace()
 
-        if action, _, err = client.Actions.Get(qualifiedName.entityName); err != nil {
-            return actionGetError(qualifiedName.entityName, err)
+        if action, _, err = client.Actions.Get(qualifiedName.GetEntityName()); err != nil {
+            return actionGetError(qualifiedName.GetEntityName(), err)
         }
 
         if flags.action.url {
             actionURL := action.ActionURL(Properties.APIHost,
                 DefaultOpenWhiskApiPath,
                 Properties.APIVersion,
-                qualifiedName.packageName)
-            printActionGetWithURL(qualifiedName.entity, actionURL)
+                qualifiedName.GetPackageName())
+            printActionGetWithURL(qualifiedName.GetEntity(), actionURL)
         } else if flags.common.summary {
             printSummary(action)
         } else {
             if len(field) > 0 {
-                printActionGetWithField(qualifiedName.entityName, field, action)
+                printActionGetWithField(qualifiedName.GetEntityName(), field, action)
             } else {
-                printActionGet(qualifiedName.entityName, action)
+                printActionGet(qualifiedName.GetEntityName(), action)
             }
         }
 
@@ -255,7 +255,7 @@ var actionDeleteCmd = &cobra.Command{
     SilenceErrors: true,
     PreRunE:       setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
         var err error
 
         if whiskErr := checkArgs(
@@ -267,17 +267,17 @@ var actionDeleteCmd = &cobra.Command{
                 return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
+        client.Namespace = qualifiedName.GetNamespace()
 
-        if _, err = client.Actions.Delete(qualifiedName.entityName); err != nil {
-            return actionDeleteError(qualifiedName.entityName, err)
+        if _, err = client.Actions.Delete(qualifiedName.GetEntityName()); err != nil {
+            return actionDeleteError(qualifiedName.GetEntityName(), err)
         }
 
-        printActionDeleted(qualifiedName.entityName)
+        printActionDeleted(qualifiedName.GetEntityName())
 
         return nil
     },
@@ -290,7 +290,7 @@ var actionListCmd = &cobra.Command{
     SilenceErrors: true,
     PreRunE:       setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
         var actions []whisk.Action
         var err error
 
@@ -304,11 +304,11 @@ var actionListCmd = &cobra.Command{
         }
 
         if len(args) == 1 {
-            if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-                return parseQualifiedNameError(args[0], err)
+            if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+                return NewQualifiedNameError(args[0], err)
             }
 
-            client.Namespace = qualifiedName.namespace
+            client.Namespace = qualifiedName.GetNamespace()
         }
 
         options := &whisk.ActionListOptions{
@@ -316,8 +316,8 @@ var actionListCmd = &cobra.Command{
             Limit: flags.common.limit,
         }
 
-        if actions, _, err = client.Actions.List(qualifiedName.entityName, options); err != nil {
-            return actionListError(qualifiedName.entityName, options, err)
+        if actions, _, err = client.Actions.List(qualifiedName.GetEntityName(), options); err != nil {
+            return actionListError(qualifiedName.GetEntityName(), options, err)
         }
 
         printList(actions)
@@ -334,16 +334,16 @@ func parseAction(cmd *cobra.Command, args []string, update bool) (*whisk.Action,
     var parameters interface{}
     var annotations interface{}
 
-    qualifiedName := QualifiedName{}
+    var qualifiedName = new(QualifiedName)
 
-    if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-        return nil, parseQualifiedNameError(args[0], err)
+    if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+        return nil, NewQualifiedNameError(args[0], err)
     }
 
-    client.Namespace = qualifiedName.namespace
+    client.Namespace = qualifiedName.GetNamespace()
     action := new(whisk.Action)
-    action.Name = qualifiedName.entityName
-    action.Namespace = qualifiedName.namespace
+    action.Name = qualifiedName.GetEntityName()
+    action.Namespace = qualifiedName.GetNamespace()
     action.Limits = getLimits(
         cmd.LocalFlags().Changed(MEMORY_FLAG),
         cmd.LocalFlags().Changed(LOG_SIZE_FLAG),
@@ -372,19 +372,19 @@ func parseAction(cmd *cobra.Command, args []string, update bool) (*whisk.Action,
     }
 
     if flags.action.copy {
-        copiedQualifiedName := QualifiedName{}
+        var copiedQualifiedName = new(QualifiedName)
 
-        if copiedQualifiedName, err = parseQualifiedName(args[1]); err != nil {
-            return nil, parseQualifiedNameError(args[1], err)
+        if copiedQualifiedName, err = NewQualifiedName(args[1]); err != nil {
+            return nil, NewQualifiedNameError(args[1], err)
         }
 
-        client.Namespace = copiedQualifiedName.namespace
+        client.Namespace = copiedQualifiedName.GetNamespace()
 
-        if existingAction, _, err = client.Actions.Get(copiedQualifiedName.entityName); err != nil {
-            return nil, actionGetError(copiedQualifiedName.entityName, err)
+        if existingAction, _, err = client.Actions.Get(copiedQualifiedName.GetEntityName()); err != nil {
+            return nil, actionGetError(copiedQualifiedName.GetEntityName(), err)
         }
 
-        client.Namespace = qualifiedName.namespace
+        client.Namespace = qualifiedName.GetNamespace()
         action.Exec = existingAction.Exec
         action.Parameters = append(action.Parameters, existingAction.Parameters...)
         action.Annotations = append(action.Annotations, existingAction.Annotations...)
@@ -407,7 +407,7 @@ func parseAction(cmd *cobra.Command, args []string, update bool) (*whisk.Action,
 
     if cmd.LocalFlags().Changed(WEB_FLAG) {
         preserveAnnotations := action.Annotations == nil
-        action.Annotations, err = webAction(flags.action.web, action.Annotations, qualifiedName.entityName, preserveAnnotations)
+        action.Annotations, err = webAction(flags.action.web, action.Annotations, qualifiedName.GetEntityName(), preserveAnnotations)
     }
 
     whisk.Debug(whisk.DbgInfo, "Parsed action struct: %#v\n", action)
@@ -874,10 +874,10 @@ func isWebAction(client *whisk.Client, qname QualifiedName) (error) {
     var err error = nil
 
     savedNs := client.Namespace
-    client.Namespace = qname.namespace
-    fullActionName := "/" + qname.namespace + "/" + qname.entityName
+    client.Namespace = qname.GetNamespace()
+    fullActionName := "/" + qname.GetNamespace() + "/" + qname.GetEntityName()
 
-    action, _, err := client.Actions.Get(qname.entityName)
+    action, _, err := client.Actions.Get(qname.GetEntityName())
 
     if err != nil {
         whisk.Debug(whisk.DbgError, "client.Actions.Get(%s) error: %s\n", fullActionName, err)
diff --git a/tools/cli/go-whisk-cli/commands/activation.go b/tools/cli/go-whisk-cli/commands/activation.go
index 868f216..4ae62fe 100644
--- a/tools/cli/go-whisk-cli/commands/activation.go
+++ b/tools/cli/go-whisk-cli/commands/activation.go
@@ -52,7 +52,7 @@ var activationListCmd = &cobra.Command{
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 0, 1, "Activation list",
             wski18n.T("An optional namespace is the only valid argument.")); whiskErr != nil {
@@ -63,15 +63,15 @@ var activationListCmd = &cobra.Command{
         if len(args) == 1 {
             whisk.Debug(whisk.DbgInfo, "Activation item name filter '%s' provided\n", args[0])
 
-            if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-                return parseQualifiedNameError(args[0], err)
+            if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+                return NewQualifiedNameError(args[0], err)
             }
 
-            client.Namespace = qualifiedName.namespace
+            client.Namespace = qualifiedName.GetNamespace()
         }
 
         options := &whisk.ActivationListOptions{
-            Name:  qualifiedName.entityName,
+            Name:  qualifiedName.GetEntityName(),
             Limit: flags.common.limit,
             Skip:  flags.common.skip,
             Upto:  flags.activation.upto,
diff --git a/tools/cli/go-whisk-cli/commands/api.go b/tools/cli/go-whisk-cli/commands/api.go
index 95b5c16..b4c4306 100644
--- a/tools/cli/go-whisk-cli/commands/api.go
+++ b/tools/cli/go-whisk-cli/commands/api.go
@@ -128,7 +128,7 @@ var apiCreateCmd = &cobra.Command{
     RunE: func(cmd *cobra.Command, args []string) error {
         var api *whisk.Api
         var err error
-        var qname *QualifiedName
+        var qname = new(QualifiedName)
 
         if (len(args) == 0 && flags.api.configfile == "") {
             whisk.Debug(whisk.DbgError, "No swagger file and no arguments\n")
@@ -746,19 +746,18 @@ func parseApi(cmd *cobra.Command, args []string) (*whisk.Api, *QualifiedName, er
     }
 
     // Is the specified action name valid?
-    var qName QualifiedName
+    var qName = new(QualifiedName)
     if (len(args) == 3) {
-        qName = QualifiedName{}
-        qName, err = parseQualifiedName(args[2])
+        qName, err = NewQualifiedName(args[2])
         if err != nil {
-            whisk.Debug(whisk.DbgError, "parseQualifiedName(%s) failed: %s\n", args[2], err)
+            whisk.Debug(whisk.DbgError, "NewQualifiedName(%s) failed: %s\n", args[2], err)
             errMsg := wski18n.T("'{{.name}}' is not a valid action name: {{.err}}",
                 map[string]interface{}{"name": args[2], "err": err})
             whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXIT_CODE_ERR_GENERAL,
                 whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
             return nil, nil, whiskErr
         }
-        if (qName.entityName == "") {
+        if (qName.GetEntityName() == "") {
             whisk.Debug(whisk.DbgError, "Action name '%s' is invalid\n", args[2])
             errMsg := wski18n.T("'{{.name}}' is not a valid action name.", map[string]interface{}{"name": args[2]})
             whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXIT_CODE_ERR_GENERAL,
@@ -782,22 +781,22 @@ func parseApi(cmd *cobra.Command, args []string) (*whisk.Api, *QualifiedName, er
     api.Namespace = client.Config.Namespace
     api.Action = new(whisk.ApiAction)
     var urlActionPackage string
-    if (len(qName.packageName) > 0) {
-        urlActionPackage = qName.packageName
+    if (len(qName.GetPackageName()) > 0) {
+        urlActionPackage = qName.GetPackageName()
     } else {
         urlActionPackage = "default"
     }
-    api.Action.BackendUrl = "https://" + client.Config.Host + "/api/v1/web/" + qName.namespace + "/" + urlActionPackage + "/" + qName.entity + ".http"
+    api.Action.BackendUrl = "https://" + client.Config.Host + "/api/v1/web/" + qName.GetNamespace() + "/" + urlActionPackage + "/" + qName.GetEntity() + ".http"
     api.Action.BackendMethod = api.GatewayMethod
-    api.Action.Name = qName.entityName
-    api.Action.Namespace = qName.namespace
+    api.Action.Name = qName.GetEntityName()
+    api.Action.Namespace = qName.GetNamespace()
     api.Action.Auth = client.Config.AuthToken
     api.ApiName = apiname
     api.GatewayBasePath = basepath
     if (!basepathArgIsApiName) { api.Id = "API:"+api.Namespace+":"+api.GatewayBasePath }
 
     whisk.Debug(whisk.DbgInfo, "Parsed api struct: %#v\n", api)
-    return api, &qName, nil
+    return api, qName, nil
 }
 
 func parseSwaggerApi() (*whisk.Api, error) {
diff --git a/tools/cli/go-whisk-cli/commands/namespace.go b/tools/cli/go-whisk-cli/commands/namespace.go
index cee13ba..f132913 100644
--- a/tools/cli/go-whisk-cli/commands/namespace.go
+++ b/tools/cli/go-whisk-cli/commands/namespace.go
@@ -67,7 +67,7 @@ var namespaceGetCmd = &cobra.Command{
     SilenceErrors:  true,
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
         var err error
 
         if whiskErr := checkArgs(args, 0, 1, "Namespace get",
@@ -77,16 +77,16 @@ var namespaceGetCmd = &cobra.Command{
 
         // Namespace argument is optional; defaults to configured property namespace
         if len(args) == 1 {
-            if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-                return parseQualifiedNameError(args[0], err)
+            if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+                return NewQualifiedNameError(args[0], err)
             }
 
-            if len(qualifiedName.entityName) > 0 {
-                return entityNameError(qualifiedName.entityName)
+            if len(qualifiedName.GetEntityName()) > 0 {
+                return entityNameError(qualifiedName.GetEntityName())
             }
         }
 
-        namespace, _, err := client.Namespaces.Get(qualifiedName.namespace)
+        namespace, _, err := client.Namespaces.Get(qualifiedName.GetNamespace())
 
         if err != nil {
             whisk.Debug(whisk.DbgError, "client.Namespaces.Get(%s) error: %s\n", getClientNamespace(), err)
diff --git a/tools/cli/go-whisk-cli/commands/package.go b/tools/cli/go-whisk-cli/commands/package.go
index 7680fcf..6721f34 100644
--- a/tools/cli/go-whisk-cli/commands/package.go
+++ b/tools/cli/go-whisk-cli/commands/package.go
@@ -42,7 +42,8 @@ var packageBindCmd = &cobra.Command{
   PreRunE:       setupClientConfig,
   RunE: func(cmd *cobra.Command, args []string) error {
     var err error
-    var pkgQualifiedName, bindQualifiedName QualifiedName
+    var pkgQualifiedName = new(QualifiedName)
+    var bindQualifiedName = new(QualifiedName)
 
     if whiskErr := checkArgs(args, 2, 2, "Package bind",
             wski18n.T("A package name and binding name are required.")); whiskErr != nil {
@@ -50,16 +51,16 @@ var packageBindCmd = &cobra.Command{
     }
 
     packageName := args[0]
-    if pkgQualifiedName, err = parseQualifiedName(packageName); err != nil {
-      return parseQualifiedNameError(packageName, err)
+    if pkgQualifiedName, err = NewQualifiedName(packageName); err != nil {
+      return NewQualifiedNameError(packageName, err)
     }
 
     bindingName := args[1]
-    if bindQualifiedName, err = parseQualifiedName(bindingName); err != nil {
-      return parseQualifiedNameError(bindingName, err)
+    if bindQualifiedName, err = NewQualifiedName(bindingName); err != nil {
+      return NewQualifiedNameError(bindingName, err)
     }
 
-    client.Namespace = bindQualifiedName.namespace
+    client.Namespace = bindQualifiedName.GetNamespace()
 
     // Convert the binding's list of default parameters from a string into []KeyValue
     // The 1 or more --param arguments have all been combined into a single []string
@@ -91,12 +92,12 @@ var packageBindCmd = &cobra.Command{
     }
 
     binding := whisk.Binding{
-      Name:      pkgQualifiedName.entityName,
-      Namespace: pkgQualifiedName.namespace,
+      Name:      pkgQualifiedName.GetEntityName(),
+      Namespace: pkgQualifiedName.GetNamespace(),
     }
 
     p := &whisk.BindingPackage{
-      Name:        bindQualifiedName.entityName,
+      Name:        bindQualifiedName.GetEntityName(),
       Annotations: annotations.(whisk.KeyValueArr),
       Parameters:  parameters.(whisk.KeyValueArr),
       Binding:     binding,
@@ -125,17 +126,17 @@ var packageCreateCmd = &cobra.Command{
   RunE: func(cmd *cobra.Command, args []string) error {
     var err error
     var shared, sharedSet bool
-    var qualifiedName QualifiedName
+    var qualifiedName = new(QualifiedName)
 
     if whiskErr := checkArgs(args, 1, 1, "Package create", wski18n.T("A package name is required.")); whiskErr != nil {
       return whiskErr
     }
 
-    if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-      return parseQualifiedNameError(args[0], err)
+    if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+      return NewQualifiedNameError(args[0], err)
     }
 
-    client.Namespace = qualifiedName.namespace
+    client.Namespace = qualifiedName.GetNamespace()
 
     if shared, sharedSet, err = parseShared(flags.common.shared); err != nil {
       whisk.Debug(whisk.DbgError, "parseShared(%s) failed: %s\n", flags.common.shared, err)
@@ -165,8 +166,8 @@ var packageCreateCmd = &cobra.Command{
     }
 
     p := &whisk.Package{
-      Name:        qualifiedName.entityName,
-      Namespace:   qualifiedName.namespace,
+      Name:        qualifiedName.GetEntityName(),
+      Namespace:   qualifiedName.GetNamespace(),
       Annotations: annotations.(whisk.KeyValueArr),
       Parameters:  parameters.(whisk.KeyValueArr),
     }
@@ -181,7 +182,7 @@ var packageCreateCmd = &cobra.Command{
       errStr := wski18n.T(
         "Unable to create package '{{.name}}': {{.err}}",
         map[string]interface{}{
-          "name": qualifiedName.entityName,
+          "name": qualifiedName.GetEntityName(),
           "err": err,
         })
       werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
@@ -189,7 +190,7 @@ var packageCreateCmd = &cobra.Command{
     }
 
     fmt.Fprintf(color.Output, wski18n.T("{{.ok}} created package {{.name}}\n",
-      map[string]interface{}{"ok": color.GreenString(wski18n.T("ok:")), "name":boldString(qualifiedName.entityName)}))
+      map[string]interface{}{"ok": color.GreenString(wski18n.T("ok:")), "name":boldString(qualifiedName.GetEntityName())}))
     return nil
   },
 }
@@ -203,17 +204,17 @@ var packageUpdateCmd = &cobra.Command{
   RunE: func(cmd *cobra.Command, args []string) error {
     var err error
     var shared, sharedSet bool
-    var qualifiedName QualifiedName
+    var qualifiedName = new(QualifiedName)
 
     if whiskErr := checkArgs(args, 1, 1, "Package update", wski18n.T("A package name is required.")); whiskErr != nil {
       return whiskErr
     }
 
-    if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-      return parseQualifiedNameError(args[0], err)
+    if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+      return NewQualifiedNameError(args[0], err)
     }
 
-    client.Namespace = qualifiedName.namespace
+    client.Namespace = qualifiedName.GetNamespace()
 
     if shared, sharedSet, err = parseShared(flags.common.shared); err != nil {
       whisk.Debug(whisk.DbgError, "parseShared(%s) failed: %s\n", flags.common.shared, err)
@@ -242,8 +243,8 @@ var packageUpdateCmd = &cobra.Command{
     }
 
     p := &whisk.Package{
-      Name:        qualifiedName.entityName,
-      Namespace:   qualifiedName.namespace,
+      Name:        qualifiedName.GetEntityName(),
+      Namespace:   qualifiedName.GetNamespace(),
       Annotations: annotations.(whisk.KeyValueArr),
       Parameters:  parameters.(whisk.KeyValueArr),
     }
@@ -261,7 +262,7 @@ var packageUpdateCmd = &cobra.Command{
     }
 
     fmt.Fprintf(color.Output, wski18n.T("{{.ok}} updated package {{.name}}\n",
-      map[string]interface{}{"ok": color.GreenString(wski18n.T("ok:")), "name":boldString(qualifiedName.entityName)}))
+      map[string]interface{}{"ok": color.GreenString(wski18n.T("ok:")), "name":boldString(qualifiedName.GetEntityName())}))
     return nil
   },
 }
@@ -275,7 +276,7 @@ var packageGetCmd = &cobra.Command{
   RunE: func(cmd *cobra.Command, args []string) error {
     var err error
     var field string
-    var qualifiedName QualifiedName
+    var qualifiedName = new(QualifiedName)
 
     if whiskErr := checkArgs(args, 1, 2, "Package get", wski18n.T("A package name is required.")); whiskErr != nil {
       return whiskErr
@@ -292,18 +293,18 @@ var packageGetCmd = &cobra.Command{
       }
     }
 
-    if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-      return parseQualifiedNameError(args[0], err)
+    if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+      return NewQualifiedNameError(args[0], err)
     }
-    client.Namespace = qualifiedName.namespace
+    client.Namespace = qualifiedName.GetNamespace()
 
-    xPackage, _, err := client.Packages.Get(qualifiedName.entityName)
+    xPackage, _, err := client.Packages.Get(qualifiedName.GetEntityName())
     if err != nil {
-      whisk.Debug(whisk.DbgError, "client.Packages.Get(%s) failed: %s\n", qualifiedName.entityName, err)
+      whisk.Debug(whisk.DbgError, "client.Packages.Get(%s) failed: %s\n", qualifiedName.GetEntityName(), err)
       errStr := wski18n.T(
         "Unable to get package '{{.name}}': {{.err}}",
         map[string]interface{}{
-          "name": qualifiedName.entityName,
+          "name": qualifiedName.GetEntityName(),
           "err":err,
         })
       werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
@@ -316,12 +317,12 @@ var packageGetCmd = &cobra.Command{
 
       if len(field) > 0 {
         fmt.Fprintf(color.Output, wski18n.T("{{.ok}} got package {{.name}}, displaying field {{.field}}\n",
-          map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.entityName),
+          map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.GetEntityName()),
           "field": boldString(field)}))
         printField(xPackage, field)
       } else {
         fmt.Fprintf(color.Output, wski18n.T("{{.ok}} got package {{.name}}\n",
-          map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.entityName)}))
+          map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.GetEntityName())}))
         printJSON(xPackage)
       }
     }
@@ -338,25 +339,25 @@ var packageDeleteCmd = &cobra.Command{
   PreRunE:       setupClientConfig,
   RunE: func(cmd *cobra.Command, args []string) error {
     var err error
-    var qualifiedName QualifiedName
+    var qualifiedName = new(QualifiedName)
 
     if whiskErr := checkArgs(args, 1, 1, "Package delete", wski18n.T("A package name is required.")); whiskErr != nil {
       return whiskErr
     }
 
-    if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-      return parseQualifiedNameError(args[0], err)
+    if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+      return NewQualifiedNameError(args[0], err)
     }
 
-    client.Namespace = qualifiedName.namespace
+    client.Namespace = qualifiedName.GetNamespace()
 
-    _, err = client.Packages.Delete(qualifiedName.entityName)
+    _, err = client.Packages.Delete(qualifiedName.GetEntityName())
     if err != nil {
-      whisk.Debug(whisk.DbgError, "client.Packages.Delete(%s) failed: %s\n", qualifiedName.entityName, err)
+      whisk.Debug(whisk.DbgError, "client.Packages.Delete(%s) failed: %s\n", qualifiedName.GetEntityName(), err)
       errStr := wski18n.T(
         "Unable to delete package '{{.name}}': {{.err}}",
         map[string]interface{}{
-          "name": qualifiedName.entityName,
+          "name": qualifiedName.GetEntityName(),
           "err": err,
         })
       werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
@@ -365,7 +366,7 @@ var packageDeleteCmd = &cobra.Command{
 
     fmt.Fprintf(color.Output,
       wski18n.T("{{.ok}} deleted package {{.name}}\n",
-        map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.entityName)}))
+        map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.GetEntityName())}))
     return nil
   },
 }
@@ -378,7 +379,7 @@ var packageListCmd = &cobra.Command{
   PreRunE:       setupClientConfig,
   RunE: func(cmd *cobra.Command, args []string) error {
     var err error
-    var qualifiedName QualifiedName
+    var qualifiedName = new(QualifiedName)
 
     if whiskErr := checkArgs(args, 0, 1, "Package list",
       wski18n.T("An optional namespace is the only valid argument.")); whiskErr != nil {
@@ -386,15 +387,15 @@ var packageListCmd = &cobra.Command{
     }
 
     if len(args) == 1 {
-      if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-        return parseQualifiedNameError(args[0], err)
+      if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+        return NewQualifiedNameError(args[0], err)
       }
 
-      if len(qualifiedName.entityName) > 0 {
-        return entityNameError(qualifiedName.entityName)
+      if len(qualifiedName.GetEntityName()) > 0 {
+        return entityNameError(qualifiedName.GetEntityName())
       }
 
-      client.Namespace = qualifiedName.namespace
+      client.Namespace = qualifiedName.GetNamespace()
     }
 
     options := &whisk.PackageListOptions{
@@ -425,7 +426,7 @@ var packageRefreshCmd = &cobra.Command{
   PreRunE:       setupClientConfig,
   RunE: func(cmd *cobra.Command, args []string) error {
     var err error
-    var qualifiedName QualifiedName
+    var qualifiedName = new(QualifiedName)
 
     if whiskErr := checkArgs(args, 0, 1, "Package refresh",
         wski18n.T("An optional namespace is the only valid argument.")); whiskErr != nil {
@@ -433,19 +434,19 @@ var packageRefreshCmd = &cobra.Command{
     }
 
     if len(args) == 0 {
-      qualifiedName.namespace = getNamespace()
+      qualifiedName.namespace = getNamespaceFromProp()
     } else {
-      if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-        return parseQualifiedNameError(args[0], err)
+      if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+        return NewQualifiedNameError(args[0], err)
       }
 
-      if len(qualifiedName.entityName) > 0 {
-        return entityNameError(qualifiedName.entityName)
+      if len(qualifiedName.GetEntityName()) > 0 {
+        return entityNameError(qualifiedName.GetEntityName())
       }
     }
 
     currentNamespace := client.Config.Namespace
-    client.Config.Namespace = qualifiedName.namespace
+    client.Config.Namespace = qualifiedName.GetNamespace()
 
     defer func() {
       client.Config.Namespace = currentNamespace
diff --git a/tools/cli/go-whisk-cli/commands/qualified_name.go b/tools/cli/go-whisk-cli/commands/qualified_name.go
new file mode 100644
index 0000000..61512f6
--- /dev/null
+++ b/tools/cli/go-whisk-cli/commands/qualified_name.go
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2015-2016 IBM Corporation
+ *
+ * Licensed 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.
+ */
+
+package commands
+
+import (
+    "errors"
+    "fmt"
+    "strings"
+    "../../go-whisk/whisk"
+    "../wski18n"
+)
+
+type QualifiedName struct {
+    namespace   string  // namespace. does not include leading '/'.  may be "" (i.e. default namespace)
+    packageName string  // package.  may be "".  does not include leading/trailing '/'
+    entity      string  // entity.  should not be ""
+    entityName  string  // pkg+entity
+}
+
+///////////////////////////
+// QualifiedName Methods //
+///////////////////////////
+
+//  GetFullQualifiedName() returns a full qualified name in proper string format
+//      from qualifiedName with proper syntax.
+//  Example: /namespace/[package/]entity
+func (qualifiedName *QualifiedName) GetFullQualifiedName() string {
+    output := []string{}
+
+    if len(qualifiedName.GetNamespace()) > 0 {
+        output = append(output, "/", qualifiedName.GetNamespace(), "/")
+    }
+    if len(qualifiedName.GetPackageName()) > 0 {
+        output = append(output, qualifiedName.GetPackageName(), "/")
+    }
+    output = append(output, qualifiedName.GetEntity())
+
+    return strings.Join(output, "")
+}
+
+//  GetPackageName() returns the package name from qualifiedName without a
+//      leading '/'
+func (qualifiedName *QualifiedName) GetPackageName() string {
+    return qualifiedName.packageName
+}
+
+//  GetEntityName() returns the entity name ([package/]entity) of qualifiedName
+//      without a leading '/'
+func (qualifiedName *QualifiedName) GetEntityName() string {
+    return qualifiedName.entityName
+}
+
+//  GetEntity() returns the name of entity in qualifiedName without a leading '/'
+func (qualifiedName *QualifiedName) GetEntity() string {
+    return qualifiedName.entity
+}
+
+//  GetNamespace() returns the name of the namespace in qualifiedName without
+//      a leading '/'
+func (qualifiedName *QualifiedName) GetNamespace() string {
+    return qualifiedName.namespace
+}
+
+//  NewQualifiedName(name) initializes and constructs a (possibly fully qualified)
+//      QualifiedName struct.
+//
+//      NOTE: If the given qualified name is None, then this is a default qualified
+//          name and it is resolved from properties.
+//      NOTE: If the namespace is missing from the qualified name, the namespace
+//          is also resolved from the property file.
+//
+//  Examples:
+//      foo => qualifiedName {namespace: "_", entityName: foo}
+//      pkg/foo => qualifiedName {namespace: "_", entityName: pkg/foo}
+//      /ns/foo => qualifiedName {namespace: ns, entityName: foo}
+//      /ns/pkg/foo => qualifiedName {namespace: ns, entityName: pkg/foo}
+func NewQualifiedName(name string) (*QualifiedName, error) {
+    qualifiedName := new(QualifiedName)
+
+    // If name has a preceding delimiter (/), or if it has two delimiters with a
+    // leading non-empty string, then it contains a namespace. Otherwise the name
+    // does not specify a namespace, so default the namespace to the namespace
+    // value set in the properties file; if that is not set, use "_"
+    name = addLeadSlash(name)
+    parts := strings.Split(name, "/")
+    if  strings.HasPrefix(name, "/")  {
+        qualifiedName.namespace = parts[1]
+
+        if len(parts) < 2 || len(parts) > 4 {
+            return qualifiedName, qualifiedNameNotSpecifiedErr()
+        }
+
+        for i := 1; i < len(parts); i++ {
+            if len(parts[i]) == 0 || parts[i] == "." {
+                return qualifiedName, qualifiedNameNotSpecifiedErr()
+            }
+        }
+
+        qualifiedName.entityName = strings.Join(parts[2:], "/")
+        if len(parts) == 4 {
+            qualifiedName.packageName = parts[2]
+        }
+        qualifiedName.entity = parts[len(parts)-1]
+    } else {
+        if len(name) == 0 || name == "." {
+            return qualifiedName, qualifiedNameNotSpecifiedErr()
+        }
+
+        qualifiedName.entity = parts[len(parts)-1]
+        if len(parts) == 2 {
+            qualifiedName.packageName = parts[0]
+        }
+        qualifiedName.entityName = name
+        qualifiedName.namespace = getNamespaceFromProp()
+    }
+
+    whisk.Debug(whisk.DbgInfo, "Qualified pkg+entity (EntityName): %s\n", qualifiedName.GetEntityName())
+    whisk.Debug(whisk.DbgInfo, "Qualified namespace: %s\n", qualifiedName.GetNamespace())
+    whisk.Debug(whisk.DbgInfo, "Qualified package: %s\n", qualifiedName.GetPackageName())
+    whisk.Debug(whisk.DbgInfo, "Qualified entity: %s\n", qualifiedName.GetEntity())
+
+    return qualifiedName, nil
+}
+
+/////////////////////
+// Error Functions //
+/////////////////////
+
+//  qualifiedNameNotSpecifiedErr() returns generic whisk error for
+//      invalid qualified names detected while building a new
+//      QualifiedName struct.
+func qualifiedNameNotSpecifiedErr() error {
+    whisk.Debug(whisk.DbgError, "A valid qualified name was not detected\n")
+    errStr := wski18n.T("A valid qualified name must be specified.")
+    return whisk.MakeWskError(errors.New(errStr), whisk.NOT_ALLOWED, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
+}
+
+//  NewQualifiedNameError(entityName, err) returns specific whisk error
+//      for invalid qualified names.
+func NewQualifiedNameError(entityName string, err error) (error) {
+    whisk.Debug(whisk.DbgError, "NewQualifiedName(%s) failed: %s\n", entityName, err)
+
+    errMsg := wski18n.T(
+        "'{{.name}}' is not a valid qualified name: {{.err}}",
+        map[string]interface{}{
+            "name": entityName,
+            "err": err,
+        })
+
+    return whisk.MakeWskError(errors.New(errMsg), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
+}
+
+///////////////////////////
+// Helper/Misc Functions //
+///////////////////////////
+
+//  addLeadSlash(name) returns a (possibly fully qualified) resource name,
+//      inserting a leading '/' if it is of 3 parts (namespace/package/action)
+//      and lacking the leading '/'.
+func addLeadSlash(name string) string {
+    parts := strings.Split(name, "/")
+    if len(parts) == 3 && parts[0] != "" {
+        name = "/" + name
+    }
+    return name
+}
+
+//  getNamespaceFromProp() returns a namespace from Properties if one exists,
+//      else defaults to returning "_"
+func getNamespaceFromProp() (string) {
+    namespace := "_"
+
+    if Properties.Namespace != "" {
+        namespace = Properties.Namespace
+    }
+
+    return namespace
+}
+
+//  getQualifiedName(name, namespace) returns a fully qualified name given a
+//      (possibly fully qualified) resource name and optional namespace.
+//
+//  Examples:
+//      (foo, None) => /_/foo
+//      (pkg/foo, None) => /_/pkg/foo
+//      (foo, ns) => /ns/foo
+//      (/ns/pkg/foo, None) => /ns/pkg/foo
+//      (/ns/pkg/foo, otherns) => /ns/pkg/foo
+func getQualifiedName(name string, namespace string) (string) {
+    name = addLeadSlash(name)
+    if strings.HasPrefix(name, "/") {
+        return name
+    } else if strings.HasPrefix(namespace, "/")  {
+        return fmt.Sprintf("%s/%s", namespace, name)
+    } else {
+        if len(namespace) == 0 {
+            namespace = Properties.Namespace
+        }
+        return fmt.Sprintf("/%s/%s", namespace, name)
+    }
+}
diff --git a/tools/cli/go-whisk-cli/commands/rule.go b/tools/cli/go-whisk-cli/commands/rule.go
index 996c368..47607cd 100644
--- a/tools/cli/go-whisk-cli/commands/rule.go
+++ b/tools/cli/go-whisk-cli/commands/rule.go
@@ -42,18 +42,18 @@ var ruleEnableCmd = &cobra.Command{
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 1, "Rule enable", wski18n.T("A rule name is required.")); whiskErr != nil {
             return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
-        ruleName := qualifiedName.entityName
+        client.Namespace = qualifiedName.GetNamespace()
+        ruleName := qualifiedName.GetEntityName()
 
         _, _, err = client.Rules.SetState(ruleName, "active")
         if err != nil {
@@ -79,18 +79,18 @@ var ruleDisableCmd = &cobra.Command{
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 1, "Rule disable", wski18n.T("A rule name is required.")); whiskErr != nil {
             return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
-        ruleName := qualifiedName.entityName
+        client.Namespace = qualifiedName.GetNamespace()
+        ruleName := qualifiedName.GetEntityName()
 
         _, _, err = client.Rules.SetState(ruleName, "inactive")
         if err != nil {
@@ -116,18 +116,18 @@ var ruleStatusCmd = &cobra.Command{
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 1, "Rule status", wski18n.T("A rule name is required.")); whiskErr != nil {
             return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
-        ruleName := qualifiedName.entityName
+        client.Namespace = qualifiedName.GetNamespace()
+        ruleName := qualifiedName.GetEntityName()
 
         rule, _, err := client.Rules.Get(ruleName)
         if err != nil {
@@ -153,19 +153,19 @@ var ruleCreateCmd = &cobra.Command{
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 3, 3, "Rule create",
                 wski18n.T("A rule, trigger and action name are required.")); whiskErr != nil {
             return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
-        ruleName := qualifiedName.entityName
+        client.Namespace = qualifiedName.GetNamespace()
+        ruleName := qualifiedName.GetEntityName()
         triggerName := getQualifiedName(args[1], Properties.Namespace)
         actionName := getQualifiedName(args[2], Properties.Namespace)
 
@@ -202,19 +202,19 @@ var ruleUpdateCmd = &cobra.Command{
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 3, 3, "Rule update",
                 wski18n.T("A rule, trigger and action name are required.")); whiskErr != nil {
             return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
-        ruleName := qualifiedName.entityName
+        client.Namespace = qualifiedName.GetNamespace()
+        ruleName := qualifiedName.GetEntityName()
         triggerName := getQualifiedName(args[1], Properties.Namespace)
         actionName := getQualifiedName(args[2], Properties.Namespace)
 
@@ -249,7 +249,7 @@ var ruleGetCmd = &cobra.Command{
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
         var field string
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 2, "Rule get", wski18n.T("A rule name is required.")); whiskErr != nil {
             return whiskErr
@@ -266,12 +266,12 @@ var ruleGetCmd = &cobra.Command{
             }
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
-        ruleName := qualifiedName.entityName
+        client.Namespace = qualifiedName.GetNamespace()
+        ruleName := qualifiedName.GetEntityName()
 
         rule, _, err := client.Rules.Get(ruleName)
         if err != nil {
@@ -309,18 +309,18 @@ var ruleDeleteCmd = &cobra.Command{
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 1, "Rule delete", wski18n.T("A rule name is required.")); whiskErr != nil {
             return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
-        ruleName := qualifiedName.entityName
+        client.Namespace = qualifiedName.GetNamespace()
+        ruleName := qualifiedName.GetEntityName()
 
         if flags.rule.disable {
             _, _, err := client.Rules.SetState(ruleName, "inactive")
@@ -357,7 +357,7 @@ var ruleListCmd = &cobra.Command{
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 0, 1, "Rule list",
             wski18n.T("An optional namespace is the only valid argument.")); whiskErr != nil {
@@ -365,15 +365,15 @@ var ruleListCmd = &cobra.Command{
         }
 
         if len(args) == 1 {
-            if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-                return parseQualifiedNameError(args[0], err)
+            if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+                return NewQualifiedNameError(args[0], err)
             }
 
-            if len(qualifiedName.entityName) > 0 {
-                return entityNameError(qualifiedName.entityName)
+            if len(qualifiedName.GetEntityName()) > 0 {
+                return entityNameError(qualifiedName.GetEntityName())
             }
 
-            client.Namespace = qualifiedName.namespace
+            client.Namespace = qualifiedName.GetNamespace()
         }
 
         ruleListOptions := &whisk.RuleListOptions{
diff --git a/tools/cli/go-whisk-cli/commands/shared.go b/tools/cli/go-whisk-cli/commands/shared.go
index ed8364d..77afb10 100644
--- a/tools/cli/go-whisk-cli/commands/shared.go
+++ b/tools/cli/go-whisk-cli/commands/shared.go
@@ -33,16 +33,3 @@ func entityNameError(entityName string) (error) {
 
     return whisk.MakeWskError(errors.New(errMsg), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
 }
-
-func parseQualifiedNameError(entityName string, err error) (error) {
-    whisk.Debug(whisk.DbgError, "parseQualifiedName(%s) failed: %s\n", entityName, err)
-
-    errMsg := wski18n.T(
-        "'{{.name}}' is not a valid qualified name: {{.err}}",
-        map[string]interface{}{
-            "name": entityName,
-            "err": err,
-        })
-
-    return whisk.MakeWskError(errors.New(errMsg), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
-}
diff --git a/tools/cli/go-whisk-cli/commands/trigger.go b/tools/cli/go-whisk-cli/commands/trigger.go
index 4f94451..b6f8b0b 100644
--- a/tools/cli/go-whisk-cli/commands/trigger.go
+++ b/tools/cli/go-whisk-cli/commands/trigger.go
@@ -49,18 +49,18 @@ var triggerFireCmd = &cobra.Command{
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
         var parameters interface{}
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 2, "Trigger fire",
                 wski18n.T("A trigger name is required. A payload is optional.")); whiskErr != nil {
             return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
+        client.Namespace = qualifiedName.GetNamespace()
 
         // Add payload to parameters
         if len(args) == 2 {
@@ -80,11 +80,11 @@ var triggerFireCmd = &cobra.Command{
             }
         }
 
-        trigResp, _, err := client.Triggers.Fire(qualifiedName.entityName, parameters)
+        trigResp, _, err := client.Triggers.Fire(qualifiedName.GetEntityName(), parameters)
         if err != nil {
-            whisk.Debug(whisk.DbgError, "client.Triggers.Fire(%s, %#v) failed: %s\n", qualifiedName.entityName, parameters, err)
+            whisk.Debug(whisk.DbgError, "client.Triggers.Fire(%s, %#v) failed: %s\n", qualifiedName.GetEntityName(), parameters, err)
             errStr := wski18n.T("Unable to fire trigger '{{.name}}': {{.err}}",
-                    map[string]interface{}{"name": qualifiedName.entityName, "err": err})
+                    map[string]interface{}{"name": qualifiedName.GetEntityName(), "err": err})
             werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL,
                 whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
             return werr
@@ -94,8 +94,8 @@ var triggerFireCmd = &cobra.Command{
             wski18n.T("{{.ok}} triggered /{{.namespace}}/{{.name}} with id {{.id}}\n",
                 map[string]interface{}{
                     "ok": color.GreenString("ok:"),
-                    "namespace": boldString(qualifiedName.namespace),
-                    "name": boldString(qualifiedName.entityName),
+                    "namespace": boldString(qualifiedName.GetNamespace()),
+                    "name": boldString(qualifiedName.GetEntityName()),
                     "id": boldString(trigResp.ActivationId)}))
         return nil
     },
@@ -111,31 +111,31 @@ var triggerCreateCmd = &cobra.Command{
         var err error
         var annotations interface{}
         var feedArgPassed bool = (flags.common.feed != "")
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 1, "Trigger create",
                 wski18n.T("A trigger name is required.")); whiskErr != nil {
             return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
+        client.Namespace = qualifiedName.GetNamespace()
 
         var fullTriggerName string
         var fullFeedName string
-        var feedQualifiedName QualifiedName
+        var feedQualifiedName = new(QualifiedName)
         if feedArgPassed {
             whisk.Debug(whisk.DbgInfo, "Trigger has a feed\n")
 
-            if feedQualifiedName, err = parseQualifiedName(flags.common.feed); err != nil {
-                return parseQualifiedNameError(flags.common.feed, err)
+            if feedQualifiedName, err = NewQualifiedName(flags.common.feed); err != nil {
+                return NewQualifiedNameError(flags.common.feed, err)
             }
 
-            fullFeedName = fmt.Sprintf("/%s/%s", feedQualifiedName.namespace, feedQualifiedName.entityName)
-            fullTriggerName = fmt.Sprintf("/%s/%s", qualifiedName.namespace, qualifiedName.entityName)
+            fullFeedName = fmt.Sprintf("/%s/%s", feedQualifiedName.GetNamespace(), feedQualifiedName.GetEntityName())
+            fullTriggerName = fmt.Sprintf("/%s/%s", qualifiedName.GetNamespace(), qualifiedName.GetEntityName())
             flags.common.param = append(flags.common.param, getFormattedJSON(FEED_LIFECYCLE_EVENT, FEED_CREATE))
             flags.common.param = append(flags.common.param, getFormattedJSON(FEED_TRIGGER_NAME, fullTriggerName))
             flags.common.param = append(flags.common.param, getFormattedJSON(FEED_AUTH_KEY, client.Config.AuthToken))
@@ -173,7 +173,7 @@ var triggerCreateCmd = &cobra.Command{
         }
 
         trigger := &whisk.Trigger{
-            Name:        qualifiedName.entityName,
+            Name:        qualifiedName.GetEntityName(),
             Annotations: annotations.(whisk.KeyValueArr),
         }
 
@@ -224,18 +224,18 @@ var triggerUpdateCmd = &cobra.Command{
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 1, "Trigger update",
                 wski18n.T("A trigger name is required.")); whiskErr != nil {
             return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
+        client.Namespace = qualifiedName.GetNamespace()
 
         // Convert the trigger's list of default parameters from a string into []KeyValue
         // The 1 or more --param arguments have all been combined into a single []string
@@ -264,7 +264,7 @@ var triggerUpdateCmd = &cobra.Command{
         }
 
         trigger := &whisk.Trigger{
-            Name:        qualifiedName.entityName,
+            Name:        qualifiedName.GetEntityName(),
             Parameters:  parameters.(whisk.KeyValueArr),
             Annotations: annotations.(whisk.KeyValueArr),
         }
@@ -294,7 +294,7 @@ var triggerGetCmd = &cobra.Command{
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
         var field string
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 2, "Trigger get", wski18n.T("A trigger name is required.")); whiskErr != nil {
             return whiskErr
@@ -311,17 +311,17 @@ var triggerGetCmd = &cobra.Command{
             }
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
+        client.Namespace = qualifiedName.GetNamespace()
 
-        retTrigger, _, err := client.Triggers.Get(qualifiedName.entityName)
+        retTrigger, _, err := client.Triggers.Get(qualifiedName.GetEntityName())
         if err != nil {
-            whisk.Debug(whisk.DbgError, "client.Triggers.Get(%s) failed: %s\n", qualifiedName.entityName, err)
+            whisk.Debug(whisk.DbgError, "client.Triggers.Get(%s) failed: %s\n", qualifiedName.GetEntityName(), err)
             errStr := wski18n.T("Unable to get trigger '{{.name}}': {{.err}}",
-                    map[string]interface{}{"name": qualifiedName.entityName, "err": err})
+                    map[string]interface{}{"name": qualifiedName.GetEntityName(), "err": err})
             werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
             return werr
         }
@@ -331,12 +331,12 @@ var triggerGetCmd = &cobra.Command{
         } else {
             if len(field) > 0 {
                 fmt.Fprintf(color.Output, wski18n.T("{{.ok}} got trigger {{.name}}, displaying field {{.field}}\n",
-                    map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.entityName),
+                    map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.GetEntityName()),
                     "field": boldString(field)}))
                 printField(retTrigger, field)
             } else {
                 fmt.Fprintf(color.Output, wski18n.T("{{.ok}} got trigger {{.name}}\n",
-                        map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.entityName)}))
+                        map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.GetEntityName())}))
                 printJSON(retTrigger)
             }
         }
@@ -356,24 +356,24 @@ var triggerDeleteCmd = &cobra.Command{
         var retTrigger *whisk.Trigger
         var fullFeedName string
         var origParams []string
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 1, 1, "Trigger delete",
                 wski18n.T("A trigger name is required.")); whiskErr != nil {
             return whiskErr
         }
 
-        if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-            return parseQualifiedNameError(args[0], err)
+        if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+            return NewQualifiedNameError(args[0], err)
         }
 
-        client.Namespace = qualifiedName.namespace
+        client.Namespace = qualifiedName.GetNamespace()
 
-        retTrigger, _, err = client.Triggers.Get(qualifiedName.entityName)
+        retTrigger, _, err = client.Triggers.Get(qualifiedName.GetEntityName())
         if err != nil {
-            whisk.Debug(whisk.DbgError, "client.Triggers.Get(%s) failed: %s\n", qualifiedName.entityName, err)
+            whisk.Debug(whisk.DbgError, "client.Triggers.Get(%s) failed: %s\n", qualifiedName.GetEntityName(), err)
             errStr := wski18n.T("Unable to get trigger '{{.name}}': {{.err}}",
-                    map[string]interface{}{"name": qualifiedName.entityName, "err": err})
+                    map[string]interface{}{"name": qualifiedName.GetEntityName(), "err": err})
             werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
             return werr
         }
@@ -384,34 +384,34 @@ var triggerDeleteCmd = &cobra.Command{
 
             if len(fullFeedName) > 0 {
                 origParams = flags.common.param
-                fullTriggerName := fmt.Sprintf("/%s/%s", qualifiedName.namespace, qualifiedName.entityName)
+                fullTriggerName := fmt.Sprintf("/%s/%s", qualifiedName.GetNamespace(), qualifiedName.GetEntityName())
                 flags.common.param = append(flags.common.param, getFormattedJSON(FEED_LIFECYCLE_EVENT, FEED_DELETE))
                 flags.common.param = append(flags.common.param, getFormattedJSON(FEED_TRIGGER_NAME, fullTriggerName))
                 flags.common.param = append(flags.common.param, getFormattedJSON(FEED_AUTH_KEY, client.Config.AuthToken))
 
-                err = configureFeed(qualifiedName.entityName, fullFeedName)
+                err = configureFeed(qualifiedName.GetEntityName(), fullFeedName)
                 if err != nil {
-                    whisk.Debug(whisk.DbgError, "configureFeed(%s, %s) failed: %s\n", qualifiedName.entityName, fullFeedName, err)
+                    whisk.Debug(whisk.DbgError, "configureFeed(%s, %s) failed: %s\n", qualifiedName.GetEntityName(), fullFeedName, err)
                 }
 
                 flags.common.param = origParams
-                client.Namespace = qualifiedName.namespace
+                client.Namespace = qualifiedName.GetNamespace()
             }
 
         }
 
-        retTrigger, _, err = client.Triggers.Delete(qualifiedName.entityName)
+        retTrigger, _, err = client.Triggers.Delete(qualifiedName.GetEntityName())
         if err != nil {
-            whisk.Debug(whisk.DbgError, "client.Triggers.Delete(%s) failed: %s\n", qualifiedName.entityName, err)
+            whisk.Debug(whisk.DbgError, "client.Triggers.Delete(%s) failed: %s\n", qualifiedName.GetEntityName(), err)
             errStr := wski18n.T("Unable to delete trigger '{{.name}}': {{.err}}",
-                map[string]interface{}{"name": qualifiedName.entityName, "err": err})
+                map[string]interface{}{"name": qualifiedName.GetEntityName(), "err": err})
             werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
             return werr
         }
 
         fmt.Fprintf(color.Output,
             wski18n.T("{{.ok}} deleted trigger {{.name}}\n",
-                map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.entityName)}))
+                map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.GetEntityName())}))
 
         return nil
     },
@@ -425,7 +425,7 @@ var triggerListCmd = &cobra.Command{
     PreRunE: setupClientConfig,
     RunE: func(cmd *cobra.Command, args []string) error {
         var err error
-        var qualifiedName QualifiedName
+        var qualifiedName = new(QualifiedName)
 
         if whiskErr := checkArgs(args, 0, 1, "Trigger list",
             wski18n.T("An optional namespace is the only valid argument.")); whiskErr != nil {
@@ -433,15 +433,15 @@ var triggerListCmd = &cobra.Command{
         }
 
         if len(args) == 1 {
-            if qualifiedName, err = parseQualifiedName(args[0]); err != nil {
-                return parseQualifiedNameError(args[0], err)
+            if qualifiedName, err = NewQualifiedName(args[0]); err != nil {
+                return NewQualifiedNameError(args[0], err)
             }
 
-            if len(qualifiedName.entityName) > 0 {
-                return entityNameError(qualifiedName.entityName)
+            if len(qualifiedName.GetEntityName()) > 0 {
+                return entityNameError(qualifiedName.GetEntityName())
             }
 
-            client.Namespace = qualifiedName.namespace
+            client.Namespace = qualifiedName.GetNamespace()
         }
 
         options := &whisk.TriggerListOptions{
diff --git a/tools/cli/go-whisk-cli/commands/util.go b/tools/cli/go-whisk-cli/commands/util.go
index 3ef9ff0..afe7cbe 100644
--- a/tools/cli/go-whisk-cli/commands/util.go
+++ b/tools/cli/go-whisk-cli/commands/util.go
@@ -41,128 +41,6 @@ import (
     "bytes"
 )
 
-type QualifiedName struct {
-    namespace   string  // namespace. does not include leading '/'.  may be "" (i.e. default namespace)
-    packageName string  // package.  may be "".  does not include leading/trailing '/'
-    entity      string  // entity.  should not be ""
-    entityName  string  // pkg+entity
-}
-
-func (qualifiedName QualifiedName) String() string {
-    output := []string{}
-
-    if len(qualifiedName.namespace) > 0 {
-        output = append(output, "/", qualifiedName.namespace, "/")
-    }
-    if len(qualifiedName.packageName) > 0 {
-        output = append(output, qualifiedName.packageName, "/")
-    }
-    output = append(output, qualifiedName.entityName)
-
-    return strings.Join(output, "")
-}
-
-/*
-Parse a (possibly fully qualified) resource name into namespace and name components. If the given qualified name isNone,
-then this is a default qualified name and it is resolved from properties. If the namespace is missing from the qualified
-name, the namespace is also resolved from the property file.
-
-Return a qualifiedName struct
-
-Examples:
-      foo => qualifiedName {namespace: "_", entityName: foo}
-      pkg/foo => qualifiedName {namespace: "_", entityName: pkg/foo}
-      /ns/foo => qualifiedName {namespace: ns, entityName: foo}
-      /ns/pkg/foo => qualifiedName {namespace: ns, entityName: pkg/foo}
-*/
-func parseQualifiedName(name string) (QualifiedName, error) {
-    var qualifiedName QualifiedName
-
-    // If name has a preceding delimiter (/), it contains a namespace. Otherwise the name does not specify a namespace,
-    // so default the namespace to the namespace value set in the properties file; if that is not set, use "_"
-    if  strings.HasPrefix(name, "/")  {
-        parts := strings.Split(name, "/")
-        qualifiedName.namespace = parts[1]
-
-        if len(parts) < 2 || len(parts) > 4 {
-            whisk.Debug(whisk.DbgError, "A valid qualified name was not detected\n")
-            errStr := wski18n.T("A valid qualified name must be specified.")
-            err := whisk.MakeWskError(errors.New(errStr), whisk.NOT_ALLOWED, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
-            return qualifiedName, err
-        }
-
-        for i := 1; i < len(parts); i++ {
-            if len(parts[i]) == 0 || parts[i] == "." {
-                whisk.Debug(whisk.DbgError, "A valid qualified name was not detected\n")
-                errStr := wski18n.T("A valid qualified name must be specified.")
-                err := whisk.MakeWskError(errors.New(errStr), whisk.NOT_ALLOWED, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
-                return qualifiedName, err
-            }
-        }
-
-        qualifiedName.entityName = strings.Join(parts[2:], "/")
-        if len(parts) == 4 {
-            qualifiedName.packageName = parts[2]
-        }
-        qualifiedName.entity = parts[len(parts)-1]
-    } else {
-        if len(name) == 0 || name == "." {
-            whisk.Debug(whisk.DbgError, "A valid qualified name was not detected\n")
-            errStr := wski18n.T("A valid qualified name must be specified.")
-            err := whisk.MakeWskError(errors.New(errStr), whisk.NOT_ALLOWED, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
-            return qualifiedName, err
-        }
-
-        parts := strings.Split(name, "/")
-        qualifiedName.entity = parts[len(parts)-1]
-        if len(parts) == 2 {
-            qualifiedName.packageName = parts[0]
-        }
-        qualifiedName.entityName = name
-        qualifiedName.namespace = getNamespace()
-    }
-
-    whisk.Debug(whisk.DbgInfo, "Qualified pkg+entity (EntityName): %s\n", qualifiedName.entityName)
-    whisk.Debug(whisk.DbgInfo, "Qualified namespace: %s\n", qualifiedName.namespace)
-    whisk.Debug(whisk.DbgInfo, "Qualified package: %s\n", qualifiedName.packageName)
-    whisk.Debug(whisk.DbgInfo, "Qualified entity: %s\n", qualifiedName.entity)
-
-    return qualifiedName, nil
-}
-
-func getNamespace() (string) {
-    namespace := "_"
-
-    if Properties.Namespace != "" {
-        namespace = Properties.Namespace
-    }
-
-    return namespace
-}
-
-/*
-Return a fully qualified name given a (possibly fully qualified) resource name and optional namespace.
-
-Examples:
-      (foo, None) => /_/foo
-      (pkg/foo, None) => /_/pkg/foo
-      (foo, ns) => /ns/foo
-      (/ns/pkg/foo, None) => /ns/pkg/foo
-      (/ns/pkg/foo, otherns) => /ns/pkg/foo
-*/
-func getQualifiedName(name string, namespace string) (string) {
-    if strings.HasPrefix(name, "/") {
-        return name
-    } else if strings.HasPrefix(namespace, "/")  {
-        return fmt.Sprintf("%s/%s", namespace, name)
-    } else {
-        if len(namespace) == 0 {
-            namespace = Properties.Namespace
-        }
-        return fmt.Sprintf("/%s/%s", namespace, name)
-    }
-}
-
 func csvToQualifiedActions(artifacts string) ([]string) {
     var res []string
     actions := strings.Split(artifacts, ",")

-- 
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <co...@openwhisk.apache.org>'].