From acb93dab8006d1bd64993ec0a8b71c108f3541f0 Mon Sep 17 00:00:00 2001 From: David Liu Date: Thu, 25 Jun 2026 14:30:10 -0400 Subject: [PATCH 1/2] Add mtls/apikey auth setting commands for namespace --- temporalcloudcli/commands.gen.go | 634 +++++++++++------- temporalcloudcli/commands.namespace.apikey.go | 74 ++ .../commands.namespace.apikey_test.go | 268 ++++++++ .../commands.namespace.cert_ca.go | 6 +- .../commands.namespace.cert_ca_test.go | 124 ++-- .../commands.namespace.cert_filter.go | 6 +- .../commands.namespace.cert_filter_test.go | 128 ++-- .../commands.namespace.create_test.go | 48 ++ temporalcloudcli/commands.namespace.go | 8 +- temporalcloudcli/commands.namespace.mtls.go | 74 ++ .../commands.namespace.mtls_test.go | 268 ++++++++ temporalcloudcli/commands.yml | 118 +++- 12 files changed, 1373 insertions(+), 383 deletions(-) create mode 100644 temporalcloudcli/commands.namespace.apikey.go create mode 100644 temporalcloudcli/commands.namespace.apikey_test.go create mode 100644 temporalcloudcli/commands.namespace.mtls.go create mode 100644 temporalcloudcli/commands.namespace.mtls_test.go diff --git a/temporalcloudcli/commands.gen.go b/temporalcloudcli/commands.gen.go index 156aa0c..fbe25a2 100644 --- a/temporalcloudcli/commands.gen.go +++ b/temporalcloudcli/commands.gen.go @@ -1839,10 +1839,9 @@ func NewCloudNamespaceCommand(cctx *CommandContext, parent *CloudCommand) *Cloud s.Command.Short = "Manage Temporal Cloud namespaces" s.Command.Long = "Commands for creating, updating, and managing Temporal Cloud namespaces.\n\nNamespaces provide isolation for workflows and activities. Each namespace\nhas its own configuration including retention period, region, and access\ncontrols." s.Command.Args = cobra.NoArgs + s.Command.AddCommand(&NewCloudNamespaceApiKeyCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceApplyCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceCapacityCommand(cctx, &s).Command) - s.Command.AddCommand(&NewCloudNamespaceCertCaCommand(cctx, &s).Command) - s.Command.AddCommand(&NewCloudNamespaceCertFilterCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceCodecCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceConnectivityCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceCreateCommand(cctx, &s).Command) @@ -1854,86 +1853,47 @@ func NewCloudNamespaceCommand(cctx *CommandContext, parent *CloudCommand) *Cloud s.Command.AddCommand(&NewCloudNamespaceHaCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceLifecycleCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceListCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceMtlsCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceRetentionCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceSearchAttributeCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceTagCommand(cctx, &s).Command) return &s } -type CloudNamespaceApplyCommand struct { - Parent *CloudNamespaceCommand - Command cobra.Command - ClientOptions - DiffOptions - Spec string - AsyncOperationId string - Idempotent bool - Async bool - ResourceVersion string -} - -func NewCloudNamespaceApplyCommand(cctx *CommandContext, parent *CloudNamespaceCommand) *CloudNamespaceApplyCommand { - var s CloudNamespaceApplyCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "apply [flags]" - s.Command.Short = "Create or update a namespace from a specification" - if hasHighlighting { - s.Command.Long = "Apply a namespace configuration to Temporal Cloud. Creates a new namespace\nif it doesn't exist, or updates an existing one to match the specification.\n\nThe specification can be provided as inline JSON or loaded from a file\nby prefixing the path with '@'.\n\nExample with inline JSON:\n\n\x1b[1mtemporal cloud namespace apply --spec '{\"name\": \"namespace-name\", \"region\": \"us-west-2\", \"retention_days\": 7}'\x1b[0m\n\nExample with file path:\n\n\x1b[1mtemporal cloud namespace apply --spec @namespace-spec.json\x1b[0m" - } else { - s.Command.Long = "Apply a namespace configuration to Temporal Cloud. Creates a new namespace\nif it doesn't exist, or updates an existing one to match the specification.\n\nThe specification can be provided as inline JSON or loaded from a file\nby prefixing the path with '@'.\n\nExample with inline JSON:\n\n```\ntemporal cloud namespace apply --spec '{\"name\": \"namespace-name\", \"region\": \"us-west-2\", \"retention_days\": 7}'\n```\n\nExample with file path:\n\n```\ntemporal cloud namespace apply --spec @namespace-spec.json\n```" - } - s.Command.Args = cobra.NoArgs - s.Command.Flags().StringVar(&s.Spec, "spec", "", "Namespace configuration in JSON format. Provide inline JSON directly, or use '@path/to/file.json' to load from a file. Required.") - _ = cobra.MarkFlagRequired(s.Command.Flags(), "spec") - s.Command.Flags().StringVar(&s.AsyncOperationId, "async-operation-id", "", "Custom identifier for tracking this async operation. If not provided, a unique ID is generated automatically.") - s.Command.Flags().BoolVar(&s.Idempotent, "idempotent", false, "Succeed silently if the namespace already matches the specification. Without this flag, the command errors when no changes are needed.") - s.Command.Flags().BoolVar(&s.Async, "async", false, "Return immediately after initiating the operation instead of waiting for completion. Use the returned operation ID to check status later.") - s.Command.Flags().StringVarP(&s.ResourceVersion, "resource-version", "v", "", "Resource version for optimistic concurrency control. If not provided, the current version is fetched automatically.") - s.ClientOptions.BuildFlags(s.Command.Flags()) - s.DiffOptions.BuildFlags(s.Command.Flags()) - s.Command.Run = func(c *cobra.Command, args []string) { - if err := s.run(cctx, args); err != nil { - cctx.Options.Fail(err) - } - } - return &s -} - -type CloudNamespaceCapacityCommand struct { +type CloudNamespaceApiKeyCommand struct { Parent *CloudNamespaceCommand Command cobra.Command } -func NewCloudNamespaceCapacityCommand(cctx *CommandContext, parent *CloudNamespaceCommand) *CloudNamespaceCapacityCommand { - var s CloudNamespaceCapacityCommand +func NewCloudNamespaceApiKeyCommand(cctx *CommandContext, parent *CloudNamespaceCommand) *CloudNamespaceApiKeyCommand { + var s CloudNamespaceApiKeyCommand s.Parent = parent - s.Command.Use = "capacity" - s.Command.Short = "Manage namespace capacity" - s.Command.Long = "Commands for managing the capacity of Temporal Cloud namespaces.\n\nCapacity controls whether a namespace runs in on-demand mode or\nprovisioned mode (with a fixed TRU allocation)." + s.Command.Use = "api-key" + s.Command.Short = "Manage namespace API key authentication settings" + s.Command.Long = "Commands for managing API key authentication configuration of Temporal Cloud namespaces." s.Command.Args = cobra.NoArgs - s.Command.AddCommand(&NewCloudNamespaceCapacityGetCommand(cctx, &s).Command) - s.Command.AddCommand(&NewCloudNamespaceCapacityUpdateCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceApiKeyGetCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceApiKeySetCommand(cctx, &s).Command) return &s } -type CloudNamespaceCapacityGetCommand struct { - Parent *CloudNamespaceCapacityCommand +type CloudNamespaceApiKeyGetCommand struct { + Parent *CloudNamespaceApiKeyCommand Command cobra.Command ClientOptions NamespaceOptions } -func NewCloudNamespaceCapacityGetCommand(cctx *CommandContext, parent *CloudNamespaceCapacityCommand) *CloudNamespaceCapacityGetCommand { - var s CloudNamespaceCapacityGetCommand +func NewCloudNamespaceApiKeyGetCommand(cctx *CommandContext, parent *CloudNamespaceApiKeyCommand) *CloudNamespaceApiKeyGetCommand { + var s CloudNamespaceApiKeyGetCommand s.Parent = parent s.Command.DisableFlagsInUseLine = true s.Command.Use = "get [flags]" - s.Command.Short = "Get namespace capacity information" + s.Command.Short = "Get namespace API key authentication configuration" if hasHighlighting { - s.Command.Long = "Retrieve capacity information for a Temporal Cloud namespace, including\nthe current mode (on-demand or provisioned), mode options, and recent usage stats.\n\nExample:\n\n\x1b[1mtemporal cloud namespace capacity get --namespace my-namespace.my-account\x1b[0m" + s.Command.Long = "Retrieve the current API key authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace api-key get --namespace my-namespace.my-account\x1b[0m" } else { - s.Command.Long = "Retrieve capacity information for a Temporal Cloud namespace, including\nthe current mode (on-demand or provisioned), mode options, and recent usage stats.\n\nExample:\n\n```\ntemporal cloud namespace capacity get --namespace my-namespace.my-account\n```" + s.Command.Long = "Retrieve the current API key authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace api-key get --namespace my-namespace.my-account\n```" } s.Command.Args = cobra.NoArgs s.ClientOptions.BuildFlags(s.Command.Flags()) @@ -1946,33 +1906,30 @@ func NewCloudNamespaceCapacityGetCommand(cctx *CommandContext, parent *CloudName return &s } -type CloudNamespaceCapacityUpdateCommand struct { - Parent *CloudNamespaceCapacityCommand +type CloudNamespaceApiKeySetCommand struct { + Parent *CloudNamespaceApiKeyCommand Command cobra.Command ClientOptions NamespaceOptions AsyncOperationOptions ResourceVersionOptions - CapacityMode cliext.FlagStringEnum - CapacityValue float32 + ApiKeyAuthEnabled bool } -func NewCloudNamespaceCapacityUpdateCommand(cctx *CommandContext, parent *CloudNamespaceCapacityCommand) *CloudNamespaceCapacityUpdateCommand { - var s CloudNamespaceCapacityUpdateCommand +func NewCloudNamespaceApiKeySetCommand(cctx *CommandContext, parent *CloudNamespaceApiKeyCommand) *CloudNamespaceApiKeySetCommand { + var s CloudNamespaceApiKeySetCommand s.Parent = parent s.Command.DisableFlagsInUseLine = true - s.Command.Use = "update [flags]" - s.Command.Short = "Update namespace capacity" + s.Command.Use = "set [flags]" + s.Command.Short = "Set namespace API key authentication configuration" if hasHighlighting { - s.Command.Long = "Update the capacity of a Temporal Cloud namespace. Choose either on-demand\nmode or provisioned mode (with a fixed TRU allocation).\n\nExample (switch to on-demand):\n\n\x1b[1mtemporal cloud namespace capacity update --namespace my-namespace.my-account --capacity-mode on_demand\x1b[0m\n\nExample (switch to provisioned with 4 TRUs):\n\n\x1b[1mtemporal cloud namespace capacity update --namespace my-namespace.my-account --capacity-mode provisioned --capacity-value 4\x1b[0m" + s.Command.Long = "Enable or disable API key authentication for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace api-key set --namespace my-namespace.my-account --api-key-auth-enabled=true\x1b[0m" } else { - s.Command.Long = "Update the capacity of a Temporal Cloud namespace. Choose either on-demand\nmode or provisioned mode (with a fixed TRU allocation).\n\nExample (switch to on-demand):\n\n```\ntemporal cloud namespace capacity update --namespace my-namespace.my-account --capacity-mode on_demand\n```\n\nExample (switch to provisioned with 4 TRUs):\n\n```\ntemporal cloud namespace capacity update --namespace my-namespace.my-account --capacity-mode provisioned --capacity-value 4\n```" + s.Command.Long = "Enable or disable API key authentication for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace api-key set --namespace my-namespace.my-account --api-key-auth-enabled=true\n```" } s.Command.Args = cobra.NoArgs - s.CapacityMode = cliext.NewFlagStringEnum([]string{"on_demand", "provisioned"}, "") - s.Command.Flags().Var(&s.CapacityMode, "capacity-mode", "Capacity mode for the namespace. Must be either 'on_demand' or 'provisioned'. Accepted values: on_demand, provisioned. Required.") - _ = cobra.MarkFlagRequired(s.Command.Flags(), "capacity-mode") - s.Command.Flags().Float32Var(&s.CapacityValue, "capacity-value", 0, "The provisioned capacity in Temporal Resource Units (TRUs). Required and must be greater than 0 when --capacity-mode is 'provisioned'. Ignored when --capacity-mode is 'on_demand'.") + s.Command.Flags().BoolVar(&s.ApiKeyAuthEnabled, "api-key-auth-enabled", false, "Enable or disable API key authentication for the namespace. Required.") + _ = cobra.MarkFlagRequired(s.Command.Flags(), "api-key-auth-enabled") s.ClientOptions.BuildFlags(s.Command.Flags()) s.NamespaceOptions.BuildFlags(s.Command.Flags()) s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) @@ -1985,51 +1942,38 @@ func NewCloudNamespaceCapacityUpdateCommand(cctx *CommandContext, parent *CloudN return &s } -type CloudNamespaceCertCaCommand struct { +type CloudNamespaceApplyCommand struct { Parent *CloudNamespaceCommand Command cobra.Command -} - -func NewCloudNamespaceCertCaCommand(cctx *CommandContext, parent *CloudNamespaceCommand) *CloudNamespaceCertCaCommand { - var s CloudNamespaceCertCaCommand - s.Parent = parent - s.Command.Use = "cert-ca" - s.Command.Short = "Manage client CA certificates for namespaces" - s.Command.Long = "Commands for managing the client CA certificates of Temporal Cloud namespaces." - s.Command.Args = cobra.NoArgs - s.Command.AddCommand(&NewCloudNamespaceCertCaCreateCommand(cctx, &s).Command) - s.Command.AddCommand(&NewCloudNamespaceCertCaDeleteCommand(cctx, &s).Command) - s.Command.AddCommand(&NewCloudNamespaceCertCaListCommand(cctx, &s).Command) - return &s -} - -type CloudNamespaceCertCaCreateCommand struct { - Parent *CloudNamespaceCertCaCommand - Command cobra.Command ClientOptions - NamespaceOptions - AsyncOperationOptions - ResourceVersionOptions - CaCertificateOptions + DiffOptions + Spec string + AsyncOperationId string + Idempotent bool + Async bool + ResourceVersion string } -func NewCloudNamespaceCertCaCreateCommand(cctx *CommandContext, parent *CloudNamespaceCertCaCommand) *CloudNamespaceCertCaCreateCommand { - var s CloudNamespaceCertCaCreateCommand +func NewCloudNamespaceApplyCommand(cctx *CommandContext, parent *CloudNamespaceCommand) *CloudNamespaceApplyCommand { + var s CloudNamespaceApplyCommand s.Parent = parent s.Command.DisableFlagsInUseLine = true - s.Command.Use = "create [flags]" - s.Command.Short = "Add CA certificates to a namespace" + s.Command.Use = "apply [flags]" + s.Command.Short = "Create or update a namespace from a specification" if hasHighlighting { - s.Command.Long = "Add client CA certificates to a Temporal Cloud namespace from a PEM file\nor base64 encoded string. These certificates are used to verify client\nconnections and enable mTLS authentication.\n\nSpecify either --ca-certificate-file or --ca-certificate, but not both.\n\nExample with file:\n\n\x1b[1mtemporal cloud namespace cert-ca create --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem\x1b[0m\n\nExample with base64 encoded data:\n\n\x1b[1mtemporal cloud namespace cert-ca create --namespace my-namespace.my-account --ca-certificate \x1b[0m" + s.Command.Long = "Apply a namespace configuration to Temporal Cloud. Creates a new namespace\nif it doesn't exist, or updates an existing one to match the specification.\n\nThe specification can be provided as inline JSON or loaded from a file\nby prefixing the path with '@'.\n\nExample with inline JSON:\n\n\x1b[1mtemporal cloud namespace apply --spec '{\"name\": \"namespace-name\", \"region\": \"us-west-2\", \"retention_days\": 7}'\x1b[0m\n\nExample with file path:\n\n\x1b[1mtemporal cloud namespace apply --spec @namespace-spec.json\x1b[0m" } else { - s.Command.Long = "Add client CA certificates to a Temporal Cloud namespace from a PEM file\nor base64 encoded string. These certificates are used to verify client\nconnections and enable mTLS authentication.\n\nSpecify either --ca-certificate-file or --ca-certificate, but not both.\n\nExample with file:\n\n```\ntemporal cloud namespace cert-ca create --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem\n```\n\nExample with base64 encoded data:\n\n```\ntemporal cloud namespace cert-ca create --namespace my-namespace.my-account --ca-certificate \n```" + s.Command.Long = "Apply a namespace configuration to Temporal Cloud. Creates a new namespace\nif it doesn't exist, or updates an existing one to match the specification.\n\nThe specification can be provided as inline JSON or loaded from a file\nby prefixing the path with '@'.\n\nExample with inline JSON:\n\n```\ntemporal cloud namespace apply --spec '{\"name\": \"namespace-name\", \"region\": \"us-west-2\", \"retention_days\": 7}'\n```\n\nExample with file path:\n\n```\ntemporal cloud namespace apply --spec @namespace-spec.json\n```" } s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVar(&s.Spec, "spec", "", "Namespace configuration in JSON format. Provide inline JSON directly, or use '@path/to/file.json' to load from a file. Required.") + _ = cobra.MarkFlagRequired(s.Command.Flags(), "spec") + s.Command.Flags().StringVar(&s.AsyncOperationId, "async-operation-id", "", "Custom identifier for tracking this async operation. If not provided, a unique ID is generated automatically.") + s.Command.Flags().BoolVar(&s.Idempotent, "idempotent", false, "Succeed silently if the namespace already matches the specification. Without this flag, the command errors when no changes are needed.") + s.Command.Flags().BoolVar(&s.Async, "async", false, "Return immediately after initiating the operation instead of waiting for completion. Use the returned operation ID to check status later.") + s.Command.Flags().StringVarP(&s.ResourceVersion, "resource-version", "v", "", "Resource version for optimistic concurrency control. If not provided, the current version is fetched automatically.") s.ClientOptions.BuildFlags(s.Command.Flags()) - s.NamespaceOptions.BuildFlags(s.Command.Flags()) - s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) - s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) - s.CaCertificateOptions.BuildFlags(s.Command.Flags()) + s.DiffOptions.BuildFlags(s.Command.Flags()) s.Command.Run = func(c *cobra.Command, args []string) { if err := s.run(cctx, args); err != nil { cctx.Options.Fail(err) @@ -2038,58 +1982,40 @@ func NewCloudNamespaceCertCaCreateCommand(cctx *CommandContext, parent *CloudNam return &s } -type CloudNamespaceCertCaDeleteCommand struct { - Parent *CloudNamespaceCertCaCommand +type CloudNamespaceCapacityCommand struct { + Parent *CloudNamespaceCommand Command cobra.Command - ClientOptions - NamespaceOptions - AsyncOperationOptions - ResourceVersionOptions - CaCertificateOptions } -func NewCloudNamespaceCertCaDeleteCommand(cctx *CommandContext, parent *CloudNamespaceCertCaCommand) *CloudNamespaceCertCaDeleteCommand { - var s CloudNamespaceCertCaDeleteCommand +func NewCloudNamespaceCapacityCommand(cctx *CommandContext, parent *CloudNamespaceCommand) *CloudNamespaceCapacityCommand { + var s CloudNamespaceCapacityCommand s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "delete [flags]" - s.Command.Short = "Delete CA certificates from a namespace" - if hasHighlighting { - s.Command.Long = "Delete client CA certificates from a Temporal Cloud namespace. This operation\nrequires confirmation and will remove the specified certificates from the\nnamespace configuration.\n\nSpecify either --ca-certificate-file or --ca-certificate, but not both.\n\nExample with file:\n\n\x1b[1mtemporal cloud namespace cert-ca delete --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem\x1b[0m\n\nExample with base64 encoded data:\n\n\x1b[1mtemporal cloud namespace cert-ca delete --namespace my-namespace.my-account --ca-certificate \x1b[0m" - } else { - s.Command.Long = "Delete client CA certificates from a Temporal Cloud namespace. This operation\nrequires confirmation and will remove the specified certificates from the\nnamespace configuration.\n\nSpecify either --ca-certificate-file or --ca-certificate, but not both.\n\nExample with file:\n\n```\ntemporal cloud namespace cert-ca delete --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem\n```\n\nExample with base64 encoded data:\n\n```\ntemporal cloud namespace cert-ca delete --namespace my-namespace.my-account --ca-certificate \n```" - } + s.Command.Use = "capacity" + s.Command.Short = "Manage namespace capacity" + s.Command.Long = "Commands for managing the capacity of Temporal Cloud namespaces.\n\nCapacity controls whether a namespace runs in on-demand mode or\nprovisioned mode (with a fixed TRU allocation)." s.Command.Args = cobra.NoArgs - s.ClientOptions.BuildFlags(s.Command.Flags()) - s.NamespaceOptions.BuildFlags(s.Command.Flags()) - s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) - s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) - s.CaCertificateOptions.BuildFlags(s.Command.Flags()) - s.Command.Run = func(c *cobra.Command, args []string) { - if err := s.run(cctx, args); err != nil { - cctx.Options.Fail(err) - } - } + s.Command.AddCommand(&NewCloudNamespaceCapacityGetCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceCapacityUpdateCommand(cctx, &s).Command) return &s } -type CloudNamespaceCertCaListCommand struct { - Parent *CloudNamespaceCertCaCommand +type CloudNamespaceCapacityGetCommand struct { + Parent *CloudNamespaceCapacityCommand Command cobra.Command ClientOptions NamespaceOptions } -func NewCloudNamespaceCertCaListCommand(cctx *CommandContext, parent *CloudNamespaceCertCaCommand) *CloudNamespaceCertCaListCommand { - var s CloudNamespaceCertCaListCommand +func NewCloudNamespaceCapacityGetCommand(cctx *CommandContext, parent *CloudNamespaceCapacityCommand) *CloudNamespaceCapacityGetCommand { + var s CloudNamespaceCapacityGetCommand s.Parent = parent s.Command.DisableFlagsInUseLine = true - s.Command.Use = "list [flags]" - s.Command.Short = "List CA certificates for a namespace" + s.Command.Use = "get [flags]" + s.Command.Short = "Get namespace capacity information" if hasHighlighting { - s.Command.Long = "Retrieve the list of client CA certificates configured for a Temporal Cloud\nnamespace. These certificates are used for client authentication.\n\nExample:\n\n\x1b[1mtemporal cloud namespace cert-ca list --namespace my-namespace.my-account\x1b[0m" + s.Command.Long = "Retrieve capacity information for a Temporal Cloud namespace, including\nthe current mode (on-demand or provisioned), mode options, and recent usage stats.\n\nExample:\n\n\x1b[1mtemporal cloud namespace capacity get --namespace my-namespace.my-account\x1b[0m" } else { - s.Command.Long = "Retrieve the list of client CA certificates configured for a Temporal Cloud\nnamespace. These certificates are used for client authentication.\n\nExample:\n\n```\ntemporal cloud namespace cert-ca list --namespace my-namespace.my-account\n```" + s.Command.Long = "Retrieve capacity information for a Temporal Cloud namespace, including\nthe current mode (on-demand or provisioned), mode options, and recent usage stats.\n\nExample:\n\n```\ntemporal cloud namespace capacity get --namespace my-namespace.my-account\n```" } s.Command.Args = cobra.NoArgs s.ClientOptions.BuildFlags(s.Command.Flags()) @@ -2102,86 +2028,33 @@ func NewCloudNamespaceCertCaListCommand(cctx *CommandContext, parent *CloudNames return &s } -type CloudNamespaceCertFilterCommand struct { - Parent *CloudNamespaceCommand - Command cobra.Command -} - -func NewCloudNamespaceCertFilterCommand(cctx *CommandContext, parent *CloudNamespaceCommand) *CloudNamespaceCertFilterCommand { - var s CloudNamespaceCertFilterCommand - s.Parent = parent - s.Command.Use = "cert-filter" - s.Command.Short = "Manage certificate filters for namespaces" - s.Command.Long = "Commands for managing certificate filters for Temporal Cloud namespaces.\nCertificate filters restrict mTLS connections to client certificates with\nspecific distinguished name properties." - s.Command.Args = cobra.NoArgs - s.Command.AddCommand(&NewCloudNamespaceCertFilterCreateCommand(cctx, &s).Command) - s.Command.AddCommand(&NewCloudNamespaceCertFilterDeleteCommand(cctx, &s).Command) - s.Command.AddCommand(&NewCloudNamespaceCertFilterListCommand(cctx, &s).Command) - return &s -} - -type CloudNamespaceCertFilterCreateCommand struct { - Parent *CloudNamespaceCertFilterCommand +type CloudNamespaceCapacityUpdateCommand struct { + Parent *CloudNamespaceCapacityCommand Command cobra.Command ClientOptions NamespaceOptions AsyncOperationOptions ResourceVersionOptions - CommonName string - Organization string - OrganizationalUnit string - SubjectAlternativeName string + CapacityMode cliext.FlagStringEnum + CapacityValue float32 } -func NewCloudNamespaceCertFilterCreateCommand(cctx *CommandContext, parent *CloudNamespaceCertFilterCommand) *CloudNamespaceCertFilterCreateCommand { - var s CloudNamespaceCertFilterCreateCommand +func NewCloudNamespaceCapacityUpdateCommand(cctx *CommandContext, parent *CloudNamespaceCapacityCommand) *CloudNamespaceCapacityUpdateCommand { + var s CloudNamespaceCapacityUpdateCommand s.Parent = parent s.Command.DisableFlagsInUseLine = true - s.Command.Use = "create [flags]" - s.Command.Short = "Add certificate filters to a namespace" - s.Command.Long = "Add new certificate filters to a Temporal Cloud namespace. Certificate\nfilters restrict mTLS connections to client certificates whose distinguished\nname properties match at least one of the filters." - s.Command.Args = cobra.NoArgs - s.Command.Flags().StringVar(&s.CommonName, "common-name", "", "The common name (CN) field from the certificate's distinguished name.") - s.Command.Flags().StringVar(&s.Organization, "organization", "", "The organization (O) field from the certificate's distinguished name.") - s.Command.Flags().StringVar(&s.OrganizationalUnit, "organizational-unit", "", "The organizational unit (OU) field from the certificate's distinguished name.") - s.Command.Flags().StringVar(&s.SubjectAlternativeName, "subject-alternative-name", "", "The subject alternative name (SAN) from the certificate.") - s.ClientOptions.BuildFlags(s.Command.Flags()) - s.NamespaceOptions.BuildFlags(s.Command.Flags()) - s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) - s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) - s.Command.Run = func(c *cobra.Command, args []string) { - if err := s.run(cctx, args); err != nil { - cctx.Options.Fail(err) - } + s.Command.Use = "update [flags]" + s.Command.Short = "Update namespace capacity" + if hasHighlighting { + s.Command.Long = "Update the capacity of a Temporal Cloud namespace. Choose either on-demand\nmode or provisioned mode (with a fixed TRU allocation).\n\nExample (switch to on-demand):\n\n\x1b[1mtemporal cloud namespace capacity update --namespace my-namespace.my-account --capacity-mode on_demand\x1b[0m\n\nExample (switch to provisioned with 4 TRUs):\n\n\x1b[1mtemporal cloud namespace capacity update --namespace my-namespace.my-account --capacity-mode provisioned --capacity-value 4\x1b[0m" + } else { + s.Command.Long = "Update the capacity of a Temporal Cloud namespace. Choose either on-demand\nmode or provisioned mode (with a fixed TRU allocation).\n\nExample (switch to on-demand):\n\n```\ntemporal cloud namespace capacity update --namespace my-namespace.my-account --capacity-mode on_demand\n```\n\nExample (switch to provisioned with 4 TRUs):\n\n```\ntemporal cloud namespace capacity update --namespace my-namespace.my-account --capacity-mode provisioned --capacity-value 4\n```" } - return &s -} - -type CloudNamespaceCertFilterDeleteCommand struct { - Parent *CloudNamespaceCertFilterCommand - Command cobra.Command - ClientOptions - NamespaceOptions - AsyncOperationOptions - ResourceVersionOptions - CommonName string - Organization string - OrganizationalUnit string - SubjectAlternativeName string -} - -func NewCloudNamespaceCertFilterDeleteCommand(cctx *CommandContext, parent *CloudNamespaceCertFilterCommand) *CloudNamespaceCertFilterDeleteCommand { - var s CloudNamespaceCertFilterDeleteCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "delete [flags]" - s.Command.Short = "Delete certificate filters from a namespace" - s.Command.Long = "Delete certificate filters from a Temporal Cloud namespace. Filters are\nmatched by exact field equality." s.Command.Args = cobra.NoArgs - s.Command.Flags().StringVar(&s.CommonName, "common-name", "", "The common name (CN) field from the certificate's distinguished name.") - s.Command.Flags().StringVar(&s.Organization, "organization", "", "The organization (O) field from the certificate's distinguished name.") - s.Command.Flags().StringVar(&s.OrganizationalUnit, "organizational-unit", "", "The organizational unit (OU) field from the certificate's distinguished name.") - s.Command.Flags().StringVar(&s.SubjectAlternativeName, "subject-alternative-name", "", "The subject alternative name (SAN) from the certificate.") + s.CapacityMode = cliext.NewFlagStringEnum([]string{"on_demand", "provisioned"}, "") + s.Command.Flags().Var(&s.CapacityMode, "capacity-mode", "Capacity mode for the namespace. Must be either 'on_demand' or 'provisioned'. Accepted values: on_demand, provisioned. Required.") + _ = cobra.MarkFlagRequired(s.Command.Flags(), "capacity-mode") + s.Command.Flags().Float32Var(&s.CapacityValue, "capacity-value", 0, "The provisioned capacity in Temporal Resource Units (TRUs). Required and must be greater than 0 when --capacity-mode is 'provisioned'. Ignored when --capacity-mode is 'on_demand'.") s.ClientOptions.BuildFlags(s.Command.Flags()) s.NamespaceOptions.BuildFlags(s.Command.Flags()) s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) @@ -2194,31 +2067,6 @@ func NewCloudNamespaceCertFilterDeleteCommand(cctx *CommandContext, parent *Clou return &s } -type CloudNamespaceCertFilterListCommand struct { - Parent *CloudNamespaceCertFilterCommand - Command cobra.Command - ClientOptions - NamespaceOptions -} - -func NewCloudNamespaceCertFilterListCommand(cctx *CommandContext, parent *CloudNamespaceCertFilterCommand) *CloudNamespaceCertFilterListCommand { - var s CloudNamespaceCertFilterListCommand - s.Parent = parent - s.Command.DisableFlagsInUseLine = true - s.Command.Use = "list [flags]" - s.Command.Short = "List certificate filters for a namespace" - s.Command.Long = "List all certificate filters configured for a Temporal Cloud namespace." - s.Command.Args = cobra.NoArgs - s.ClientOptions.BuildFlags(s.Command.Flags()) - s.NamespaceOptions.BuildFlags(s.Command.Flags()) - s.Command.Run = func(c *cobra.Command, args []string) { - if err := s.run(cctx, args); err != nil { - cctx.Options.Fail(err) - } - } - return &s -} - type CloudNamespaceCodecCommand struct { Parent *CloudNamespaceCommand Command cobra.Command @@ -2474,6 +2322,7 @@ type CloudNamespaceCreateCommand struct { Region []string RetentionDays int ApiKeyAuthEnabled bool + MtlsAuthEnabled bool EnableDeleteProtection bool EnableTaskQueueFairness bool SearchAttribute []string @@ -2498,6 +2347,7 @@ func NewCloudNamespaceCreateCommand(cctx *CommandContext, parent *CloudNamespace _ = cobra.MarkFlagRequired(s.Command.Flags(), "region") s.Command.Flags().IntVar(&s.RetentionDays, "retention-days", 0, "Number of days to retain closed workflow history. If not specified, the server default applies.") s.Command.Flags().BoolVar(&s.ApiKeyAuthEnabled, "api-key-auth-enabled", false, "Enable API key authentication for the namespace.") + s.Command.Flags().BoolVar(&s.MtlsAuthEnabled, "mtls-auth-enabled", false, "Enable mTLS authentication for the namespace.") s.Command.Flags().BoolVar(&s.EnableDeleteProtection, "enable-delete-protection", false, "Prevent accidental deletion of this namespace.") s.Command.Flags().BoolVar(&s.EnableTaskQueueFairness, "enable-task-queue-fairness", false, "Enable task queue fairness for the namespace.") s.Command.Flags().StringArrayVar(&s.SearchAttribute, "search-attribute", nil, "Custom search attribute as 'name=Type' (e.g. --search-attribute myAttr=Keyword). Valid types: Text, Keyword, Int, Double, Bool, Datetime, KeywordList. Repeat to add multiple.") @@ -3517,6 +3367,324 @@ func NewCloudNamespaceListCommand(cctx *CommandContext, parent *CloudNamespaceCo return &s } +type CloudNamespaceMtlsCommand struct { + Parent *CloudNamespaceCommand + Command cobra.Command +} + +func NewCloudNamespaceMtlsCommand(cctx *CommandContext, parent *CloudNamespaceCommand) *CloudNamespaceMtlsCommand { + var s CloudNamespaceMtlsCommand + s.Parent = parent + s.Command.Use = "mtls" + s.Command.Short = "Manage namespace mTLS authentication settings" + s.Command.Long = "Commands for managing mTLS authentication configuration of Temporal Cloud namespaces." + s.Command.Args = cobra.NoArgs + s.Command.AddCommand(&NewCloudNamespaceMtlsCertCaCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceMtlsCertFilterCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceMtlsGetCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceMtlsSetCommand(cctx, &s).Command) + return &s +} + +type CloudNamespaceMtlsCertCaCommand struct { + Parent *CloudNamespaceMtlsCommand + Command cobra.Command +} + +func NewCloudNamespaceMtlsCertCaCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCommand) *CloudNamespaceMtlsCertCaCommand { + var s CloudNamespaceMtlsCertCaCommand + s.Parent = parent + s.Command.Use = "cert-ca" + s.Command.Short = "Manage client CA certificates for namespaces" + s.Command.Long = "Commands for managing the client CA certificates of Temporal Cloud namespaces." + s.Command.Args = cobra.NoArgs + s.Command.AddCommand(&NewCloudNamespaceMtlsCertCaCreateCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceMtlsCertCaDeleteCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceMtlsCertCaListCommand(cctx, &s).Command) + return &s +} + +type CloudNamespaceMtlsCertCaCreateCommand struct { + Parent *CloudNamespaceMtlsCertCaCommand + Command cobra.Command + ClientOptions + NamespaceOptions + AsyncOperationOptions + ResourceVersionOptions + CaCertificateOptions +} + +func NewCloudNamespaceMtlsCertCaCreateCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCertCaCommand) *CloudNamespaceMtlsCertCaCreateCommand { + var s CloudNamespaceMtlsCertCaCreateCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "create [flags]" + s.Command.Short = "Add CA certificates to a namespace" + if hasHighlighting { + s.Command.Long = "Add client CA certificates to a Temporal Cloud namespace from a PEM file\nor base64 encoded string. These certificates are used to verify client\nconnections and enable mTLS authentication.\n\nSpecify either --ca-certificate-file or --ca-certificate, but not both.\n\nExample with file:\n\n\x1b[1mtemporal cloud namespace mtls cert-ca create --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem\x1b[0m\n\nExample with base64 encoded data:\n\n\x1b[1mtemporal cloud namespace mtls cert-ca create --namespace my-namespace.my-account --ca-certificate \x1b[0m" + } else { + s.Command.Long = "Add client CA certificates to a Temporal Cloud namespace from a PEM file\nor base64 encoded string. These certificates are used to verify client\nconnections and enable mTLS authentication.\n\nSpecify either --ca-certificate-file or --ca-certificate, but not both.\n\nExample with file:\n\n```\ntemporal cloud namespace mtls cert-ca create --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem\n```\n\nExample with base64 encoded data:\n\n```\ntemporal cloud namespace mtls cert-ca create --namespace my-namespace.my-account --ca-certificate \n```" + } + s.Command.Args = cobra.NoArgs + s.ClientOptions.BuildFlags(s.Command.Flags()) + s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) + s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) + s.CaCertificateOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type CloudNamespaceMtlsCertCaDeleteCommand struct { + Parent *CloudNamespaceMtlsCertCaCommand + Command cobra.Command + ClientOptions + NamespaceOptions + AsyncOperationOptions + ResourceVersionOptions + CaCertificateOptions +} + +func NewCloudNamespaceMtlsCertCaDeleteCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCertCaCommand) *CloudNamespaceMtlsCertCaDeleteCommand { + var s CloudNamespaceMtlsCertCaDeleteCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "delete [flags]" + s.Command.Short = "Delete CA certificates from a namespace" + if hasHighlighting { + s.Command.Long = "Delete client CA certificates from a Temporal Cloud namespace. This operation\nrequires confirmation and will remove the specified certificates from the\nnamespace configuration.\n\nSpecify either --ca-certificate-file or --ca-certificate, but not both.\n\nExample with file:\n\n\x1b[1mtemporal cloud namespace mtls cert-ca delete --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem\x1b[0m\n\nExample with base64 encoded data:\n\n\x1b[1mtemporal cloud namespace mtls cert-ca delete --namespace my-namespace.my-account --ca-certificate \x1b[0m" + } else { + s.Command.Long = "Delete client CA certificates from a Temporal Cloud namespace. This operation\nrequires confirmation and will remove the specified certificates from the\nnamespace configuration.\n\nSpecify either --ca-certificate-file or --ca-certificate, but not both.\n\nExample with file:\n\n```\ntemporal cloud namespace mtls cert-ca delete --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem\n```\n\nExample with base64 encoded data:\n\n```\ntemporal cloud namespace mtls cert-ca delete --namespace my-namespace.my-account --ca-certificate \n```" + } + s.Command.Args = cobra.NoArgs + s.ClientOptions.BuildFlags(s.Command.Flags()) + s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) + s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) + s.CaCertificateOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type CloudNamespaceMtlsCertCaListCommand struct { + Parent *CloudNamespaceMtlsCertCaCommand + Command cobra.Command + ClientOptions + NamespaceOptions +} + +func NewCloudNamespaceMtlsCertCaListCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCertCaCommand) *CloudNamespaceMtlsCertCaListCommand { + var s CloudNamespaceMtlsCertCaListCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "list [flags]" + s.Command.Short = "List CA certificates for a namespace" + if hasHighlighting { + s.Command.Long = "Retrieve the list of client CA certificates configured for a Temporal Cloud\nnamespace. These certificates are used for client authentication.\n\nExample:\n\n\x1b[1mtemporal cloud namespace mtls cert-ca list --namespace my-namespace.my-account\x1b[0m" + } else { + s.Command.Long = "Retrieve the list of client CA certificates configured for a Temporal Cloud\nnamespace. These certificates are used for client authentication.\n\nExample:\n\n```\ntemporal cloud namespace mtls cert-ca list --namespace my-namespace.my-account\n```" + } + s.Command.Args = cobra.NoArgs + s.ClientOptions.BuildFlags(s.Command.Flags()) + s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type CloudNamespaceMtlsCertFilterCommand struct { + Parent *CloudNamespaceMtlsCommand + Command cobra.Command +} + +func NewCloudNamespaceMtlsCertFilterCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCommand) *CloudNamespaceMtlsCertFilterCommand { + var s CloudNamespaceMtlsCertFilterCommand + s.Parent = parent + s.Command.Use = "cert-filter" + s.Command.Short = "Manage certificate filters for namespaces" + s.Command.Long = "Commands for managing certificate filters for Temporal Cloud namespaces.\nCertificate filters restrict mTLS connections to client certificates with\nspecific distinguished name properties." + s.Command.Args = cobra.NoArgs + s.Command.AddCommand(&NewCloudNamespaceMtlsCertFilterCreateCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceMtlsCertFilterDeleteCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceMtlsCertFilterListCommand(cctx, &s).Command) + return &s +} + +type CloudNamespaceMtlsCertFilterCreateCommand struct { + Parent *CloudNamespaceMtlsCertFilterCommand + Command cobra.Command + ClientOptions + NamespaceOptions + AsyncOperationOptions + ResourceVersionOptions + CommonName string + Organization string + OrganizationalUnit string + SubjectAlternativeName string +} + +func NewCloudNamespaceMtlsCertFilterCreateCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCertFilterCommand) *CloudNamespaceMtlsCertFilterCreateCommand { + var s CloudNamespaceMtlsCertFilterCreateCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "create [flags]" + s.Command.Short = "Add certificate filters to a namespace" + s.Command.Long = "Add new certificate filters to a Temporal Cloud namespace. Certificate\nfilters restrict mTLS connections to client certificates whose distinguished\nname properties match at least one of the filters." + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVar(&s.CommonName, "common-name", "", "The common name (CN) field from the certificate's distinguished name.") + s.Command.Flags().StringVar(&s.Organization, "organization", "", "The organization (O) field from the certificate's distinguished name.") + s.Command.Flags().StringVar(&s.OrganizationalUnit, "organizational-unit", "", "The organizational unit (OU) field from the certificate's distinguished name.") + s.Command.Flags().StringVar(&s.SubjectAlternativeName, "subject-alternative-name", "", "The subject alternative name (SAN) from the certificate.") + s.ClientOptions.BuildFlags(s.Command.Flags()) + s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) + s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type CloudNamespaceMtlsCertFilterDeleteCommand struct { + Parent *CloudNamespaceMtlsCertFilterCommand + Command cobra.Command + ClientOptions + NamespaceOptions + AsyncOperationOptions + ResourceVersionOptions + CommonName string + Organization string + OrganizationalUnit string + SubjectAlternativeName string +} + +func NewCloudNamespaceMtlsCertFilterDeleteCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCertFilterCommand) *CloudNamespaceMtlsCertFilterDeleteCommand { + var s CloudNamespaceMtlsCertFilterDeleteCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "delete [flags]" + s.Command.Short = "Delete certificate filters from a namespace" + s.Command.Long = "Delete certificate filters from a Temporal Cloud namespace. Filters are\nmatched by exact field equality." + s.Command.Args = cobra.NoArgs + s.Command.Flags().StringVar(&s.CommonName, "common-name", "", "The common name (CN) field from the certificate's distinguished name.") + s.Command.Flags().StringVar(&s.Organization, "organization", "", "The organization (O) field from the certificate's distinguished name.") + s.Command.Flags().StringVar(&s.OrganizationalUnit, "organizational-unit", "", "The organizational unit (OU) field from the certificate's distinguished name.") + s.Command.Flags().StringVar(&s.SubjectAlternativeName, "subject-alternative-name", "", "The subject alternative name (SAN) from the certificate.") + s.ClientOptions.BuildFlags(s.Command.Flags()) + s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) + s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type CloudNamespaceMtlsCertFilterListCommand struct { + Parent *CloudNamespaceMtlsCertFilterCommand + Command cobra.Command + ClientOptions + NamespaceOptions +} + +func NewCloudNamespaceMtlsCertFilterListCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCertFilterCommand) *CloudNamespaceMtlsCertFilterListCommand { + var s CloudNamespaceMtlsCertFilterListCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "list [flags]" + s.Command.Short = "List certificate filters for a namespace" + s.Command.Long = "List all certificate filters configured for a Temporal Cloud namespace." + s.Command.Args = cobra.NoArgs + s.ClientOptions.BuildFlags(s.Command.Flags()) + s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type CloudNamespaceMtlsGetCommand struct { + Parent *CloudNamespaceMtlsCommand + Command cobra.Command + ClientOptions + NamespaceOptions +} + +func NewCloudNamespaceMtlsGetCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCommand) *CloudNamespaceMtlsGetCommand { + var s CloudNamespaceMtlsGetCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "get [flags]" + s.Command.Short = "Get namespace mTLS authentication configuration" + if hasHighlighting { + s.Command.Long = "Retrieve the current mTLS authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace mtls get --namespace my-namespace.my-account\x1b[0m" + } else { + s.Command.Long = "Retrieve the current mTLS authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace mtls get --namespace my-namespace.my-account\n```" + } + s.Command.Args = cobra.NoArgs + s.ClientOptions.BuildFlags(s.Command.Flags()) + s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + +type CloudNamespaceMtlsSetCommand struct { + Parent *CloudNamespaceMtlsCommand + Command cobra.Command + ClientOptions + NamespaceOptions + AsyncOperationOptions + ResourceVersionOptions + MtlsAuthEnabled bool +} + +func NewCloudNamespaceMtlsSetCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCommand) *CloudNamespaceMtlsSetCommand { + var s CloudNamespaceMtlsSetCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "set [flags]" + s.Command.Short = "Set namespace mTLS authentication configuration" + if hasHighlighting { + s.Command.Long = "Enable or disable mTLS authentication for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace mtls set --namespace my-namespace.my-account --mtls-auth-enabled=true\x1b[0m" + } else { + s.Command.Long = "Enable or disable mTLS authentication for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace mtls set --namespace my-namespace.my-account --mtls-auth-enabled=true\n```" + } + s.Command.Args = cobra.NoArgs + s.Command.Flags().BoolVar(&s.MtlsAuthEnabled, "mtls-auth-enabled", false, "Enable or disable mTLS authentication for the namespace. Required.") + _ = cobra.MarkFlagRequired(s.Command.Flags(), "mtls-auth-enabled") + s.ClientOptions.BuildFlags(s.Command.Flags()) + s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) + s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + type CloudNamespaceRetentionCommand struct { Parent *CloudNamespaceCommand Command cobra.Command diff --git a/temporalcloudcli/commands.namespace.apikey.go b/temporalcloudcli/commands.namespace.apikey.go new file mode 100644 index 0000000..3077a38 --- /dev/null +++ b/temporalcloudcli/commands.namespace.apikey.go @@ -0,0 +1,74 @@ +package temporalcloudcli + +import ( + "errors" + + cloudservice "go.temporal.io/cloud-sdk/api/cloudservice/v1" + namespacev1 "go.temporal.io/cloud-sdk/api/namespace/v1" + "google.golang.org/protobuf/proto" + + "github.com/temporalio/cloud-cli/temporalcloudcli/internal/printer" +) + +func (c *CloudNamespaceApiKeyGetCommand) run(cctx *CommandContext, _ []string) error { + client, err := cctx.GetCloudClient(c.ClientOptions) + if err != nil { + return err + } + res, err := client.GetNamespace(cctx, &cloudservice.GetNamespaceRequest{Namespace: c.Namespace}) + if err != nil { + return err + } + + enabled := false + if res.Namespace.Spec.ApiKeyAuth != nil { + enabled = res.Namespace.Spec.ApiKeyAuth.Enabled + } + + result := struct { + Namespace string `json:"namespace"` + ApiKeyAuthEnabled bool `json:"apiKeyAuthEnabled"` + }{ + Namespace: res.Namespace.Namespace, + ApiKeyAuthEnabled: enabled, + } + return cctx.Printer.PrintStructured(result, printer.StructuredOptions{}) +} + +func (c *CloudNamespaceApiKeySetCommand) run(cctx *CommandContext, _ []string) error { + client, err := cctx.GetCloudClient(c.ClientOptions) + if err != nil { + return err + } + res, err := client.GetNamespace(cctx, &cloudservice.GetNamespaceRequest{Namespace: c.Namespace}) + if err != nil { + return err + } + + ns := res.Namespace + newSpec := proto.Clone(ns.Spec).(*namespacev1.NamespaceSpec) + if newSpec.ApiKeyAuth == nil { + newSpec.ApiKeyAuth = &namespacev1.ApiKeyAuthSpec{} + } + newSpec.ApiKeyAuth.Enabled = c.ApiKeyAuthEnabled + + yes, err := cctx.GetPrompter().PromptApply(ns.Spec, newSpec, false) + if err != nil { + return err + } + if !yes { + return errors.New("Aborting set.") + } + + rv := ns.ResourceVersion + if c.ResourceVersion != "" { + rv = c.ResourceVersion + } + resp, err := client.UpdateNamespace(cctx, &cloudservice.UpdateNamespaceRequest{ + Namespace: c.Namespace, + Spec: newSpec, + ResourceVersion: rv, + AsyncOperationId: c.AsyncOperationId, + }) + return cctx.GetPoller(client, c.AsyncOperationOptions).HandleUpdateOperation(cctx, resp, err) +} diff --git a/temporalcloudcli/commands.namespace.apikey_test.go b/temporalcloudcli/commands.namespace.apikey_test.go new file mode 100644 index 0000000..7298bc3 --- /dev/null +++ b/temporalcloudcli/commands.namespace.apikey_test.go @@ -0,0 +1,268 @@ +package temporalcloudcli_test + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/mock" + cloudservice "go.temporal.io/cloud-sdk/api/cloudservice/v1" + namespacev1 "go.temporal.io/cloud-sdk/api/namespace/v1" + operation "go.temporal.io/cloud-sdk/api/operation/v1" + "google.golang.org/protobuf/proto" + + cloudmock "github.com/temporalio/cloud-cli/internal/cloudservice/mock" + "github.com/temporalio/cloud-cli/temporalcloudcli" +) + +func TestNamespaceApikeyGet(t *testing.T) { + tests := []struct { + name string + cmd temporalcloudcli.CloudNamespaceApiKeyGetCommand + cloudClientExpectations func(*cloudmock.MockCloudServiceClient) + expectedErr string + expectedJsonOutput any + }{ + { + name: "Enabled", + cmd: temporalcloudcli.CloudNamespaceApiKeyGetCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, &cloudservice.GetNamespaceRequest{Namespace: "my-ns.my-acct"}, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: &namespacev1.Namespace{ + Namespace: "my-ns.my-acct", + Spec: &namespacev1.NamespaceSpec{ + ApiKeyAuth: &namespacev1.ApiKeyAuthSpec{Enabled: true}, + }, + }}, nil) + }, + expectedJsonOutput: map[string]any{ + "namespace": "my-ns.my-acct", + "apiKeyAuthEnabled": true, + }, + }, + { + name: "Disabled", + cmd: temporalcloudcli.CloudNamespaceApiKeyGetCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, &cloudservice.GetNamespaceRequest{Namespace: "my-ns.my-acct"}, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: &namespacev1.Namespace{ + Namespace: "my-ns.my-acct", + Spec: &namespacev1.NamespaceSpec{ + ApiKeyAuth: &namespacev1.ApiKeyAuthSpec{Enabled: false}, + }, + }}, nil) + }, + expectedJsonOutput: map[string]any{ + "namespace": "my-ns.my-acct", + "apiKeyAuthEnabled": false, + }, + }, + { + name: "NilApiKeyAuth", + cmd: temporalcloudcli.CloudNamespaceApiKeyGetCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, &cloudservice.GetNamespaceRequest{Namespace: "my-ns.my-acct"}, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: &namespacev1.Namespace{ + Namespace: "my-ns.my-acct", + Spec: &namespacev1.NamespaceSpec{}, + }}, nil) + }, + expectedJsonOutput: map[string]any{ + "namespace": "my-ns.my-acct", + "apiKeyAuthEnabled": false, + }, + }, + { + name: "GetNamespaceError", + cmd: temporalcloudcli.CloudNamespaceApiKeyGetCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(nil, errors.New("namespace not found")) + }, + expectedErr: "namespace not found", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + temporalcloudcli.TestCommand(t, &tt.cmd, temporalcloudcli.TestCommandOptions{ + CloudClientExpectations: tt.cloudClientExpectations, + JSONOutput: true, + ExpectedError: tt.expectedErr, + ExpectedOutputJson: tt.expectedJsonOutput, + }) + }) + } +} + +func TestNamespaceApiKeySet(t *testing.T) { + existingNS := func(apiKeyAuth *namespacev1.ApiKeyAuthSpec) *namespacev1.Namespace { + return &namespacev1.Namespace{ + Namespace: "my-ns.my-acct", + ResourceVersion: "rv-fetched", + Spec: &namespacev1.NamespaceSpec{ + Name: "my-ns", + Regions: []string{"aws-us-east-1"}, + RetentionDays: 30, + ApiKeyAuth: apiKeyAuth, + }, + } + } + + tests := []struct { + name string + cmd temporalcloudcli.CloudNamespaceApiKeySetCommand + cloudClientExpectations func(*cloudmock.MockCloudServiceClient) + promptOptions temporalcloudcli.TestPromptOptions + asyncPollerOptions temporalcloudcli.TestAsyncPollerOptions + expectedErr string + }{ + { + name: "EnableFromNil", + cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + ApiKeyAuthEnabled: true, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, &cloudservice.GetNamespaceRequest{Namespace: "my-ns.my-acct"}, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { + return req.Namespace == "my-ns.my-acct" && + req.ResourceVersion == "rv-fetched" && + proto.Equal(req.Spec.ApiKeyAuth, &namespacev1.ApiKeyAuthSpec{Enabled: true}) + }), mock.Anything). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-enable"}, + }, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-enable"}, + }, + { + name: "DisableFromEnabled", + cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + ApiKeyAuthEnabled: false, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{ + Namespace: existingNS(&namespacev1.ApiKeyAuthSpec{Enabled: true}), + }, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { + return proto.Equal(req.Spec.ApiKeyAuth, &namespacev1.ApiKeyAuthSpec{Enabled: false}) + }), mock.Anything). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-disable"}, + }, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-disable"}, + }, + { + name: "ResourceVersionOverride", + cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + ApiKeyAuthEnabled: true, + ResourceVersionOptions: temporalcloudcli.ResourceVersionOptions{ResourceVersion: "rv-user"}, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { + return req.ResourceVersion == "rv-user" + }), mock.Anything). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-rv"}, + }, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-rv"}, + }, + { + name: "AsyncOperationIdOverride", + cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + ApiKeyAuthEnabled: true, + AsyncOperationOptions: temporalcloudcli.AsyncOperationOptions{AsyncOperationId: "op-custom"}, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { + return req.AsyncOperationId == "op-custom" + }), mock.Anything). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-custom"}, + }, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-custom"}, + }, + { + name: "GetNamespaceError", + cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + ApiKeyAuthEnabled: true, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(nil, errors.New("namespace not found")) + }, + expectedErr: "namespace not found", + }, + { + name: "UpdateNamespaceError", + cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + ApiKeyAuthEnabled: true, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(nil, errors.New("update failed")) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + expectedErr: "update failed", + }, + { + name: "PromptDeclined", + cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + ApiKeyAuthEnabled: true, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: false}, + expectedErr: "Aborting set.", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + temporalcloudcli.TestCommand(t, &tt.cmd, temporalcloudcli.TestCommandOptions{ + CloudClientExpectations: tt.cloudClientExpectations, + PromptOptions: tt.promptOptions, + AsyncPollerOptions: tt.asyncPollerOptions, + JSONOutput: true, + ExpectedError: tt.expectedErr, + }) + }) + } +} diff --git a/temporalcloudcli/commands.namespace.cert_ca.go b/temporalcloudcli/commands.namespace.cert_ca.go index eb2294d..f5e6f63 100644 --- a/temporalcloudcli/commands.namespace.cert_ca.go +++ b/temporalcloudcli/commands.namespace.cert_ca.go @@ -11,7 +11,7 @@ import ( "github.com/temporalio/cloud-cli/temporalcloudcli/internal/printer" ) -func (c *CloudNamespaceCertCaCreateCommand) run(cctx *CommandContext, _ []string) error { +func (c *CloudNamespaceMtlsCertCaCreateCommand) run(cctx *CommandContext, _ []string) error { namespaceClient, err := getNamespaceClient(cctx, c.ClientOptions) if err != nil { return err @@ -31,7 +31,7 @@ func (c *CloudNamespaceCertCaCreateCommand) run(cctx *CommandContext, _ []string }) } -func (c *CloudNamespaceCertCaListCommand) run(cctx *CommandContext, _ []string) error { +func (c *CloudNamespaceMtlsCertCaListCommand) run(cctx *CommandContext, _ []string) error { namespaceClient, err := getNamespaceClient(cctx, c.ClientOptions) if err != nil { return err @@ -45,7 +45,7 @@ func (c *CloudNamespaceCertCaListCommand) run(cctx *CommandContext, _ []string) return cctx.Printer.PrintStructured(certs, printer.StructuredOptions{}) } -func (c *CloudNamespaceCertCaDeleteCommand) run(cctx *CommandContext, _ []string) error { +func (c *CloudNamespaceMtlsCertCaDeleteCommand) run(cctx *CommandContext, _ []string) error { namespaceClient, err := getNamespaceClient(cctx, c.ClientOptions) if err != nil { return err diff --git a/temporalcloudcli/commands.namespace.cert_ca_test.go b/temporalcloudcli/commands.namespace.cert_ca_test.go index ed7edf2..4b11463 100644 --- a/temporalcloudcli/commands.namespace.cert_ca_test.go +++ b/temporalcloudcli/commands.namespace.cert_ca_test.go @@ -66,12 +66,12 @@ func TestCloudNamespaceCertCaCreateCommand_Success(t *testing.T) { tests := []struct { name string - setupCmd func(*temporalcloudcli.CloudNamespaceCertCaCreateCommand) + setupCmd func(*temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) assertResult func(*testing.T, bytes.Buffer) }{ { name: "async with file", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.ResourceVersion = "test-version" @@ -93,7 +93,7 @@ func TestCloudNamespaceCertCaCreateCommand_Success(t *testing.T) { }, { name: "sync with file", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = false @@ -104,7 +104,7 @@ func TestCloudNamespaceCertCaCreateCommand_Success(t *testing.T) { }, { name: "async with base64", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificate = base64Cert cmd.ResourceVersion = "test-version" @@ -126,7 +126,7 @@ func TestCloudNamespaceCertCaCreateCommand_Success(t *testing.T) { }, { name: "sync with base64", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificate = base64Cert cmd.Async = false @@ -152,8 +152,8 @@ func TestCloudNamespaceCertCaCreateCommand_Success(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaCreateCommand(cctx, parent) tt.setupCmd(cmd) cmd.Command.Run(&cmd.Command, []string{}) @@ -164,15 +164,15 @@ func TestCloudNamespaceCertCaCreateCommand_Success(t *testing.T) { } } -func TestCloudNamespaceCertCaCreateCommand_InvalidInput(t *testing.T) { +func TestCloudNamespaceMtlsCertCaCreateCommand_InvalidInput(t *testing.T) { tests := []struct { name string - setupCmd func(*temporalcloudcli.CloudNamespaceCertCaCreateCommand) + setupCmd func(*temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) assertError func(*testing.T, error) }{ { name: "file not found", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = "testdata/nonexistent-cert.pem" cmd.Async = true @@ -183,7 +183,7 @@ func TestCloudNamespaceCertCaCreateCommand_InvalidInput(t *testing.T) { }, { name: "invalid certificate", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = "testdata/invalid-cert.pem" cmd.Async = true @@ -194,7 +194,7 @@ func TestCloudNamespaceCertCaCreateCommand_InvalidInput(t *testing.T) { }, { name: "both flags provided", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = "testdata/cert.pem" cmd.CaCertificate = "base64data" @@ -206,7 +206,7 @@ func TestCloudNamespaceCertCaCreateCommand_InvalidInput(t *testing.T) { }, { name: "neither flag provided", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.Async = true }, @@ -216,7 +216,7 @@ func TestCloudNamespaceCertCaCreateCommand_InvalidInput(t *testing.T) { }, { name: "invalid base64", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificate = "invalid!!!base64" cmd.Async = true @@ -242,8 +242,8 @@ func TestCloudNamespaceCertCaCreateCommand_InvalidInput(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaCreateCommand(cctx, parent) tt.setupCmd(cmd) cmd.Command.Run(&cmd.Command, []string{}) @@ -254,7 +254,7 @@ func TestCloudNamespaceCertCaCreateCommand_InvalidInput(t *testing.T) { } } -func TestCloudNamespaceCertCaCreateCommand_AddCACertsError(t *testing.T) { +func TestCloudNamespaceMtlsCertCaCreateCommand_AddCACertsError(t *testing.T) { parsedCerts, certPath, _ := setupTestCertFile(t) mockClient := cmdmock.NewMockNamespaceClient(t) @@ -283,8 +283,8 @@ func TestCloudNamespaceCertCaCreateCommand_AddCACertsError(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaCreateCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = true @@ -295,7 +295,7 @@ func TestCloudNamespaceCertCaCreateCommand_AddCACertsError(t *testing.T) { assert.Equal(t, expectedErr, capturedErr) } -func TestCloudNamespaceCertCaCreateCommand_NothingToChange(t *testing.T) { +func TestCloudNamespaceMtlsCertCaCreateCommand_NothingToChange(t *testing.T) { parsedCerts, certPath, _ := setupTestCertFile(t) nothingToChangeErr := status.Error(codes.InvalidArgument, "nothing to change") @@ -355,8 +355,8 @@ func TestCloudNamespaceCertCaCreateCommand_NothingToChange(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaCreateCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = true @@ -369,7 +369,7 @@ func TestCloudNamespaceCertCaCreateCommand_NothingToChange(t *testing.T) { } } -func TestCloudNamespaceCertCaCreateCommand_IdempotentWithOtherError(t *testing.T) { +func TestCloudNamespaceMtlsCertCaCreateCommand_IdempotentWithOtherError(t *testing.T) { parsedCerts, certPath, _ := setupTestCertFile(t) mockClient := cmdmock.NewMockNamespaceClient(t) @@ -398,8 +398,8 @@ func TestCloudNamespaceCertCaCreateCommand_IdempotentWithOtherError(t *testing.T capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaCreateCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = true @@ -410,7 +410,7 @@ func TestCloudNamespaceCertCaCreateCommand_IdempotentWithOtherError(t *testing.T assert.Equal(t, otherErr, capturedErr) } -func TestCloudNamespaceCertCaCreateCommand_PollingError(t *testing.T) { +func TestCloudNamespaceMtlsCertCaCreateCommand_PollingError(t *testing.T) { parsedCerts, certPath, _ := setupTestCertFile(t) mockClient := cmdmock.NewMockNamespaceClient(t) @@ -449,8 +449,8 @@ func TestCloudNamespaceCertCaCreateCommand_PollingError(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaCreateCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = false @@ -523,7 +523,7 @@ func setupTestCertFile(t *testing.T) ([]cert.CACert, string, []byte) { return parsedCerts, certPath, certData } -func TestCloudNamespaceCertCaDeleteCommand_Success(t *testing.T) { +func TestCloudNamespaceMtlsCertCaDeleteCommand_Success(t *testing.T) { parsedCerts, certPath, certData := setupTestCertFile(t) base64Cert := base64.StdEncoding.EncodeToString(certData) @@ -560,12 +560,12 @@ func TestCloudNamespaceCertCaDeleteCommand_Success(t *testing.T) { tests := []struct { name string - setupCmd func(*temporalcloudcli.CloudNamespaceCertCaDeleteCommand) + setupCmd func(*temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) assertResult func(*testing.T, bytes.Buffer) }{ { name: "async with file", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.ResourceVersion = "test-version" @@ -587,7 +587,7 @@ func TestCloudNamespaceCertCaDeleteCommand_Success(t *testing.T) { }, { name: "sync with file", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = false @@ -598,7 +598,7 @@ func TestCloudNamespaceCertCaDeleteCommand_Success(t *testing.T) { }, { name: "async with base64", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificate = base64Cert cmd.ResourceVersion = "test-version" @@ -620,7 +620,7 @@ func TestCloudNamespaceCertCaDeleteCommand_Success(t *testing.T) { }, { name: "sync with base64", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificate = base64Cert cmd.Async = false @@ -649,8 +649,8 @@ func TestCloudNamespaceCertCaDeleteCommand_Success(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaDeleteCommand(cctx, parent) tt.setupCmd(cmd) cmd.Command.Run(&cmd.Command, []string{}) @@ -664,12 +664,12 @@ func TestCloudNamespaceCertCaDeleteCommand_Success(t *testing.T) { func TestCloudNamespaceCertCaDeleteCommand_InvalidInput(t *testing.T) { tests := []struct { name string - setupCmd func(*temporalcloudcli.CloudNamespaceCertCaDeleteCommand) + setupCmd func(*temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) assertError func(*testing.T, error) }{ { name: "file not found", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = "testdata/nonexistent-cert.pem" cmd.Async = true @@ -680,7 +680,7 @@ func TestCloudNamespaceCertCaDeleteCommand_InvalidInput(t *testing.T) { }, { name: "both flags provided", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = "testdata/cert.pem" cmd.CaCertificate = "base64data" @@ -692,7 +692,7 @@ func TestCloudNamespaceCertCaDeleteCommand_InvalidInput(t *testing.T) { }, { name: "neither flag provided", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.Async = true }, @@ -702,7 +702,7 @@ func TestCloudNamespaceCertCaDeleteCommand_InvalidInput(t *testing.T) { }, { name: "invalid base64", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificate = "invalid!!!base64" cmd.Async = true @@ -713,7 +713,7 @@ func TestCloudNamespaceCertCaDeleteCommand_InvalidInput(t *testing.T) { }, { name: "invalid certificate", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertCaDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertCaDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = "testdata/invalid-cert.pem" cmd.Async = true @@ -742,8 +742,8 @@ func TestCloudNamespaceCertCaDeleteCommand_InvalidInput(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaDeleteCommand(cctx, parent) tt.setupCmd(cmd) cmd.Command.Run(&cmd.Command, []string{}) @@ -754,7 +754,7 @@ func TestCloudNamespaceCertCaDeleteCommand_InvalidInput(t *testing.T) { } } -func TestCloudNamespaceCertCaDeleteCommand_DeleteCACertsError(t *testing.T) { +func TestCloudNamespaceMtlsCertCaDeleteCommand_DeleteCACertsError(t *testing.T) { parsedCerts, certPath, _ := setupTestCertFile(t) mockClient := cmdmock.NewMockNamespaceClient(t) @@ -786,8 +786,8 @@ func TestCloudNamespaceCertCaDeleteCommand_DeleteCACertsError(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = true @@ -798,7 +798,7 @@ func TestCloudNamespaceCertCaDeleteCommand_DeleteCACertsError(t *testing.T) { assert.Equal(t, expectedErr, capturedErr) } -func TestCloudNamespaceCertCaDeleteCommand_NothingToChange(t *testing.T) { +func TestCloudNamespaceMtlsCertCaDeleteCommand_NothingToChange(t *testing.T) { parsedCerts, certPath, _ := setupTestCertFile(t) nothingToChangeErr := status.Error(codes.InvalidArgument, "nothing to change") @@ -861,8 +861,8 @@ func TestCloudNamespaceCertCaDeleteCommand_NothingToChange(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = true @@ -875,7 +875,7 @@ func TestCloudNamespaceCertCaDeleteCommand_NothingToChange(t *testing.T) { } } -func TestCloudNamespaceCertCaDeleteCommand_IdempotentWithOtherError(t *testing.T) { +func TestCloudNamespaceMtlsCertCaDeleteCommand_IdempotentWithOtherError(t *testing.T) { parsedCerts, certPath, _ := setupTestCertFile(t) mockClient := cmdmock.NewMockNamespaceClient(t) @@ -907,8 +907,8 @@ func TestCloudNamespaceCertCaDeleteCommand_IdempotentWithOtherError(t *testing.T capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = true @@ -919,7 +919,7 @@ func TestCloudNamespaceCertCaDeleteCommand_IdempotentWithOtherError(t *testing.T assert.Equal(t, otherErr, capturedErr) } -func TestCloudNamespaceCertCaDeleteCommand_PollingError(t *testing.T) { +func TestCloudNamespaceMtlsCertCaDeleteCommand_PollingError(t *testing.T) { parsedCerts, certPath, _ := setupTestCertFile(t) mockClient := cmdmock.NewMockNamespaceClient(t) @@ -961,8 +961,8 @@ func TestCloudNamespaceCertCaDeleteCommand_PollingError(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = false @@ -972,7 +972,7 @@ func TestCloudNamespaceCertCaDeleteCommand_PollingError(t *testing.T) { assert.Equal(t, pollErr, capturedErr) } -func TestCloudNamespaceCertCaDeleteCommand_UserDeclinesPrompt(t *testing.T) { +func TestCloudNamespaceMtlsCertCaDeleteCommand_UserDeclinesPrompt(t *testing.T) { _, certPath, _ := setupTestCertFile(t) mockClient := cmdmock.NewMockNamespaceClient(t) @@ -993,8 +993,8 @@ func TestCloudNamespaceCertCaDeleteCommand_UserDeclinesPrompt(t *testing.T) { // Simulate user declining the prompt by providing "n" as input cctx.Options.Stdin = bytes.NewBufferString("n\n") - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = true @@ -1004,7 +1004,7 @@ func TestCloudNamespaceCertCaDeleteCommand_UserDeclinesPrompt(t *testing.T) { assert.Contains(t, capturedErr.Error(), "Aborting delete") } -func TestCloudNamespaceCertCaDeleteCommand_JSONOutputWithoutAutoConfirm(t *testing.T) { +func TestCloudNamespaceMtlsCertCaDeleteCommand_JSONOutputWithoutAutoConfirm(t *testing.T) { _, certPath, _ := setupTestCertFile(t) mockClient := cmdmock.NewMockNamespaceClient(t) @@ -1024,8 +1024,8 @@ func TestCloudNamespaceCertCaDeleteCommand_JSONOutputWithoutAutoConfirm(t *testi capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertCaCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertCaDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertCaCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertCaDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CaCertificateFile = certPath cmd.Async = true diff --git a/temporalcloudcli/commands.namespace.cert_filter.go b/temporalcloudcli/commands.namespace.cert_filter.go index 60956e6..5749731 100644 --- a/temporalcloudcli/commands.namespace.cert_filter.go +++ b/temporalcloudcli/commands.namespace.cert_filter.go @@ -9,7 +9,7 @@ import ( "github.com/temporalio/cloud-cli/temporalcloudcli/internal/printer" ) -func (c *CloudNamespaceCertFilterCreateCommand) run(cctx *CommandContext, _ []string) error { +func (c *CloudNamespaceMtlsCertFilterCreateCommand) run(cctx *CommandContext, _ []string) error { namespaceClient, err := getNamespaceClient(cctx, c.ClientOptions) if err != nil { return err @@ -38,7 +38,7 @@ func (c *CloudNamespaceCertFilterCreateCommand) run(cctx *CommandContext, _ []st }) } -func (c *CloudNamespaceCertFilterListCommand) run(cctx *CommandContext, _ []string) error { +func (c *CloudNamespaceMtlsCertFilterListCommand) run(cctx *CommandContext, _ []string) error { namespaceClient, err := getNamespaceClient(cctx, c.ClientOptions) if err != nil { return err @@ -60,7 +60,7 @@ func (c *CloudNamespaceCertFilterListCommand) run(cctx *CommandContext, _ []stri ) } -func (c *CloudNamespaceCertFilterDeleteCommand) run(cctx *CommandContext, _ []string) error { +func (c *CloudNamespaceMtlsCertFilterDeleteCommand) run(cctx *CommandContext, _ []string) error { namespaceClient, err := getNamespaceClient(cctx, c.ClientOptions) if err != nil { return err diff --git a/temporalcloudcli/commands.namespace.cert_filter_test.go b/temporalcloudcli/commands.namespace.cert_filter_test.go index 506d77c..9797d8d 100644 --- a/temporalcloudcli/commands.namespace.cert_filter_test.go +++ b/temporalcloudcli/commands.namespace.cert_filter_test.go @@ -48,8 +48,8 @@ func TestCloudNamespaceCertFilterListCommand_Success(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterListCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterListCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.Command.Run(&cmd.Command, []string{}) @@ -67,7 +67,7 @@ func TestCloudNamespaceCertFilterListCommand_Success(t *testing.T) { assert.Equal(t, expected, result) } -func TestCloudNamespaceCertFilterListCommand_EmptyList(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterListCommand_EmptyList(t *testing.T) { mockClient := cmdmock.NewMockNamespaceClient(t) mockClient.EXPECT(). ListCertFilters(mock.Anything, "test-namespace.test-account"). @@ -85,8 +85,8 @@ func TestCloudNamespaceCertFilterListCommand_EmptyList(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterListCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterListCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.Command.Run(&cmd.Command, []string{}) @@ -100,7 +100,7 @@ func TestCloudNamespaceCertFilterListCommand_EmptyList(t *testing.T) { assert.Empty(t, result.CertificateFilters) } -func TestCloudNamespaceCertFilterListCommand_ListError(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterListCommand_ListError(t *testing.T) { expectedErr := errors.New("failed to list filters") mockClient := cmdmock.NewMockNamespaceClient(t) @@ -119,8 +119,8 @@ func TestCloudNamespaceCertFilterListCommand_ListError(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterListCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterListCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.Command.Run(&cmd.Command, []string{}) @@ -128,7 +128,7 @@ func TestCloudNamespaceCertFilterListCommand_ListError(t *testing.T) { assert.Equal(t, expectedErr, capturedErr) } -func TestCloudNamespaceCertFilterCreateCommand_Success(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterCreateCommand_Success(t *testing.T) { expectedOp := &operation.AsyncOperation{ Id: "test-operation-id", } @@ -172,12 +172,12 @@ func TestCloudNamespaceCertFilterCreateCommand_Success(t *testing.T) { tests := []struct { name string - setupCmd func(*temporalcloudcli.CloudNamespaceCertFilterCreateCommand) + setupCmd func(*temporalcloudcli.CloudNamespaceMtlsCertFilterCreateCommand) assertResult func(*testing.T, bytes.Buffer) }{ { name: "async", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertFilterCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertFilterCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Organization = "Temporal" @@ -200,7 +200,7 @@ func TestCloudNamespaceCertFilterCreateCommand_Success(t *testing.T) { }, { name: "sync", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertFilterCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertFilterCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Organization = "Temporal" @@ -230,8 +230,8 @@ func TestCloudNamespaceCertFilterCreateCommand_Success(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterCreateCommand(cctx, parent) tt.setupCmd(cmd) cmd.Command.Run(&cmd.Command, []string{}) @@ -242,15 +242,15 @@ func TestCloudNamespaceCertFilterCreateCommand_Success(t *testing.T) { } } -func TestCloudNamespaceCertFilterCreateCommand_InvalidInput(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterCreateCommand_InvalidInput(t *testing.T) { tests := []struct { name string - setupCmd func(*temporalcloudcli.CloudNamespaceCertFilterCreateCommand) + setupCmd func(*temporalcloudcli.CloudNamespaceMtlsCertFilterCreateCommand) assertError func(*testing.T, error) }{ { name: "no fields provided", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertFilterCreateCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertFilterCreateCommand) { cmd.Namespace = "test-namespace.test-account" cmd.Async = true }, @@ -278,8 +278,8 @@ func TestCloudNamespaceCertFilterCreateCommand_InvalidInput(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterCreateCommand(cctx, parent) tt.setupCmd(cmd) cmd.Command.Run(&cmd.Command, []string{}) @@ -290,7 +290,7 @@ func TestCloudNamespaceCertFilterCreateCommand_InvalidInput(t *testing.T) { } } -func TestCloudNamespaceCertFilterCreateCommand_AddCertFiltersError(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterCreateCommand_AddCertFiltersError(t *testing.T) { mockClient := cmdmock.NewMockNamespaceClient(t) expectedErr := errors.New("failed to add filters") @@ -324,8 +324,8 @@ func TestCloudNamespaceCertFilterCreateCommand_AddCertFiltersError(t *testing.T) capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterCreateCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = true @@ -406,8 +406,8 @@ func TestCloudNamespaceCertFilterCreateCommand_NothingToChange(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterCreateCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = true @@ -420,7 +420,7 @@ func TestCloudNamespaceCertFilterCreateCommand_NothingToChange(t *testing.T) { } } -func TestCloudNamespaceCertFilterCreateCommand_IdempotentWithOtherError(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterCreateCommand_IdempotentWithOtherError(t *testing.T) { mockClient := cmdmock.NewMockNamespaceClient(t) otherErr := status.Error(codes.PermissionDenied, "permission denied") @@ -454,8 +454,8 @@ func TestCloudNamespaceCertFilterCreateCommand_IdempotentWithOtherError(t *testi capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterCreateCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = true @@ -466,7 +466,7 @@ func TestCloudNamespaceCertFilterCreateCommand_IdempotentWithOtherError(t *testi assert.Equal(t, otherErr, capturedErr) } -func TestCloudNamespaceCertFilterCreateCommand_PollingError(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterCreateCommand_PollingError(t *testing.T) { mockClient := cmdmock.NewMockNamespaceClient(t) mockPoller := cmdmock.NewMockPoller(t) @@ -510,8 +510,8 @@ func TestCloudNamespaceCertFilterCreateCommand_PollingError(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterCreateCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = false @@ -521,7 +521,7 @@ func TestCloudNamespaceCertFilterCreateCommand_PollingError(t *testing.T) { assert.Equal(t, pollErr, capturedErr) } -func TestCloudNamespaceCertFilterCreateCommand_UserDeclinesPrompt(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterCreateCommand_UserDeclinesPrompt(t *testing.T) { mockClient := cmdmock.NewMockNamespaceClient(t) var buf bytes.Buffer @@ -540,8 +540,8 @@ func TestCloudNamespaceCertFilterCreateCommand_UserDeclinesPrompt(t *testing.T) // Simulate user declining the prompt by providing "n" as input cctx.Options.Stdin = bytes.NewBufferString("n\n") - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterCreateCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = true @@ -551,7 +551,7 @@ func TestCloudNamespaceCertFilterCreateCommand_UserDeclinesPrompt(t *testing.T) assert.Contains(t, capturedErr.Error(), "Aborting create") } -func TestCloudNamespaceCertFilterCreateCommand_JSONOutputWithoutAutoConfirm(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterCreateCommand_JSONOutputWithoutAutoConfirm(t *testing.T) { mockClient := cmdmock.NewMockNamespaceClient(t) var buf bytes.Buffer @@ -569,8 +569,8 @@ func TestCloudNamespaceCertFilterCreateCommand_JSONOutputWithoutAutoConfirm(t *t capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterCreateCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterCreateCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = true @@ -580,7 +580,7 @@ func TestCloudNamespaceCertFilterCreateCommand_JSONOutputWithoutAutoConfirm(t *t assert.Contains(t, capturedErr.Error(), "must bypass prompts when using JSON output") } -func TestCloudNamespaceCertFilterDeleteCommand_Success(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterDeleteCommand_Success(t *testing.T) { expectedOp := &operation.AsyncOperation{ Id: "test-operation-id", } @@ -624,12 +624,12 @@ func TestCloudNamespaceCertFilterDeleteCommand_Success(t *testing.T) { tests := []struct { name string - setupCmd func(*temporalcloudcli.CloudNamespaceCertFilterDeleteCommand) + setupCmd func(*temporalcloudcli.CloudNamespaceMtlsCertFilterDeleteCommand) assertResult func(*testing.T, bytes.Buffer) }{ { name: "async", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertFilterDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertFilterDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Organization = "Temporal" @@ -652,7 +652,7 @@ func TestCloudNamespaceCertFilterDeleteCommand_Success(t *testing.T) { }, { name: "sync", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertFilterDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertFilterDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Organization = "Temporal" @@ -682,8 +682,8 @@ func TestCloudNamespaceCertFilterDeleteCommand_Success(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterDeleteCommand(cctx, parent) tt.setupCmd(cmd) cmd.Command.Run(&cmd.Command, []string{}) @@ -694,15 +694,15 @@ func TestCloudNamespaceCertFilterDeleteCommand_Success(t *testing.T) { } } -func TestCloudNamespaceCertFilterDeleteCommand_InvalidInput(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterDeleteCommand_InvalidInput(t *testing.T) { tests := []struct { name string - setupCmd func(*temporalcloudcli.CloudNamespaceCertFilterDeleteCommand) + setupCmd func(*temporalcloudcli.CloudNamespaceMtlsCertFilterDeleteCommand) assertError func(*testing.T, error) }{ { name: "no fields provided", - setupCmd: func(cmd *temporalcloudcli.CloudNamespaceCertFilterDeleteCommand) { + setupCmd: func(cmd *temporalcloudcli.CloudNamespaceMtlsCertFilterDeleteCommand) { cmd.Namespace = "test-namespace.test-account" cmd.Async = true }, @@ -730,8 +730,8 @@ func TestCloudNamespaceCertFilterDeleteCommand_InvalidInput(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterDeleteCommand(cctx, parent) tt.setupCmd(cmd) cmd.Command.Run(&cmd.Command, []string{}) @@ -742,7 +742,7 @@ func TestCloudNamespaceCertFilterDeleteCommand_InvalidInput(t *testing.T) { } } -func TestCloudNamespaceCertFilterDeleteCommand_DeleteCertFiltersError(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterDeleteCommand_DeleteCertFiltersError(t *testing.T) { mockClient := cmdmock.NewMockNamespaceClient(t) expectedErr := errors.New("failed to remove filters") @@ -776,8 +776,8 @@ func TestCloudNamespaceCertFilterDeleteCommand_DeleteCertFiltersError(t *testing capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = true @@ -788,7 +788,7 @@ func TestCloudNamespaceCertFilterDeleteCommand_DeleteCertFiltersError(t *testing assert.Equal(t, expectedErr, capturedErr) } -func TestCloudNamespaceCertFilterDeleteCommand_NothingToChange(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterDeleteCommand_NothingToChange(t *testing.T) { nothingToChangeErr := status.Error(codes.InvalidArgument, "nothing to change") tests := []struct { @@ -858,8 +858,8 @@ func TestCloudNamespaceCertFilterDeleteCommand_NothingToChange(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = true @@ -872,7 +872,7 @@ func TestCloudNamespaceCertFilterDeleteCommand_NothingToChange(t *testing.T) { } } -func TestCloudNamespaceCertFilterDeleteCommand_IdempotentWithOtherError(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterDeleteCommand_IdempotentWithOtherError(t *testing.T) { mockClient := cmdmock.NewMockNamespaceClient(t) otherErr := status.Error(codes.PermissionDenied, "permission denied") @@ -906,8 +906,8 @@ func TestCloudNamespaceCertFilterDeleteCommand_IdempotentWithOtherError(t *testi capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = true @@ -962,8 +962,8 @@ func TestCloudNamespaceCertFilterDeleteCommand_PollingError(t *testing.T) { capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = false @@ -973,7 +973,7 @@ func TestCloudNamespaceCertFilterDeleteCommand_PollingError(t *testing.T) { assert.Equal(t, pollErr, capturedErr) } -func TestCloudNamespaceCertFilterDeleteCommand_UserDeclinesPrompt(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterDeleteCommand_UserDeclinesPrompt(t *testing.T) { mockClient := cmdmock.NewMockNamespaceClient(t) var buf bytes.Buffer @@ -992,8 +992,8 @@ func TestCloudNamespaceCertFilterDeleteCommand_UserDeclinesPrompt(t *testing.T) // Simulate user declining the prompt by providing "n" as input cctx.Options.Stdin = bytes.NewBufferString("n\n") - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = true @@ -1003,7 +1003,7 @@ func TestCloudNamespaceCertFilterDeleteCommand_UserDeclinesPrompt(t *testing.T) assert.Contains(t, capturedErr.Error(), "Aborting delete") } -func TestCloudNamespaceCertFilterDeleteCommand_JSONOutputWithoutAutoConfirm(t *testing.T) { +func TestCloudNamespaceMtlsCertFilterDeleteCommand_JSONOutputWithoutAutoConfirm(t *testing.T) { mockClient := cmdmock.NewMockNamespaceClient(t) var buf bytes.Buffer @@ -1021,8 +1021,8 @@ func TestCloudNamespaceCertFilterDeleteCommand_JSONOutputWithoutAutoConfirm(t *t capturedErr = err } - parent := &temporalcloudcli.CloudNamespaceCertFilterCommand{} - cmd := temporalcloudcli.NewCloudNamespaceCertFilterDeleteCommand(cctx, parent) + parent := &temporalcloudcli.CloudNamespaceMtlsCertFilterCommand{} + cmd := temporalcloudcli.NewCloudNamespaceMtlsCertFilterDeleteCommand(cctx, parent) cmd.Namespace = "test-namespace.test-account" cmd.CommonName = "test.temporal.io" cmd.Async = true diff --git a/temporalcloudcli/commands.namespace.create_test.go b/temporalcloudcli/commands.namespace.create_test.go index 48772d0..4114cfc 100644 --- a/temporalcloudcli/commands.namespace.create_test.go +++ b/temporalcloudcli/commands.namespace.create_test.go @@ -36,6 +36,7 @@ func baseNamespaceSpec() *namespacev1.NamespaceSpec { Name: "my-namespace", Regions: []string{"aws-us-east-1"}, ApiKeyAuth: &namespacev1.ApiKeyAuthSpec{Enabled: false}, + MtlsAuth: &namespacev1.MtlsAuthSpec{Enabled: false}, Lifecycle: &namespacev1.LifecycleSpec{EnableDeleteProtection: false}, } } @@ -93,6 +94,7 @@ func TestCreateNamespace_BuildsSpec(t *testing.T) { Lifecycle: &namespacev1.LifecycleSpec{EnableDeleteProtection: false}, Fairness: &namespacev1.FairnessSpec{TaskQueueFairnessEnabled: true}, MtlsAuth: &namespacev1.MtlsAuthSpec{ + Enabled: true, CertificateFilters: []*namespacev1.CertificateFilterSpec{ {CommonName: "test.temporal.io"}, {SubjectAlternativeName: "*.temporal.io"}, @@ -126,6 +128,7 @@ func TestCreateNamespace_BuildsSpec(t *testing.T) { Regions: []string{"aws-us-east-1"}, RetentionDays: 30, ApiKeyAuthEnabled: true, + MtlsAuthEnabled: true, EnableTaskQueueFairness: proto.Bool(true), CertificateFilterOptions: temporalcloudcli.CertificateFilterOptions{ CertificateFilter: []string{ @@ -190,6 +193,51 @@ func TestCreateNamespace_Fairness(t *testing.T) { } } +// TestCreateNamespace_MtlsAuthEnabled verifies the mtls-auth-enabled flag wires Enabled into MtlsAuthSpec. +func TestCreateNamespace_MtlsAuthEnabled(t *testing.T) { + tests := []struct { + name string + mtlsAuthEnabled bool + expectedMtlsAuth *namespacev1.MtlsAuthSpec + }{ + {name: "Disabled", mtlsAuthEnabled: false, expectedMtlsAuth: &namespacev1.MtlsAuthSpec{Enabled: false}}, + {name: "Enabled", mtlsAuthEnabled: true, expectedMtlsAuth: &namespacev1.MtlsAuthSpec{Enabled: true}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expectedSpec := baseNamespaceSpec() + expectedSpec.MtlsAuth = tt.expectedMtlsAuth + + mockCloud := cloudmock.NewMockCloudServiceClient(t) + mockPrompter := cmdmock.NewMockPrompter(t) + mockHandler := cmdmock.NewMockAsyncOperationHandler(t) + + mockPrompter.EXPECT(). + PromptApply(&namespacev1.NamespaceSpec{}, specMatcher(expectedSpec), false). + Return(nil) + mockCloud.EXPECT(). + CreateNamespace(context.Background(), createReqMatcher(expectedSpec)). + Return(defaultCreateResponse, nil) + mockHandler.EXPECT(). + HandleOperation(defaultCreateResponse.AsyncOperation, "my-namespace.my-account"). + Return(nil) + + var buf bytes.Buffer + err := temporalcloudcli.CreateNamespace(context.Background(), temporalcloudcli.CreateNamespaceParams{ + Name: "my-namespace", + Regions: []string{"aws-us-east-1"}, + MtlsAuthEnabled: tt.mtlsAuthEnabled, + Cloud: mockCloud, + Printer: &printer.Printer{Output: &buf, JSON: true}, + Prompter: mockPrompter, + UnmarshalProtoJSON: noopUnmarshalProtoJSON, + OperationHandler: mockHandler, + }) + require.NoError(t, err) + }) + } +} + // TestCreateNamespace_Error verifies that an API error is forwarded to HandleCreateErr. func TestCreateNamespace_Error(t *testing.T) { mockCloud := cloudmock.NewMockCloudServiceClient(t) diff --git a/temporalcloudcli/commands.namespace.go b/temporalcloudcli/commands.namespace.go index 31ee48f..d416080 100644 --- a/temporalcloudcli/commands.namespace.go +++ b/temporalcloudcli/commands.namespace.go @@ -313,6 +313,7 @@ type ( Regions []string RetentionDays int32 ApiKeyAuthEnabled bool + MtlsAuthEnabled bool EnableDeleteProtection bool EnableTaskQueueFairness *bool AsyncOperationID string @@ -385,6 +386,7 @@ func CreateNamespace(ctx context.Context, params CreateNamespaceParams) error { Regions: params.Regions, RetentionDays: params.RetentionDays, ApiKeyAuth: &namespacev1.ApiKeyAuthSpec{Enabled: params.ApiKeyAuthEnabled}, + MtlsAuth: &namespacev1.MtlsAuthSpec{Enabled: params.MtlsAuthEnabled}, Lifecycle: &namespacev1.LifecycleSpec{EnableDeleteProtection: params.EnableDeleteProtection}, ConnectivityRuleIds: params.ConnectionRuleIDs, } @@ -396,12 +398,9 @@ func CreateNamespace(ctx context.Context, params CreateNamespaceParams) error { } if len(certBytes) > 0 { - spec.MtlsAuth = &namespacev1.MtlsAuthSpec{AcceptedClientCa: certBytes} + spec.MtlsAuth.AcceptedClientCa = certBytes } if len(certFilters) > 0 { - if spec.MtlsAuth == nil { - spec.MtlsAuth = &namespacev1.MtlsAuthSpec{} - } spec.MtlsAuth.CertificateFilters = certFilters } if params.CodecEndpoint != "" { @@ -447,6 +446,7 @@ func (c *CloudNamespaceCreateCommand) run(cctx *CommandContext, _ []string) erro Regions: c.Region, RetentionDays: int32(c.RetentionDays), ApiKeyAuthEnabled: c.ApiKeyAuthEnabled, + MtlsAuthEnabled: c.MtlsAuthEnabled, EnableDeleteProtection: c.EnableDeleteProtection, EnableTaskQueueFairness: enableTaskQueueFairness, AsyncOperationID: c.AsyncOperationId, diff --git a/temporalcloudcli/commands.namespace.mtls.go b/temporalcloudcli/commands.namespace.mtls.go new file mode 100644 index 0000000..c9bf33b --- /dev/null +++ b/temporalcloudcli/commands.namespace.mtls.go @@ -0,0 +1,74 @@ +package temporalcloudcli + +import ( + "errors" + + cloudservice "go.temporal.io/cloud-sdk/api/cloudservice/v1" + namespacev1 "go.temporal.io/cloud-sdk/api/namespace/v1" + "google.golang.org/protobuf/proto" + + "github.com/temporalio/cloud-cli/temporalcloudcli/internal/printer" +) + +func (c *CloudNamespaceMtlsGetCommand) run(cctx *CommandContext, _ []string) error { + client, err := cctx.GetCloudClient(c.ClientOptions) + if err != nil { + return err + } + res, err := client.GetNamespace(cctx, &cloudservice.GetNamespaceRequest{Namespace: c.Namespace}) + if err != nil { + return err + } + + enabled := false + if res.Namespace.Spec.MtlsAuth != nil { + enabled = res.Namespace.Spec.MtlsAuth.Enabled + } + + result := struct { + Namespace string `json:"namespace"` + MtlsAuthEnabled bool `json:"mtlsAuthEnabled"` + }{ + Namespace: res.Namespace.Namespace, + MtlsAuthEnabled: enabled, + } + return cctx.Printer.PrintStructured(result, printer.StructuredOptions{}) +} + +func (c *CloudNamespaceMtlsSetCommand) run(cctx *CommandContext, _ []string) error { + client, err := cctx.GetCloudClient(c.ClientOptions) + if err != nil { + return err + } + res, err := client.GetNamespace(cctx, &cloudservice.GetNamespaceRequest{Namespace: c.Namespace}) + if err != nil { + return err + } + + ns := res.Namespace + newSpec := proto.Clone(ns.Spec).(*namespacev1.NamespaceSpec) + if newSpec.MtlsAuth == nil { + newSpec.MtlsAuth = &namespacev1.MtlsAuthSpec{} + } + newSpec.MtlsAuth.Enabled = c.MtlsAuthEnabled + + yes, err := cctx.GetPrompter().PromptApply(ns.Spec, newSpec, false) + if err != nil { + return err + } + if !yes { + return errors.New("Aborting set.") + } + + rv := ns.ResourceVersion + if c.ResourceVersion != "" { + rv = c.ResourceVersion + } + resp, err := client.UpdateNamespace(cctx, &cloudservice.UpdateNamespaceRequest{ + Namespace: c.Namespace, + Spec: newSpec, + ResourceVersion: rv, + AsyncOperationId: c.AsyncOperationId, + }) + return cctx.GetPoller(client, c.AsyncOperationOptions).HandleUpdateOperation(cctx, resp, err) +} diff --git a/temporalcloudcli/commands.namespace.mtls_test.go b/temporalcloudcli/commands.namespace.mtls_test.go new file mode 100644 index 0000000..f24aa79 --- /dev/null +++ b/temporalcloudcli/commands.namespace.mtls_test.go @@ -0,0 +1,268 @@ +package temporalcloudcli_test + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/mock" + cloudservice "go.temporal.io/cloud-sdk/api/cloudservice/v1" + namespacev1 "go.temporal.io/cloud-sdk/api/namespace/v1" + operation "go.temporal.io/cloud-sdk/api/operation/v1" + "google.golang.org/protobuf/proto" + + cloudmock "github.com/temporalio/cloud-cli/internal/cloudservice/mock" + "github.com/temporalio/cloud-cli/temporalcloudcli" +) + +func TestNamespaceMtlsGet(t *testing.T) { + tests := []struct { + name string + cmd temporalcloudcli.CloudNamespaceMtlsGetCommand + cloudClientExpectations func(*cloudmock.MockCloudServiceClient) + expectedErr string + expectedJsonOutput any + }{ + { + name: "Enabled", + cmd: temporalcloudcli.CloudNamespaceMtlsGetCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, &cloudservice.GetNamespaceRequest{Namespace: "my-ns.my-acct"}, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: &namespacev1.Namespace{ + Namespace: "my-ns.my-acct", + Spec: &namespacev1.NamespaceSpec{ + MtlsAuth: &namespacev1.MtlsAuthSpec{Enabled: true}, + }, + }}, nil) + }, + expectedJsonOutput: map[string]any{ + "namespace": "my-ns.my-acct", + "mtlsAuthEnabled": true, + }, + }, + { + name: "Disabled", + cmd: temporalcloudcli.CloudNamespaceMtlsGetCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, &cloudservice.GetNamespaceRequest{Namespace: "my-ns.my-acct"}, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: &namespacev1.Namespace{ + Namespace: "my-ns.my-acct", + Spec: &namespacev1.NamespaceSpec{ + MtlsAuth: &namespacev1.MtlsAuthSpec{Enabled: false}, + }, + }}, nil) + }, + expectedJsonOutput: map[string]any{ + "namespace": "my-ns.my-acct", + "mtlsAuthEnabled": false, + }, + }, + { + name: "NilMtlsAuth", + cmd: temporalcloudcli.CloudNamespaceMtlsGetCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, &cloudservice.GetNamespaceRequest{Namespace: "my-ns.my-acct"}, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: &namespacev1.Namespace{ + Namespace: "my-ns.my-acct", + Spec: &namespacev1.NamespaceSpec{}, + }}, nil) + }, + expectedJsonOutput: map[string]any{ + "namespace": "my-ns.my-acct", + "mtlsAuthEnabled": false, + }, + }, + { + name: "GetNamespaceError", + cmd: temporalcloudcli.CloudNamespaceMtlsGetCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(nil, errors.New("namespace not found")) + }, + expectedErr: "namespace not found", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + temporalcloudcli.TestCommand(t, &tt.cmd, temporalcloudcli.TestCommandOptions{ + CloudClientExpectations: tt.cloudClientExpectations, + JSONOutput: true, + ExpectedError: tt.expectedErr, + ExpectedOutputJson: tt.expectedJsonOutput, + }) + }) + } +} + +func TestNamespaceMtlsSet(t *testing.T) { + existingNS := func(mtlsAuth *namespacev1.MtlsAuthSpec) *namespacev1.Namespace { + return &namespacev1.Namespace{ + Namespace: "my-ns.my-acct", + ResourceVersion: "rv-fetched", + Spec: &namespacev1.NamespaceSpec{ + Name: "my-ns", + Regions: []string{"aws-us-east-1"}, + RetentionDays: 30, + MtlsAuth: mtlsAuth, + }, + } + } + + tests := []struct { + name string + cmd temporalcloudcli.CloudNamespaceMtlsSetCommand + cloudClientExpectations func(*cloudmock.MockCloudServiceClient) + promptOptions temporalcloudcli.TestPromptOptions + asyncPollerOptions temporalcloudcli.TestAsyncPollerOptions + expectedErr string + }{ + { + name: "EnableFromNil", + cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + MtlsAuthEnabled: true, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, &cloudservice.GetNamespaceRequest{Namespace: "my-ns.my-acct"}, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { + return req.Namespace == "my-ns.my-acct" && + req.ResourceVersion == "rv-fetched" && + proto.Equal(req.Spec.MtlsAuth, &namespacev1.MtlsAuthSpec{Enabled: true}) + }), mock.Anything). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-enable"}, + }, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-enable"}, + }, + { + name: "DisableFromEnabled", + cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + MtlsAuthEnabled: false, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{ + Namespace: existingNS(&namespacev1.MtlsAuthSpec{Enabled: true}), + }, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { + return proto.Equal(req.Spec.MtlsAuth, &namespacev1.MtlsAuthSpec{Enabled: false}) + }), mock.Anything). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-disable"}, + }, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-disable"}, + }, + { + name: "ResourceVersionOverride", + cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + MtlsAuthEnabled: true, + ResourceVersionOptions: temporalcloudcli.ResourceVersionOptions{ResourceVersion: "rv-user"}, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { + return req.ResourceVersion == "rv-user" + }), mock.Anything). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-rv"}, + }, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-rv"}, + }, + { + name: "AsyncOperationIdOverride", + cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + MtlsAuthEnabled: true, + AsyncOperationOptions: temporalcloudcli.AsyncOperationOptions{AsyncOperationId: "op-custom"}, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { + return req.AsyncOperationId == "op-custom" + }), mock.Anything). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-custom"}, + }, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-custom"}, + }, + { + name: "GetNamespaceError", + cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + MtlsAuthEnabled: true, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(nil, errors.New("namespace not found")) + }, + expectedErr: "namespace not found", + }, + { + name: "UpdateNamespaceError", + cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + MtlsAuthEnabled: true, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(nil, errors.New("update failed")) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + expectedErr: "update failed", + }, + { + name: "PromptDeclined", + cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ + NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, + MtlsAuthEnabled: true, + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: false}, + expectedErr: "Aborting set.", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + temporalcloudcli.TestCommand(t, &tt.cmd, temporalcloudcli.TestCommandOptions{ + CloudClientExpectations: tt.cloudClientExpectations, + PromptOptions: tt.promptOptions, + AsyncPollerOptions: tt.asyncPollerOptions, + JSONOutput: true, + ExpectedError: tt.expectedErr, + }) + }) + } +} diff --git a/temporalcloudcli/commands.yml b/temporalcloudcli/commands.yml index 38d0072..bdd2978 100644 --- a/temporalcloudcli/commands.yml +++ b/temporalcloudcli/commands.yml @@ -404,6 +404,10 @@ commands: type: bool description: | Enable API key authentication for the namespace. + - name: mtls-auth-enabled + type: bool + description: | + Enable mTLS authentication for the namespace. - name: enable-delete-protection type: bool description: | @@ -596,13 +600,99 @@ commands: description: | Enable or disable task queue fairness for the namespace. - # Namespace cert-ca commands - - name: cloud namespace cert-ca + # Namespace mtls commands + - name: cloud namespace mtls + summary: Manage namespace mTLS authentication settings + description: | + Commands for managing mTLS authentication configuration of Temporal Cloud namespaces. + has-init: false + - name: cloud namespace mtls get + summary: Get namespace mTLS authentication configuration + description: | + Retrieve the current mTLS authentication configuration for a Temporal Cloud namespace. + + Example: + + ``` + temporal cloud namespace mtls get --namespace my-namespace.my-account + ``` + has-init: false + option-sets: + - client + - namespace + - name: cloud namespace mtls set + summary: Set namespace mTLS authentication configuration + description: | + Enable or disable mTLS authentication for a Temporal Cloud namespace. + + Example: + + ``` + temporal cloud namespace mtls set --namespace my-namespace.my-account --mtls-auth-enabled=true + ``` + has-init: false + option-sets: + - client + - namespace + - async-operation + - resource-version + options: + - name: mtls-auth-enabled + type: bool + required: true + description: | + Enable or disable mTLS authentication for the namespace. + + # Namespace api-key auth commands + - name: cloud namespace api-key + summary: Manage namespace API key authentication settings + description: | + Commands for managing API key authentication configuration of Temporal Cloud namespaces. + has-init: false + - name: cloud namespace api-key get + summary: Get namespace API key authentication configuration + description: | + Retrieve the current API key authentication configuration for a Temporal Cloud namespace. + + Example: + + ``` + temporal cloud namespace api-key get --namespace my-namespace.my-account + ``` + has-init: false + option-sets: + - client + - namespace + - name: cloud namespace api-key set + summary: Set namespace API key authentication configuration + description: | + Enable or disable API key authentication for a Temporal Cloud namespace. + + Example: + + ``` + temporal cloud namespace api-key set --namespace my-namespace.my-account --api-key-auth-enabled=true + ``` + has-init: false + option-sets: + - client + - namespace + - async-operation + - resource-version + options: + - name: api-key-auth-enabled + type: bool + required: true + description: | + Enable or disable API key authentication for the namespace. + + # Namespace mtls cert-ca commands + - name: cloud namespace mtls cert-ca summary: Manage client CA certificates for namespaces description: | Commands for managing the client CA certificates of Temporal Cloud namespaces. has-init: false - - name: cloud namespace cert-ca list + - name: cloud namespace mtls cert-ca list summary: List CA certificates for a namespace description: | Retrieve the list of client CA certificates configured for a Temporal Cloud @@ -611,13 +701,13 @@ commands: Example: ``` - temporal cloud namespace cert-ca list --namespace my-namespace.my-account + temporal cloud namespace mtls cert-ca list --namespace my-namespace.my-account ``` has-init: false option-sets: - client - namespace - - name: cloud namespace cert-ca create + - name: cloud namespace mtls cert-ca create summary: Add CA certificates to a namespace description: | Add client CA certificates to a Temporal Cloud namespace from a PEM file @@ -629,13 +719,13 @@ commands: Example with file: ``` - temporal cloud namespace cert-ca create --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem + temporal cloud namespace mtls cert-ca create --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem ``` Example with base64 encoded data: ``` - temporal cloud namespace cert-ca create --namespace my-namespace.my-account --ca-certificate + temporal cloud namespace mtls cert-ca create --namespace my-namespace.my-account --ca-certificate ``` has-init: false option-sets: @@ -645,7 +735,7 @@ commands: - resource-version - ca-certificate - - name: cloud namespace cert-ca delete + - name: cloud namespace mtls cert-ca delete summary: Delete CA certificates from a namespace description: | Delete client CA certificates from a Temporal Cloud namespace. This operation @@ -657,13 +747,13 @@ commands: Example with file: ``` - temporal cloud namespace cert-ca delete --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem + temporal cloud namespace mtls cert-ca delete --namespace my-namespace.my-account --ca-certificate-file ca-cert.pem ``` Example with base64 encoded data: ``` - temporal cloud namespace cert-ca delete --namespace my-namespace.my-account --ca-certificate + temporal cloud namespace mtls cert-ca delete --namespace my-namespace.my-account --ca-certificate ``` has-init: false option-sets: @@ -674,14 +764,14 @@ commands: - ca-certificate # Namespace cert-filter commands - - name: cloud namespace cert-filter + - name: cloud namespace mtls cert-filter summary: Manage certificate filters for namespaces description: | Commands for managing certificate filters for Temporal Cloud namespaces. Certificate filters restrict mTLS connections to client certificates with specific distinguished name properties. has-init: false - - name: cloud namespace cert-filter list + - name: cloud namespace mtls cert-filter list summary: List certificate filters for a namespace description: | List all certificate filters configured for a Temporal Cloud namespace. @@ -689,7 +779,7 @@ commands: option-sets: - client - namespace - - name: cloud namespace cert-filter create + - name: cloud namespace mtls cert-filter create summary: Add certificate filters to a namespace description: | Add new certificate filters to a Temporal Cloud namespace. Certificate @@ -717,7 +807,7 @@ commands: type: string description: | The subject alternative name (SAN) from the certificate. - - name: cloud namespace cert-filter delete + - name: cloud namespace mtls cert-filter delete summary: Delete certificate filters from a namespace description: | Delete certificate filters from a Temporal Cloud namespace. Filters are From 9c828edd9d7197d44556cbaa18d16b8c8ac36341 Mon Sep 17 00:00:00 2001 From: David Liu Date: Thu, 25 Jun 2026 15:29:04 -0400 Subject: [PATCH 2/2] consistency with export --- temporalcloudcli/commands.gen.go | 134 +++++++++++----- temporalcloudcli/commands.namespace.apikey.go | 28 ++-- .../commands.namespace.apikey_test.go | 144 +++++++++--------- temporalcloudcli/commands.namespace.mtls.go | 59 ++++--- .../commands.namespace.mtls_test.go | 135 ++++++++-------- temporalcloudcli/commands.yml | 60 +++++--- 6 files changed, 330 insertions(+), 230 deletions(-) diff --git a/temporalcloudcli/commands.gen.go b/temporalcloudcli/commands.gen.go index fbe25a2..41ffeff 100644 --- a/temporalcloudcli/commands.gen.go +++ b/temporalcloudcli/commands.gen.go @@ -1872,32 +1872,37 @@ func NewCloudNamespaceApiKeyCommand(cctx *CommandContext, parent *CloudNamespace s.Command.Short = "Manage namespace API key authentication settings" s.Command.Long = "Commands for managing API key authentication configuration of Temporal Cloud namespaces." s.Command.Args = cobra.NoArgs + s.Command.AddCommand(&NewCloudNamespaceApiKeyDisableCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceApiKeyEnableCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceApiKeyGetCommand(cctx, &s).Command) - s.Command.AddCommand(&NewCloudNamespaceApiKeySetCommand(cctx, &s).Command) return &s } -type CloudNamespaceApiKeyGetCommand struct { +type CloudNamespaceApiKeyDisableCommand struct { Parent *CloudNamespaceApiKeyCommand Command cobra.Command ClientOptions NamespaceOptions + AsyncOperationOptions + ResourceVersionOptions } -func NewCloudNamespaceApiKeyGetCommand(cctx *CommandContext, parent *CloudNamespaceApiKeyCommand) *CloudNamespaceApiKeyGetCommand { - var s CloudNamespaceApiKeyGetCommand +func NewCloudNamespaceApiKeyDisableCommand(cctx *CommandContext, parent *CloudNamespaceApiKeyCommand) *CloudNamespaceApiKeyDisableCommand { + var s CloudNamespaceApiKeyDisableCommand s.Parent = parent s.Command.DisableFlagsInUseLine = true - s.Command.Use = "get [flags]" - s.Command.Short = "Get namespace API key authentication configuration" + s.Command.Use = "disable [flags]" + s.Command.Short = "Disable API key authentication for a namespace" if hasHighlighting { - s.Command.Long = "Retrieve the current API key authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace api-key get --namespace my-namespace.my-account\x1b[0m" + s.Command.Long = "Disable API key authentication for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace api-key disable --namespace my-namespace.my-account\x1b[0m" } else { - s.Command.Long = "Retrieve the current API key authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace api-key get --namespace my-namespace.my-account\n```" + s.Command.Long = "Disable API key authentication for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace api-key disable --namespace my-namespace.my-account\n```" } s.Command.Args = cobra.NoArgs s.ClientOptions.BuildFlags(s.Command.Flags()) s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) + s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) s.Command.Run = func(c *cobra.Command, args []string) { if err := s.run(cctx, args); err != nil { cctx.Options.Fail(err) @@ -1906,30 +1911,27 @@ func NewCloudNamespaceApiKeyGetCommand(cctx *CommandContext, parent *CloudNamesp return &s } -type CloudNamespaceApiKeySetCommand struct { +type CloudNamespaceApiKeyEnableCommand struct { Parent *CloudNamespaceApiKeyCommand Command cobra.Command ClientOptions NamespaceOptions AsyncOperationOptions ResourceVersionOptions - ApiKeyAuthEnabled bool } -func NewCloudNamespaceApiKeySetCommand(cctx *CommandContext, parent *CloudNamespaceApiKeyCommand) *CloudNamespaceApiKeySetCommand { - var s CloudNamespaceApiKeySetCommand +func NewCloudNamespaceApiKeyEnableCommand(cctx *CommandContext, parent *CloudNamespaceApiKeyCommand) *CloudNamespaceApiKeyEnableCommand { + var s CloudNamespaceApiKeyEnableCommand s.Parent = parent s.Command.DisableFlagsInUseLine = true - s.Command.Use = "set [flags]" - s.Command.Short = "Set namespace API key authentication configuration" + s.Command.Use = "enable [flags]" + s.Command.Short = "Enable API key authentication for a namespace" if hasHighlighting { - s.Command.Long = "Enable or disable API key authentication for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace api-key set --namespace my-namespace.my-account --api-key-auth-enabled=true\x1b[0m" + s.Command.Long = "Enable API key authentication for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace api-key enable --namespace my-namespace.my-account\x1b[0m" } else { - s.Command.Long = "Enable or disable API key authentication for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace api-key set --namespace my-namespace.my-account --api-key-auth-enabled=true\n```" + s.Command.Long = "Enable API key authentication for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace api-key enable --namespace my-namespace.my-account\n```" } s.Command.Args = cobra.NoArgs - s.Command.Flags().BoolVar(&s.ApiKeyAuthEnabled, "api-key-auth-enabled", false, "Enable or disable API key authentication for the namespace. Required.") - _ = cobra.MarkFlagRequired(s.Command.Flags(), "api-key-auth-enabled") s.ClientOptions.BuildFlags(s.Command.Flags()) s.NamespaceOptions.BuildFlags(s.Command.Flags()) s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) @@ -1942,6 +1944,35 @@ func NewCloudNamespaceApiKeySetCommand(cctx *CommandContext, parent *CloudNamesp return &s } +type CloudNamespaceApiKeyGetCommand struct { + Parent *CloudNamespaceApiKeyCommand + Command cobra.Command + ClientOptions + NamespaceOptions +} + +func NewCloudNamespaceApiKeyGetCommand(cctx *CommandContext, parent *CloudNamespaceApiKeyCommand) *CloudNamespaceApiKeyGetCommand { + var s CloudNamespaceApiKeyGetCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "get [flags]" + s.Command.Short = "Get namespace API key authentication configuration" + if hasHighlighting { + s.Command.Long = "Retrieve the current API key authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace api-key get --namespace my-namespace.my-account\x1b[0m" + } else { + s.Command.Long = "Retrieve the current API key authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace api-key get --namespace my-namespace.my-account\n```" + } + s.Command.Args = cobra.NoArgs + s.ClientOptions.BuildFlags(s.Command.Flags()) + s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + type CloudNamespaceApplyCommand struct { Parent *CloudNamespaceCommand Command cobra.Command @@ -3381,8 +3412,9 @@ func NewCloudNamespaceMtlsCommand(cctx *CommandContext, parent *CloudNamespaceCo s.Command.Args = cobra.NoArgs s.Command.AddCommand(&NewCloudNamespaceMtlsCertCaCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceMtlsCertFilterCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceMtlsDisableCommand(cctx, &s).Command) + s.Command.AddCommand(&NewCloudNamespaceMtlsEnableCommand(cctx, &s).Command) s.Command.AddCommand(&NewCloudNamespaceMtlsGetCommand(cctx, &s).Command) - s.Command.AddCommand(&NewCloudNamespaceMtlsSetCommand(cctx, &s).Command) return &s } @@ -3620,27 +3652,31 @@ func NewCloudNamespaceMtlsCertFilterListCommand(cctx *CommandContext, parent *Cl return &s } -type CloudNamespaceMtlsGetCommand struct { +type CloudNamespaceMtlsDisableCommand struct { Parent *CloudNamespaceMtlsCommand Command cobra.Command ClientOptions NamespaceOptions + AsyncOperationOptions + ResourceVersionOptions } -func NewCloudNamespaceMtlsGetCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCommand) *CloudNamespaceMtlsGetCommand { - var s CloudNamespaceMtlsGetCommand +func NewCloudNamespaceMtlsDisableCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCommand) *CloudNamespaceMtlsDisableCommand { + var s CloudNamespaceMtlsDisableCommand s.Parent = parent s.Command.DisableFlagsInUseLine = true - s.Command.Use = "get [flags]" - s.Command.Short = "Get namespace mTLS authentication configuration" + s.Command.Use = "disable [flags]" + s.Command.Short = "Disable mTLS authentication for a namespace" if hasHighlighting { - s.Command.Long = "Retrieve the current mTLS authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace mtls get --namespace my-namespace.my-account\x1b[0m" + s.Command.Long = "Disable mTLS authentication for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace mtls disable --namespace my-namespace.my-account\x1b[0m" } else { - s.Command.Long = "Retrieve the current mTLS authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace mtls get --namespace my-namespace.my-account\n```" + s.Command.Long = "Disable mTLS authentication for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace mtls disable --namespace my-namespace.my-account\n```" } s.Command.Args = cobra.NoArgs s.ClientOptions.BuildFlags(s.Command.Flags()) s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) + s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) s.Command.Run = func(c *cobra.Command, args []string) { if err := s.run(cctx, args); err != nil { cctx.Options.Fail(err) @@ -3649,30 +3685,27 @@ func NewCloudNamespaceMtlsGetCommand(cctx *CommandContext, parent *CloudNamespac return &s } -type CloudNamespaceMtlsSetCommand struct { +type CloudNamespaceMtlsEnableCommand struct { Parent *CloudNamespaceMtlsCommand Command cobra.Command ClientOptions NamespaceOptions AsyncOperationOptions ResourceVersionOptions - MtlsAuthEnabled bool } -func NewCloudNamespaceMtlsSetCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCommand) *CloudNamespaceMtlsSetCommand { - var s CloudNamespaceMtlsSetCommand +func NewCloudNamespaceMtlsEnableCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCommand) *CloudNamespaceMtlsEnableCommand { + var s CloudNamespaceMtlsEnableCommand s.Parent = parent s.Command.DisableFlagsInUseLine = true - s.Command.Use = "set [flags]" - s.Command.Short = "Set namespace mTLS authentication configuration" + s.Command.Use = "enable [flags]" + s.Command.Short = "Enable mTLS authentication for a namespace" if hasHighlighting { - s.Command.Long = "Enable or disable mTLS authentication for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace mtls set --namespace my-namespace.my-account --mtls-auth-enabled=true\x1b[0m" + s.Command.Long = "Enable mTLS authentication for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace mtls enable --namespace my-namespace.my-account\x1b[0m" } else { - s.Command.Long = "Enable or disable mTLS authentication for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace mtls set --namespace my-namespace.my-account --mtls-auth-enabled=true\n```" + s.Command.Long = "Enable mTLS authentication for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace mtls enable --namespace my-namespace.my-account\n```" } s.Command.Args = cobra.NoArgs - s.Command.Flags().BoolVar(&s.MtlsAuthEnabled, "mtls-auth-enabled", false, "Enable or disable mTLS authentication for the namespace. Required.") - _ = cobra.MarkFlagRequired(s.Command.Flags(), "mtls-auth-enabled") s.ClientOptions.BuildFlags(s.Command.Flags()) s.NamespaceOptions.BuildFlags(s.Command.Flags()) s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) @@ -3685,6 +3718,35 @@ func NewCloudNamespaceMtlsSetCommand(cctx *CommandContext, parent *CloudNamespac return &s } +type CloudNamespaceMtlsGetCommand struct { + Parent *CloudNamespaceMtlsCommand + Command cobra.Command + ClientOptions + NamespaceOptions +} + +func NewCloudNamespaceMtlsGetCommand(cctx *CommandContext, parent *CloudNamespaceMtlsCommand) *CloudNamespaceMtlsGetCommand { + var s CloudNamespaceMtlsGetCommand + s.Parent = parent + s.Command.DisableFlagsInUseLine = true + s.Command.Use = "get [flags]" + s.Command.Short = "Get namespace mTLS authentication configuration" + if hasHighlighting { + s.Command.Long = "Retrieve the current mTLS authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n\x1b[1mtemporal cloud namespace mtls get --namespace my-namespace.my-account\x1b[0m" + } else { + s.Command.Long = "Retrieve the current mTLS authentication configuration for a Temporal Cloud namespace.\n\nExample:\n\n```\ntemporal cloud namespace mtls get --namespace my-namespace.my-account\n```" + } + s.Command.Args = cobra.NoArgs + s.ClientOptions.BuildFlags(s.Command.Flags()) + s.NamespaceOptions.BuildFlags(s.Command.Flags()) + s.Command.Run = func(c *cobra.Command, args []string) { + if err := s.run(cctx, args); err != nil { + cctx.Options.Fail(err) + } + } + return &s +} + type CloudNamespaceRetentionCommand struct { Parent *CloudNamespaceCommand Command cobra.Command diff --git a/temporalcloudcli/commands.namespace.apikey.go b/temporalcloudcli/commands.namespace.apikey.go index 3077a38..47ded09 100644 --- a/temporalcloudcli/commands.namespace.apikey.go +++ b/temporalcloudcli/commands.namespace.apikey.go @@ -35,12 +35,20 @@ func (c *CloudNamespaceApiKeyGetCommand) run(cctx *CommandContext, _ []string) e return cctx.Printer.PrintStructured(result, printer.StructuredOptions{}) } -func (c *CloudNamespaceApiKeySetCommand) run(cctx *CommandContext, _ []string) error { - client, err := cctx.GetCloudClient(c.ClientOptions) +func (c *CloudNamespaceApiKeyEnableCommand) run(cctx *CommandContext, _ []string) error { + return setApiKeyAuthEnabled(cctx, c.ClientOptions, c.NamespaceOptions, c.ResourceVersionOptions, c.AsyncOperationOptions, true) +} + +func (c *CloudNamespaceApiKeyDisableCommand) run(cctx *CommandContext, _ []string) error { + return setApiKeyAuthEnabled(cctx, c.ClientOptions, c.NamespaceOptions, c.ResourceVersionOptions, c.AsyncOperationOptions, false) +} + +func setApiKeyAuthEnabled(cctx *CommandContext, clientOpts ClientOptions, nsOpts NamespaceOptions, rvOpts ResourceVersionOptions, asyncOpts AsyncOperationOptions, enabled bool) error { + client, err := cctx.GetCloudClient(clientOpts) if err != nil { return err } - res, err := client.GetNamespace(cctx, &cloudservice.GetNamespaceRequest{Namespace: c.Namespace}) + res, err := client.GetNamespace(cctx, &cloudservice.GetNamespaceRequest{Namespace: nsOpts.Namespace}) if err != nil { return err } @@ -50,25 +58,25 @@ func (c *CloudNamespaceApiKeySetCommand) run(cctx *CommandContext, _ []string) e if newSpec.ApiKeyAuth == nil { newSpec.ApiKeyAuth = &namespacev1.ApiKeyAuthSpec{} } - newSpec.ApiKeyAuth.Enabled = c.ApiKeyAuthEnabled + newSpec.ApiKeyAuth.Enabled = enabled yes, err := cctx.GetPrompter().PromptApply(ns.Spec, newSpec, false) if err != nil { return err } if !yes { - return errors.New("Aborting set.") + return errors.New("Aborting.") } rv := ns.ResourceVersion - if c.ResourceVersion != "" { - rv = c.ResourceVersion + if rvOpts.ResourceVersion != "" { + rv = rvOpts.ResourceVersion } resp, err := client.UpdateNamespace(cctx, &cloudservice.UpdateNamespaceRequest{ - Namespace: c.Namespace, + Namespace: nsOpts.Namespace, Spec: newSpec, ResourceVersion: rv, - AsyncOperationId: c.AsyncOperationId, + AsyncOperationId: asyncOpts.AsyncOperationId, }) - return cctx.GetPoller(client, c.AsyncOperationOptions).HandleUpdateOperation(cctx, resp, err) + return cctx.GetPoller(client, asyncOpts).HandleUpdateOperation(cctx, resp, err) } diff --git a/temporalcloudcli/commands.namespace.apikey_test.go b/temporalcloudcli/commands.namespace.apikey_test.go index 7298bc3..bf64e53 100644 --- a/temporalcloudcli/commands.namespace.apikey_test.go +++ b/temporalcloudcli/commands.namespace.apikey_test.go @@ -14,7 +14,7 @@ import ( "github.com/temporalio/cloud-cli/temporalcloudcli" ) -func TestNamespaceApikeyGet(t *testing.T) { +func TestNamespaceApiKeyGet(t *testing.T) { tests := []struct { name string cmd temporalcloudcli.CloudNamespaceApiKeyGetCommand @@ -97,23 +97,22 @@ func TestNamespaceApikeyGet(t *testing.T) { } } -func TestNamespaceApiKeySet(t *testing.T) { +func TestNamespaceApiKeyEnable(t *testing.T) { existingNS := func(apiKeyAuth *namespacev1.ApiKeyAuthSpec) *namespacev1.Namespace { return &namespacev1.Namespace{ Namespace: "my-ns.my-acct", ResourceVersion: "rv-fetched", Spec: &namespacev1.NamespaceSpec{ - Name: "my-ns", - Regions: []string{"aws-us-east-1"}, - RetentionDays: 30, - ApiKeyAuth: apiKeyAuth, + Name: "my-ns", + Regions: []string{"aws-us-east-1"}, + ApiKeyAuth: apiKeyAuth, }, } } tests := []struct { name string - cmd temporalcloudcli.CloudNamespaceApiKeySetCommand + cmd temporalcloudcli.CloudNamespaceApiKeyEnableCommand cloudClientExpectations func(*cloudmock.MockCloudServiceClient) promptOptions temporalcloudcli.TestPromptOptions asyncPollerOptions temporalcloudcli.TestAsyncPollerOptions @@ -121,10 +120,7 @@ func TestNamespaceApiKeySet(t *testing.T) { }{ { name: "EnableFromNil", - cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - ApiKeyAuthEnabled: true, - }, + cmd: temporalcloudcli.CloudNamespaceApiKeyEnableCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { c.EXPECT(). GetNamespace(mock.Anything, &cloudservice.GetNamespaceRequest{Namespace: "my-ns.my-acct"}, mock.Anything). @@ -142,34 +138,10 @@ func TestNamespaceApiKeySet(t *testing.T) { promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-enable"}, }, - { - name: "DisableFromEnabled", - cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - ApiKeyAuthEnabled: false, - }, - cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { - c.EXPECT(). - GetNamespace(mock.Anything, mock.Anything, mock.Anything). - Return(&cloudservice.GetNamespaceResponse{ - Namespace: existingNS(&namespacev1.ApiKeyAuthSpec{Enabled: true}), - }, nil) - c.EXPECT(). - UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { - return proto.Equal(req.Spec.ApiKeyAuth, &namespacev1.ApiKeyAuthSpec{Enabled: false}) - }), mock.Anything). - Return(&cloudservice.UpdateNamespaceResponse{ - AsyncOperation: &operation.AsyncOperation{Id: "op-disable"}, - }, nil) - }, - promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, - asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-disable"}, - }, { name: "ResourceVersionOverride", - cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ + cmd: temporalcloudcli.CloudNamespaceApiKeyEnableCommand{ NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - ApiKeyAuthEnabled: true, ResourceVersionOptions: temporalcloudcli.ResourceVersionOptions{ResourceVersion: "rv-user"}, }, cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { @@ -188,50 +160,84 @@ func TestNamespaceApiKeySet(t *testing.T) { asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-rv"}, }, { - name: "AsyncOperationIdOverride", - cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - ApiKeyAuthEnabled: true, - AsyncOperationOptions: temporalcloudcli.AsyncOperationOptions{AsyncOperationId: "op-custom"}, + name: "GetNamespaceError", + cmd: temporalcloudcli.CloudNamespaceApiKeyEnableCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(nil, errors.New("namespace not found")) }, + expectedErr: "namespace not found", + }, + { + name: "PromptDeclined", + cmd: temporalcloudcli.CloudNamespaceApiKeyEnableCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { c.EXPECT(). GetNamespace(mock.Anything, mock.Anything, mock.Anything). Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) - c.EXPECT(). - UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { - return req.AsyncOperationId == "op-custom" - }), mock.Anything). - Return(&cloudservice.UpdateNamespaceResponse{ - AsyncOperation: &operation.AsyncOperation{Id: "op-custom"}, - }, nil) }, - promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, - asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-custom"}, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: false}, + expectedErr: "Aborting.", }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + temporalcloudcli.TestCommand(t, &tt.cmd, temporalcloudcli.TestCommandOptions{ + CloudClientExpectations: tt.cloudClientExpectations, + PromptOptions: tt.promptOptions, + AsyncPollerOptions: tt.asyncPollerOptions, + JSONOutput: true, + ExpectedError: tt.expectedErr, + }) + }) + } +} + +func TestNamespaceApiKeyDisable(t *testing.T) { + existingNS := &namespacev1.Namespace{ + Namespace: "my-ns.my-acct", + ResourceVersion: "rv-fetched", + Spec: &namespacev1.NamespaceSpec{ + Name: "my-ns", + Regions: []string{"aws-us-east-1"}, + ApiKeyAuth: &namespacev1.ApiKeyAuthSpec{Enabled: true}, + }, + } + + tests := []struct { + name string + cmd temporalcloudcli.CloudNamespaceApiKeyDisableCommand + cloudClientExpectations func(*cloudmock.MockCloudServiceClient) + promptOptions temporalcloudcli.TestPromptOptions + asyncPollerOptions temporalcloudcli.TestAsyncPollerOptions + expectedErr string + }{ { - name: "GetNamespaceError", - cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - ApiKeyAuthEnabled: true, - }, + name: "DisableFromEnabled", + cmd: temporalcloudcli.CloudNamespaceApiKeyDisableCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { c.EXPECT(). GetNamespace(mock.Anything, mock.Anything, mock.Anything). - Return(nil, errors.New("namespace not found")) + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS}, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { + return proto.Equal(req.Spec.ApiKeyAuth, &namespacev1.ApiKeyAuthSpec{Enabled: false}) + }), mock.Anything). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-disable"}, + }, nil) }, - expectedErr: "namespace not found", + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-disable"}, }, { name: "UpdateNamespaceError", - cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - ApiKeyAuthEnabled: true, - }, + cmd: temporalcloudcli.CloudNamespaceApiKeyDisableCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { c.EXPECT(). GetNamespace(mock.Anything, mock.Anything, mock.Anything). - Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS}, nil) c.EXPECT(). UpdateNamespace(mock.Anything, mock.Anything, mock.Anything). Return(nil, errors.New("update failed")) @@ -239,20 +245,6 @@ func TestNamespaceApiKeySet(t *testing.T) { promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, expectedErr: "update failed", }, - { - name: "PromptDeclined", - cmd: temporalcloudcli.CloudNamespaceApiKeySetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - ApiKeyAuthEnabled: true, - }, - cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { - c.EXPECT(). - GetNamespace(mock.Anything, mock.Anything, mock.Anything). - Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) - }, - promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: false}, - expectedErr: "Aborting set.", - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/temporalcloudcli/commands.namespace.mtls.go b/temporalcloudcli/commands.namespace.mtls.go index c9bf33b..da3a20a 100644 --- a/temporalcloudcli/commands.namespace.mtls.go +++ b/temporalcloudcli/commands.namespace.mtls.go @@ -7,6 +7,7 @@ import ( namespacev1 "go.temporal.io/cloud-sdk/api/namespace/v1" "google.golang.org/protobuf/proto" + "github.com/temporalio/cloud-cli/internal/cert" "github.com/temporalio/cloud-cli/temporalcloudcli/internal/printer" ) @@ -20,27 +21,51 @@ func (c *CloudNamespaceMtlsGetCommand) run(cctx *CommandContext, _ []string) err return err } - enabled := false - if res.Namespace.Spec.MtlsAuth != nil { - enabled = res.Namespace.Spec.MtlsAuth.Enabled + var ( + enabled bool + caCerts []cert.CACert + certFilter []*namespacev1.CertificateFilterSpec + ) + if mtls := res.Namespace.Spec.MtlsAuth; mtls != nil { + enabled = mtls.Enabled + certFilter = mtls.CertificateFilters + if len(mtls.AcceptedClientCa) > 0 { + var err error + caCerts, err = cert.ParseCACerts(mtls.AcceptedClientCa) + if err != nil { + return err + } + } } result := struct { - Namespace string `json:"namespace"` - MtlsAuthEnabled bool `json:"mtlsAuthEnabled"` + Namespace string `json:"namespace"` + MtlsAuthEnabled bool `json:"mtlsAuthEnabled"` + CACerts []cert.CACert `json:"caCerts,omitempty"` + CertificateFilters []*namespacev1.CertificateFilterSpec `json:"certificateFilters,omitempty"` }{ - Namespace: res.Namespace.Namespace, - MtlsAuthEnabled: enabled, + Namespace: res.Namespace.Namespace, + MtlsAuthEnabled: enabled, + CACerts: caCerts, + CertificateFilters: certFilter, } return cctx.Printer.PrintStructured(result, printer.StructuredOptions{}) } -func (c *CloudNamespaceMtlsSetCommand) run(cctx *CommandContext, _ []string) error { - client, err := cctx.GetCloudClient(c.ClientOptions) +func (c *CloudNamespaceMtlsEnableCommand) run(cctx *CommandContext, _ []string) error { + return setMtlsAuthEnabled(cctx, c.ClientOptions, c.NamespaceOptions, c.ResourceVersionOptions, c.AsyncOperationOptions, true) +} + +func (c *CloudNamespaceMtlsDisableCommand) run(cctx *CommandContext, _ []string) error { + return setMtlsAuthEnabled(cctx, c.ClientOptions, c.NamespaceOptions, c.ResourceVersionOptions, c.AsyncOperationOptions, false) +} + +func setMtlsAuthEnabled(cctx *CommandContext, clientOpts ClientOptions, nsOpts NamespaceOptions, rvOpts ResourceVersionOptions, asyncOpts AsyncOperationOptions, enabled bool) error { + client, err := cctx.GetCloudClient(clientOpts) if err != nil { return err } - res, err := client.GetNamespace(cctx, &cloudservice.GetNamespaceRequest{Namespace: c.Namespace}) + res, err := client.GetNamespace(cctx, &cloudservice.GetNamespaceRequest{Namespace: nsOpts.Namespace}) if err != nil { return err } @@ -50,25 +75,25 @@ func (c *CloudNamespaceMtlsSetCommand) run(cctx *CommandContext, _ []string) err if newSpec.MtlsAuth == nil { newSpec.MtlsAuth = &namespacev1.MtlsAuthSpec{} } - newSpec.MtlsAuth.Enabled = c.MtlsAuthEnabled + newSpec.MtlsAuth.Enabled = enabled yes, err := cctx.GetPrompter().PromptApply(ns.Spec, newSpec, false) if err != nil { return err } if !yes { - return errors.New("Aborting set.") + return errors.New("Aborting.") } rv := ns.ResourceVersion - if c.ResourceVersion != "" { - rv = c.ResourceVersion + if rvOpts.ResourceVersion != "" { + rv = rvOpts.ResourceVersion } resp, err := client.UpdateNamespace(cctx, &cloudservice.UpdateNamespaceRequest{ - Namespace: c.Namespace, + Namespace: nsOpts.Namespace, Spec: newSpec, ResourceVersion: rv, - AsyncOperationId: c.AsyncOperationId, + AsyncOperationId: asyncOpts.AsyncOperationId, }) - return cctx.GetPoller(client, c.AsyncOperationOptions).HandleUpdateOperation(cctx, resp, err) + return cctx.GetPoller(client, asyncOpts).HandleUpdateOperation(cctx, resp, err) } diff --git a/temporalcloudcli/commands.namespace.mtls_test.go b/temporalcloudcli/commands.namespace.mtls_test.go index f24aa79..73b2dcd 100644 --- a/temporalcloudcli/commands.namespace.mtls_test.go +++ b/temporalcloudcli/commands.namespace.mtls_test.go @@ -97,7 +97,7 @@ func TestNamespaceMtlsGet(t *testing.T) { } } -func TestNamespaceMtlsSet(t *testing.T) { +func TestNamespaceMtlsEnable(t *testing.T) { existingNS := func(mtlsAuth *namespacev1.MtlsAuthSpec) *namespacev1.Namespace { return &namespacev1.Namespace{ Namespace: "my-ns.my-acct", @@ -113,7 +113,7 @@ func TestNamespaceMtlsSet(t *testing.T) { tests := []struct { name string - cmd temporalcloudcli.CloudNamespaceMtlsSetCommand + cmd temporalcloudcli.CloudNamespaceMtlsEnableCommand cloudClientExpectations func(*cloudmock.MockCloudServiceClient) promptOptions temporalcloudcli.TestPromptOptions asyncPollerOptions temporalcloudcli.TestAsyncPollerOptions @@ -121,10 +121,7 @@ func TestNamespaceMtlsSet(t *testing.T) { }{ { name: "EnableFromNil", - cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - MtlsAuthEnabled: true, - }, + cmd: temporalcloudcli.CloudNamespaceMtlsEnableCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { c.EXPECT(). GetNamespace(mock.Anything, &cloudservice.GetNamespaceRequest{Namespace: "my-ns.my-acct"}, mock.Anything). @@ -142,34 +139,10 @@ func TestNamespaceMtlsSet(t *testing.T) { promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-enable"}, }, - { - name: "DisableFromEnabled", - cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - MtlsAuthEnabled: false, - }, - cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { - c.EXPECT(). - GetNamespace(mock.Anything, mock.Anything, mock.Anything). - Return(&cloudservice.GetNamespaceResponse{ - Namespace: existingNS(&namespacev1.MtlsAuthSpec{Enabled: true}), - }, nil) - c.EXPECT(). - UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { - return proto.Equal(req.Spec.MtlsAuth, &namespacev1.MtlsAuthSpec{Enabled: false}) - }), mock.Anything). - Return(&cloudservice.UpdateNamespaceResponse{ - AsyncOperation: &operation.AsyncOperation{Id: "op-disable"}, - }, nil) - }, - promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, - asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-disable"}, - }, { name: "ResourceVersionOverride", - cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ + cmd: temporalcloudcli.CloudNamespaceMtlsEnableCommand{ NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - MtlsAuthEnabled: true, ResourceVersionOptions: temporalcloudcli.ResourceVersionOptions{ResourceVersion: "rv-user"}, }, cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { @@ -188,50 +161,84 @@ func TestNamespaceMtlsSet(t *testing.T) { asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-rv"}, }, { - name: "AsyncOperationIdOverride", - cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - MtlsAuthEnabled: true, - AsyncOperationOptions: temporalcloudcli.AsyncOperationOptions{AsyncOperationId: "op-custom"}, + name: "GetNamespaceError", + cmd: temporalcloudcli.CloudNamespaceMtlsEnableCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNamespace(mock.Anything, mock.Anything, mock.Anything). + Return(nil, errors.New("namespace not found")) }, + expectedErr: "namespace not found", + }, + { + name: "PromptDeclined", + cmd: temporalcloudcli.CloudNamespaceMtlsEnableCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { c.EXPECT(). GetNamespace(mock.Anything, mock.Anything, mock.Anything). Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) - c.EXPECT(). - UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { - return req.AsyncOperationId == "op-custom" - }), mock.Anything). - Return(&cloudservice.UpdateNamespaceResponse{ - AsyncOperation: &operation.AsyncOperation{Id: "op-custom"}, - }, nil) }, - promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, - asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-custom"}, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: false}, + expectedErr: "Aborting.", }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + temporalcloudcli.TestCommand(t, &tt.cmd, temporalcloudcli.TestCommandOptions{ + CloudClientExpectations: tt.cloudClientExpectations, + PromptOptions: tt.promptOptions, + AsyncPollerOptions: tt.asyncPollerOptions, + JSONOutput: true, + ExpectedError: tt.expectedErr, + }) + }) + } +} + +func TestNamespaceMtlsDisable(t *testing.T) { + existingNS := &namespacev1.Namespace{ + Namespace: "my-ns.my-acct", + ResourceVersion: "rv-fetched", + Spec: &namespacev1.NamespaceSpec{ + Name: "my-ns", + Regions: []string{"aws-us-east-1"}, + MtlsAuth: &namespacev1.MtlsAuthSpec{Enabled: true}, + }, + } + + tests := []struct { + name string + cmd temporalcloudcli.CloudNamespaceMtlsDisableCommand + cloudClientExpectations func(*cloudmock.MockCloudServiceClient) + promptOptions temporalcloudcli.TestPromptOptions + asyncPollerOptions temporalcloudcli.TestAsyncPollerOptions + expectedErr string + }{ { - name: "GetNamespaceError", - cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - MtlsAuthEnabled: true, - }, + name: "DisableFromEnabled", + cmd: temporalcloudcli.CloudNamespaceMtlsDisableCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { c.EXPECT(). GetNamespace(mock.Anything, mock.Anything, mock.Anything). - Return(nil, errors.New("namespace not found")) + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS}, nil) + c.EXPECT(). + UpdateNamespace(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNamespaceRequest) bool { + return proto.Equal(req.Spec.MtlsAuth, &namespacev1.MtlsAuthSpec{Enabled: false}) + }), mock.Anything). + Return(&cloudservice.UpdateNamespaceResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-disable"}, + }, nil) }, - expectedErr: "namespace not found", + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-disable"}, }, { name: "UpdateNamespaceError", - cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - MtlsAuthEnabled: true, - }, + cmd: temporalcloudcli.CloudNamespaceMtlsDisableCommand{NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}}, cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { c.EXPECT(). GetNamespace(mock.Anything, mock.Anything, mock.Anything). - Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) + Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS}, nil) c.EXPECT(). UpdateNamespace(mock.Anything, mock.Anything, mock.Anything). Return(nil, errors.New("update failed")) @@ -239,20 +246,6 @@ func TestNamespaceMtlsSet(t *testing.T) { promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, expectedErr: "update failed", }, - { - name: "PromptDeclined", - cmd: temporalcloudcli.CloudNamespaceMtlsSetCommand{ - NamespaceOptions: temporalcloudcli.NamespaceOptions{Namespace: "my-ns.my-acct"}, - MtlsAuthEnabled: true, - }, - cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { - c.EXPECT(). - GetNamespace(mock.Anything, mock.Anything, mock.Anything). - Return(&cloudservice.GetNamespaceResponse{Namespace: existingNS(nil)}, nil) - }, - promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: false}, - expectedErr: "Aborting set.", - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/temporalcloudcli/commands.yml b/temporalcloudcli/commands.yml index bdd2978..7dbfc81 100644 --- a/temporalcloudcli/commands.yml +++ b/temporalcloudcli/commands.yml @@ -620,15 +620,31 @@ commands: option-sets: - client - namespace - - name: cloud namespace mtls set - summary: Set namespace mTLS authentication configuration + - name: cloud namespace mtls enable + summary: Enable mTLS authentication for a namespace description: | - Enable or disable mTLS authentication for a Temporal Cloud namespace. + Enable mTLS authentication for a Temporal Cloud namespace. Example: ``` - temporal cloud namespace mtls set --namespace my-namespace.my-account --mtls-auth-enabled=true + temporal cloud namespace mtls enable --namespace my-namespace.my-account + ``` + has-init: false + option-sets: + - client + - namespace + - async-operation + - resource-version + - name: cloud namespace mtls disable + summary: Disable mTLS authentication for a namespace + description: | + Disable mTLS authentication for a Temporal Cloud namespace. + + Example: + + ``` + temporal cloud namespace mtls disable --namespace my-namespace.my-account ``` has-init: false option-sets: @@ -636,12 +652,6 @@ commands: - namespace - async-operation - resource-version - options: - - name: mtls-auth-enabled - type: bool - required: true - description: | - Enable or disable mTLS authentication for the namespace. # Namespace api-key auth commands - name: cloud namespace api-key @@ -663,15 +673,31 @@ commands: option-sets: - client - namespace - - name: cloud namespace api-key set - summary: Set namespace API key authentication configuration + - name: cloud namespace api-key enable + summary: Enable API key authentication for a namespace description: | - Enable or disable API key authentication for a Temporal Cloud namespace. + Enable API key authentication for a Temporal Cloud namespace. Example: ``` - temporal cloud namespace api-key set --namespace my-namespace.my-account --api-key-auth-enabled=true + temporal cloud namespace api-key enable --namespace my-namespace.my-account + ``` + has-init: false + option-sets: + - client + - namespace + - async-operation + - resource-version + - name: cloud namespace api-key disable + summary: Disable API key authentication for a namespace + description: | + Disable API key authentication for a Temporal Cloud namespace. + + Example: + + ``` + temporal cloud namespace api-key disable --namespace my-namespace.my-account ``` has-init: false option-sets: @@ -679,12 +705,6 @@ commands: - namespace - async-operation - resource-version - options: - - name: api-key-auth-enabled - type: bool - required: true - description: | - Enable or disable API key authentication for the namespace. # Namespace mtls cert-ca commands - name: cloud namespace mtls cert-ca