diff --git a/cmd/policy/namespaces.go b/cmd/policy/namespaces.go index 6269944b..79ccb4da 100644 --- a/cmd/policy/namespaces.go +++ b/cmd/policy/namespaces.go @@ -11,11 +11,7 @@ import ( "github.com/spf13/cobra" ) -var ( - NamespacesCmd = man.Docs.GetCommand("policy/attributes/namespaces") - - forceUnsafe bool -) +var forceUnsafe bool func getAttributeNamespace(cmd *cobra.Command, args []string) { c := cli.New(cmd, args) @@ -320,146 +316,176 @@ func policyRemoveKeyFromNamespace(cmd *cobra.Command, args []string) { common.HandleSuccess(cmd, namespace, t, nil) } -func initNamespacesCommands() { - getCmd := man.Docs.GetCommand("policy/attributes/namespaces/get", - man.WithRun(getAttributeNamespace), - ) +// newCommandFromDoc creates an independent cobra.Command with metadata copied from a Doc. +// This allows registering the same logical command under multiple parents. +func newCommandFromDoc(doc *man.Doc, run func(*cobra.Command, []string)) *cobra.Command { + cmd := &cobra.Command{ + Use: doc.Use, + Short: doc.Short, + Long: doc.Long, + Args: doc.Args, + Aliases: doc.Aliases, + Hidden: doc.Hidden, + Run: run, + } + return cmd +} + +// buildNamespacesCommandTree creates a full namespaces command tree with all subcommands and flags. +// Each call returns an independent *cobra.Command so it can be parented under multiple commands. +func buildNamespacesCommandTree() *cobra.Command { + nsDoc := man.Docs.GetDoc("policy/namespaces") + nsCmd := newCommandFromDoc(nsDoc, nil) + + getDoc := man.Docs.GetDoc("policy/namespaces/get") + getCmd := newCommandFromDoc(getDoc, getAttributeNamespace) getCmd.Flags().StringP( - getCmd.GetDocFlag("id").Name, - getCmd.GetDocFlag("id").Shorthand, - getCmd.GetDocFlag("id").Default, - getCmd.GetDocFlag("id").Description, + getDoc.GetDocFlag("id").Name, + getDoc.GetDocFlag("id").Shorthand, + getDoc.GetDocFlag("id").Default, + getDoc.GetDocFlag("id").Description, ) - listCmd := man.Docs.GetCommand("policy/attributes/namespaces/list", - man.WithRun(listAttributeNamespaces), - ) + listDoc := man.Docs.GetDoc("policy/namespaces/list") + listCmd := newCommandFromDoc(listDoc, listAttributeNamespaces) listCmd.Flags().StringP( - listCmd.GetDocFlag("state").Name, - listCmd.GetDocFlag("state").Shorthand, - listCmd.GetDocFlag("state").Default, - listCmd.GetDocFlag("state").Description, + listDoc.GetDocFlag("state").Name, + listDoc.GetDocFlag("state").Shorthand, + listDoc.GetDocFlag("state").Default, + listDoc.GetDocFlag("state").Description, ) - injectListPaginationFlags(listCmd) - - createDoc := man.Docs.GetCommand("policy/attributes/namespaces/create", - man.WithRun(createAttributeNamespace), + listCmd.Flags().Int32P( + listDoc.GetDocFlag("limit").Name, + listDoc.GetDocFlag("limit").Shorthand, + defaultListFlagLimit, + listDoc.GetDocFlag("limit").Description, + ) + listCmd.Flags().Int32P( + listDoc.GetDocFlag("offset").Name, + listDoc.GetDocFlag("offset").Shorthand, + defaultListFlagOffset, + listDoc.GetDocFlag("offset").Description, ) - createDoc.Flags().StringP( + + createDoc := man.Docs.GetDoc("policy/namespaces/create") + createCmd := newCommandFromDoc(createDoc, createAttributeNamespace) + createCmd.Flags().StringP( createDoc.GetDocFlag("name").Name, createDoc.GetDocFlag("name").Shorthand, createDoc.GetDocFlag("name").Default, createDoc.GetDocFlag("name").Description, ) - injectLabelFlags(&createDoc.Command, false) + injectLabelFlags(createCmd, false) - updateCmd := man.Docs.GetCommand("policy/attributes/namespaces/update", - man.WithRun(updateAttributeNamespace), - ) + updateDoc := man.Docs.GetDoc("policy/namespaces/update") + updateCmd := newCommandFromDoc(updateDoc, updateAttributeNamespace) updateCmd.Flags().StringP( - updateCmd.GetDocFlag("id").Name, - updateCmd.GetDocFlag("id").Shorthand, - updateCmd.GetDocFlag("id").Default, - updateCmd.GetDocFlag("id").Description, + updateDoc.GetDocFlag("id").Name, + updateDoc.GetDocFlag("id").Shorthand, + updateDoc.GetDocFlag("id").Default, + updateDoc.GetDocFlag("id").Description, ) - injectLabelFlags(&updateCmd.Command, true) + injectLabelFlags(updateCmd, true) - deactivateCmd := man.Docs.GetCommand("policy/attributes/namespaces/deactivate", - man.WithRun(deactivateAttributeNamespace), - ) + deactivateDoc := man.Docs.GetDoc("policy/namespaces/deactivate") + deactivateCmd := newCommandFromDoc(deactivateDoc, deactivateAttributeNamespace) deactivateCmd.Flags().StringP( - deactivateCmd.GetDocFlag("id").Name, - deactivateCmd.GetDocFlag("id").Shorthand, - deactivateCmd.GetDocFlag("id").Default, - deactivateCmd.GetDocFlag("id").Description, + deactivateDoc.GetDocFlag("id").Name, + deactivateDoc.GetDocFlag("id").Shorthand, + deactivateDoc.GetDocFlag("id").Default, + deactivateDoc.GetDocFlag("id").Description, ) deactivateCmd.Flags().Bool( - deactivateCmd.GetDocFlag("force").Name, + deactivateDoc.GetDocFlag("force").Name, false, - deactivateCmd.GetDocFlag("force").Description, + deactivateDoc.GetDocFlag("force").Description, ) // unsafe - unsafeCmd := man.Docs.GetCommand("policy/attributes/namespaces/unsafe") + unsafeDoc := man.Docs.GetDoc("policy/namespaces/unsafe") + unsafeCmd := newCommandFromDoc(unsafeDoc, nil) unsafeCmd.PersistentFlags().BoolVar( &forceUnsafe, - unsafeCmd.GetDocFlag("force").Name, + unsafeDoc.GetDocFlag("force").Name, false, - unsafeCmd.GetDocFlag("force").Description, - ) - deleteCmd := man.Docs.GetCommand("policy/attributes/namespaces/unsafe/delete", - man.WithRun(unsafeDeleteAttributeNamespace), + unsafeDoc.GetDocFlag("force").Description, ) + + deleteDoc := man.Docs.GetDoc("policy/namespaces/unsafe/delete") + deleteCmd := newCommandFromDoc(deleteDoc, unsafeDeleteAttributeNamespace) deleteCmd.Flags().StringP( - deactivateCmd.GetDocFlag("id").Name, - deactivateCmd.GetDocFlag("id").Shorthand, - deactivateCmd.GetDocFlag("id").Default, - deactivateCmd.GetDocFlag("id").Description, - ) - reactivateCmd := man.Docs.GetCommand("policy/attributes/namespaces/unsafe/reactivate", - man.WithRun(unsafeReactivateAttributeNamespace), + deleteDoc.GetDocFlag("id").Name, + deleteDoc.GetDocFlag("id").Shorthand, + deleteDoc.GetDocFlag("id").Default, + deleteDoc.GetDocFlag("id").Description, ) + + reactivateDoc := man.Docs.GetDoc("policy/namespaces/unsafe/reactivate") + reactivateCmd := newCommandFromDoc(reactivateDoc, unsafeReactivateAttributeNamespace) reactivateCmd.Flags().StringP( - deactivateCmd.GetDocFlag("id").Name, - deactivateCmd.GetDocFlag("id").Shorthand, - deactivateCmd.GetDocFlag("id").Default, - deactivateCmd.GetDocFlag("id").Description, - ) - unsafeUpdateCmd := man.Docs.GetCommand("policy/attributes/namespaces/unsafe/update", - man.WithRun(unsafeUpdateAttributeNamespace), + reactivateDoc.GetDocFlag("id").Name, + reactivateDoc.GetDocFlag("id").Shorthand, + reactivateDoc.GetDocFlag("id").Default, + reactivateDoc.GetDocFlag("id").Description, ) + + unsafeUpdateDoc := man.Docs.GetDoc("policy/namespaces/unsafe/update") + unsafeUpdateCmd := newCommandFromDoc(unsafeUpdateDoc, unsafeUpdateAttributeNamespace) unsafeUpdateCmd.Flags().StringP( - deactivateCmd.GetDocFlag("id").Name, - deactivateCmd.GetDocFlag("id").Shorthand, - deactivateCmd.GetDocFlag("id").Default, - deactivateCmd.GetDocFlag("id").Description, + unsafeUpdateDoc.GetDocFlag("id").Name, + unsafeUpdateDoc.GetDocFlag("id").Shorthand, + unsafeUpdateDoc.GetDocFlag("id").Default, + unsafeUpdateDoc.GetDocFlag("id").Description, ) unsafeUpdateCmd.Flags().StringP( - unsafeUpdateCmd.GetDocFlag("name").Name, - unsafeUpdateCmd.GetDocFlag("name").Shorthand, - unsafeUpdateCmd.GetDocFlag("name").Default, - unsafeUpdateCmd.GetDocFlag("name").Description, + unsafeUpdateDoc.GetDocFlag("name").Name, + unsafeUpdateDoc.GetDocFlag("name").Shorthand, + unsafeUpdateDoc.GetDocFlag("name").Default, + unsafeUpdateDoc.GetDocFlag("name").Description, ) - keyCmd := man.Docs.GetCommand("policy/attributes/namespaces/key") - - // Assign KAS key to attribute namespace - assignKasKeyCmd := man.Docs.GetCommand("policy/attributes/namespaces/key/assign", - man.WithRun(policyAssignKeyToNamespace), - ) - assignKasKeyCmd.Flags().StringP( - assignKasKeyCmd.GetDocFlag("namespace").Name, - assignKasKeyCmd.GetDocFlag("namespace").Shorthand, - assignKasKeyCmd.GetDocFlag("namespace").Default, - assignKasKeyCmd.GetDocFlag("namespace").Description, + // key + keyDoc := man.Docs.GetDoc("policy/namespaces/key") + keyCmd := newCommandFromDoc(keyDoc, nil) + + assignDoc := man.Docs.GetDoc("policy/namespaces/key/assign") + assignCmd := newCommandFromDoc(assignDoc, policyAssignKeyToNamespace) + assignCmd.Flags().StringP( + assignDoc.GetDocFlag("namespace").Name, + assignDoc.GetDocFlag("namespace").Shorthand, + assignDoc.GetDocFlag("namespace").Default, + assignDoc.GetDocFlag("namespace").Description, ) - assignKasKeyCmd.Flags().StringP( - assignKasKeyCmd.GetDocFlag("key-id").Name, - assignKasKeyCmd.GetDocFlag("key-id").Shorthand, - assignKasKeyCmd.GetDocFlag("key-id").Default, - assignKasKeyCmd.GetDocFlag("key-id").Description, + assignCmd.Flags().StringP( + assignDoc.GetDocFlag("key-id").Name, + assignDoc.GetDocFlag("key-id").Shorthand, + assignDoc.GetDocFlag("key-id").Default, + assignDoc.GetDocFlag("key-id").Description, ) - // Remove KAS key from attribute namespace - removeKasKeyCmd := man.Docs.GetCommand("policy/attributes/namespaces/key/remove", - man.WithRun(policyRemoveKeyFromNamespace), - ) - removeKasKeyCmd.Flags().StringP( - removeKasKeyCmd.GetDocFlag("namespace").Name, - removeKasKeyCmd.GetDocFlag("namespace").Shorthand, - removeKasKeyCmd.GetDocFlag("namespace").Default, - removeKasKeyCmd.GetDocFlag("namespace").Description, + removeDoc := man.Docs.GetDoc("policy/namespaces/key/remove") + removeCmd := newCommandFromDoc(removeDoc, policyRemoveKeyFromNamespace) + removeCmd.Flags().StringP( + removeDoc.GetDocFlag("namespace").Name, + removeDoc.GetDocFlag("namespace").Shorthand, + removeDoc.GetDocFlag("namespace").Default, + removeDoc.GetDocFlag("namespace").Description, ) - removeKasKeyCmd.Flags().StringP( - removeKasKeyCmd.GetDocFlag("key-id").Name, - removeKasKeyCmd.GetDocFlag("key-id").Shorthand, - removeKasKeyCmd.GetDocFlag("key-id").Default, - removeKasKeyCmd.GetDocFlag("key-id").Description, + removeCmd.Flags().StringP( + removeDoc.GetDocFlag("key-id").Name, + removeDoc.GetDocFlag("key-id").Shorthand, + removeDoc.GetDocFlag("key-id").Default, + removeDoc.GetDocFlag("key-id").Description, ) - keyCmd.AddSubcommands(assignKasKeyCmd, removeKasKeyCmd) - unsafeCmd.AddSubcommands(deleteCmd, reactivateCmd, unsafeUpdateCmd) + keyCmd.AddCommand(assignCmd, removeCmd) + unsafeCmd.AddCommand(deleteCmd, reactivateCmd, unsafeUpdateCmd) + nsCmd.AddCommand(getCmd, listCmd, createCmd, updateCmd, deactivateCmd, unsafeCmd, keyCmd) - NamespacesCmd.AddSubcommands(getCmd, listCmd, createDoc, updateCmd, deactivateCmd, unsafeCmd, keyCmd) - AttributesCmd.AddCommand(&NamespacesCmd.Command) + return nsCmd +} + +func initNamespacesCommands() { + Cmd.AddCommand(buildNamespacesCommandTree()) + AttributesCmd.AddCommand(buildNamespacesCommandTree()) } diff --git a/docs/man/policy/attributes/namespaces/_index.md b/docs/man/policy/namespaces/_index.md similarity index 100% rename from docs/man/policy/attributes/namespaces/_index.md rename to docs/man/policy/namespaces/_index.md diff --git a/docs/man/policy/attributes/namespaces/create.md b/docs/man/policy/namespaces/create.md similarity index 92% rename from docs/man/policy/attributes/namespaces/create.md rename to docs/man/policy/namespaces/create.md index 3f70741f..7fbd064b 100644 --- a/docs/man/policy/attributes/namespaces/create.md +++ b/docs/man/policy/namespaces/create.md @@ -27,5 +27,5 @@ For more information, see the `namespaces` subcommand. ## Example ```shell -otdfctl policy attributes namespaces create --name opentdf.io +otdfctl policy namespaces create --name opentdf.io ``` diff --git a/docs/man/policy/attributes/namespaces/deactivate.md b/docs/man/policy/namespaces/deactivate.md similarity index 89% rename from docs/man/policy/attributes/namespaces/deactivate.md rename to docs/man/policy/namespaces/deactivate.md index a4ced922..35de01d6 100644 --- a/docs/man/policy/attributes/namespaces/deactivate.md +++ b/docs/man/policy/namespaces/deactivate.md @@ -25,5 +25,5 @@ For reactivation, see the `unsafe` command. ## Example ```shell -otdfctl policy attributes namespaces deactivate --id 7650f02a-be00-4faa-a1d1-37cded5e23dc +otdfctl policy namespaces deactivate --id 7650f02a-be00-4faa-a1d1-37cded5e23dc ``` diff --git a/docs/man/policy/attributes/namespaces/get.md b/docs/man/policy/namespaces/get.md similarity index 75% rename from docs/man/policy/attributes/namespaces/get.md rename to docs/man/policy/namespaces/get.md index 0e746e01..3bac5f27 100644 --- a/docs/man/policy/attributes/namespaces/get.md +++ b/docs/man/policy/namespaces/get.md @@ -15,5 +15,5 @@ For more information, see the `namespaces` subcommand. ## Example ```shell -otdfctl policy attributes namespaces get --id=7650f02a-be00-4faa-a1d1-37cded5e23dc +otdfctl policy namespaces get --id=7650f02a-be00-4faa-a1d1-37cded5e23dc ``` \ No newline at end of file diff --git a/docs/man/policy/attributes/namespaces/key/_index.md b/docs/man/policy/namespaces/key/_index.md similarity index 100% rename from docs/man/policy/attributes/namespaces/key/_index.md rename to docs/man/policy/namespaces/key/_index.md diff --git a/docs/man/policy/attributes/namespaces/key/assign.md b/docs/man/policy/namespaces/key/assign.md similarity index 66% rename from docs/man/policy/attributes/namespaces/key/assign.md rename to docs/man/policy/namespaces/key/assign.md index f85f5b96..489de5d8 100644 --- a/docs/man/policy/attributes/namespaces/key/assign.md +++ b/docs/man/policy/namespaces/key/assign.md @@ -18,9 +18,9 @@ Assigns a KAS key to a policy attribute namespace. This enables the attribute na ## Example ```shell -otdfctl policy attributes namespaces assign --namespace 3d25d33e-2469-4990-a9ed-fdd13ce74436 --key-id 8f7e6d5c-4b3a-2d1e-9f8d-7c6b5a432f1d +otdfctl policy namespaces assign --namespace 3d25d33e-2469-4990-a9ed-fdd13ce74436 --key-id 8f7e6d5c-4b3a-2d1e-9f8d-7c6b5a432f1d ``` ```shell -otdfctl policy attributes namespaces remove --namespace "https://example.com" --key-id 8f7e6d5c-4b3a-2d1e-9f8d-7c6b5a432f1d +otdfctl policy namespaces remove --namespace "https://example.com" --key-id 8f7e6d5c-4b3a-2d1e-9f8d-7c6b5a432f1d ``` diff --git a/docs/man/policy/attributes/namespaces/key/remove.md b/docs/man/policy/namespaces/key/remove.md similarity index 67% rename from docs/man/policy/attributes/namespaces/key/remove.md rename to docs/man/policy/namespaces/key/remove.md index a58d4eb4..6853dc23 100644 --- a/docs/man/policy/attributes/namespaces/key/remove.md +++ b/docs/man/policy/namespaces/key/remove.md @@ -18,9 +18,9 @@ Removes a KAS key from a policy attribute namespace. After removing the key, the ## Example ```shell -otdfctl policy attributes namespaces remove --namespace 3d25d33e-2469-4990-a9ed-fdd13ce74436 --key-id 8f7e6d5c-4b3a-2d1e-9f8d-7c6b5a432f1d +otdfctl policy namespaces remove --namespace 3d25d33e-2469-4990-a9ed-fdd13ce74436 --key-id 8f7e6d5c-4b3a-2d1e-9f8d-7c6b5a432f1d ``` ```shell -otdfctl policy attributes namespaces remove --namespace "https://example.com" --key-id 8f7e6d5c-4b3a-2d1e-9f8d-7c6b5a432f1d +otdfctl policy namespaces remove --namespace "https://example.com" --key-id 8f7e6d5c-4b3a-2d1e-9f8d-7c6b5a432f1d ``` diff --git a/docs/man/policy/attributes/namespaces/list.md b/docs/man/policy/namespaces/list.md similarity index 91% rename from docs/man/policy/attributes/namespaces/list.md rename to docs/man/policy/namespaces/list.md index c328d95a..390d84a5 100644 --- a/docs/man/policy/attributes/namespaces/list.md +++ b/docs/man/policy/namespaces/list.md @@ -22,5 +22,5 @@ For more general information, see the `namespaces` subcommand. ## Example ```shell -otdfctl policy attributes namespaces list +otdfctl policy namespaces list ``` diff --git a/docs/man/policy/attributes/namespaces/unsafe/_index.md b/docs/man/policy/namespaces/unsafe/_index.md similarity index 100% rename from docs/man/policy/attributes/namespaces/unsafe/_index.md rename to docs/man/policy/namespaces/unsafe/_index.md diff --git a/docs/man/policy/attributes/namespaces/unsafe/delete.md b/docs/man/policy/namespaces/unsafe/delete.md similarity index 86% rename from docs/man/policy/attributes/namespaces/unsafe/delete.md rename to docs/man/policy/namespaces/unsafe/delete.md index e877ba77..74ef4503 100644 --- a/docs/man/policy/attributes/namespaces/unsafe/delete.md +++ b/docs/man/policy/namespaces/unsafe/delete.md @@ -22,5 +22,5 @@ For more general information, see the `namespaces` subcommand. ## Example ```shell -otdfctl policy attributes namespaces unsafe delete --id 7650f02a-be00-4faa-a1d1-37cded5e23dc +otdfctl policy namespaces unsafe delete --id 7650f02a-be00-4faa-a1d1-37cded5e23dc ``` diff --git a/docs/man/policy/attributes/namespaces/unsafe/reactivate.md b/docs/man/policy/namespaces/unsafe/reactivate.md similarity index 85% rename from docs/man/policy/attributes/namespaces/unsafe/reactivate.md rename to docs/man/policy/namespaces/unsafe/reactivate.md index 5e0ed742..2f4a6b97 100644 --- a/docs/man/policy/attributes/namespaces/unsafe/reactivate.md +++ b/docs/man/policy/namespaces/unsafe/reactivate.md @@ -22,5 +22,5 @@ For more general information, see the `namespaces` subcommand. ## Example ```shell -otdfctl policy attributes namespaces unsafe reactivate --id 7650f02a-be00-4faa-a1d1-37cded5e23dc +otdfctl policy namespaces unsafe reactivate --id 7650f02a-be00-4faa-a1d1-37cded5e23dc ``` diff --git a/docs/man/policy/attributes/namespaces/unsafe/update.md b/docs/man/policy/namespaces/unsafe/update.md similarity index 87% rename from docs/man/policy/attributes/namespaces/unsafe/update.md rename to docs/man/policy/namespaces/unsafe/update.md index b536e91f..a2ead5a7 100644 --- a/docs/man/policy/attributes/namespaces/unsafe/update.md +++ b/docs/man/policy/namespaces/unsafe/update.md @@ -27,5 +27,5 @@ For more general information, see the `namespaces` subcommand. ## Example ```shell -otdfctl policy attributes namespaces unsafe update --id=7650f02a-be00-4faa-a1d1-37cded5e23dc --name opentdf2.io +otdfctl policy namespaces unsafe update --id=7650f02a-be00-4faa-a1d1-37cded5e23dc --name opentdf2.io ``` diff --git a/docs/man/policy/attributes/namespaces/update.md b/docs/man/policy/namespaces/update.md similarity index 88% rename from docs/man/policy/attributes/namespaces/update.md rename to docs/man/policy/namespaces/update.md index 57390939..25cba980 100644 --- a/docs/man/policy/attributes/namespaces/update.md +++ b/docs/man/policy/namespaces/update.md @@ -25,5 +25,5 @@ For unsafe updates, see the dedicated `unsafe update` command. For more general ## Example ```shell -otdfctl policy attributes namespaces update --id=7650f02a-be00-4faa-a1d1-37cded5e23dc --label hello=world +otdfctl policy namespaces update --id=7650f02a-be00-4faa-a1d1-37cded5e23dc --label hello=world ``` diff --git a/e2e/namespaces.bats b/e2e/namespaces.bats index 699acedf..f39e1344 100755 --- a/e2e/namespaces.bats +++ b/e2e/namespaces.bats @@ -26,10 +26,14 @@ setup_file() { } setup() { - # invoke binary with credentials + # invoke binary with credentials under 'policy attributes namespaces' run_otdfctl_ns() { run sh -c "./otdfctl $HOST $WITH_CREDS policy attributes namespaces $*" } + # invoke binary with credentials under 'policy namespaces' (direct path) + run_otdfctl_nsd() { + run sh -c "./otdfctl $HOST $WITH_CREDS policy namespaces $*" + } } teardown_file() { @@ -280,3 +284,23 @@ teardown_file() { run_otdfctl_ns list --state active echo $output | refute_output --partial "$NS_ID" } + +# ── policy namespaces (direct path) ────────────────────────────────────────── + +@test "Direct path: policy namespaces commands are accessible" { + run_otdfctl_nsd create --name direct-path-test.net --json + assert_success + DIRECT_NS_ID=$(echo "$output" | jq -r '.id') + + run_otdfctl_nsd get --id "$DIRECT_NS_ID" --json + assert_success + assert_equal "$(echo "$output" | jq -r '.name')" "direct-path-test.net" + + run_otdfctl_nsd list --json + assert_success + assert_output --partial "$DIRECT_NS_ID" + + # cleanup + run_otdfctl_nsd unsafe delete --id "$DIRECT_NS_ID" --force + assert_success +}