diff --git a/docs/commands/rhoas_kafka_acl_list.adoc b/docs/commands/rhoas_kafka_acl_list.adoc index 0587e1031..3b1bf6ada 100644 --- a/docs/commands/rhoas_kafka_acl_list.adoc +++ b/docs/commands/rhoas_kafka_acl_list.adoc @@ -43,17 +43,29 @@ $ rhoas kafka acl list --user foo_user # Display Kafka ACL rules for a specific service account $ rhoas kafka acl list --service-account srvc-acct-f20a7561-7426-4f5a-b5e7-0ef2db31e15b +# Display Kafka ACL rules for a specific topic +$ rhoas kafka acl list --topic foo_topic_name + +# Display Kafka ACL rules for a specific consumer group +$ rhoas kafka acl list --group foo_group_id + +# Display Kafka ACL rules for a specific consumer group and user +$ rhoas kafka acl list --group foo_group_id --user foo_user + .... [discrete] == Options `--all-accounts`:: Set the ACL principal to match all principals (users and service accounts) + `--cluster`:: Set filter to cluster resource + `--group` _string_:: Text search to filter ACL rules for consumer groups by id `--instance-id` _string_:: Kafka instance ID. Uses the current instance if not set `-o`, `--output` _string_:: Specify the output format. Choose from: "json", "yaml", "yml" `--page` _int32_:: Current page number for the list (default 1) `--service-account` _string_:: Service account client ID used as principal for this operation `--size` _int32_:: Maximum number of items to be returned per page (default 10) + `--topic` _string_:: Text search to filter ACL rules for topics by name `--user` _string_:: User ID to be used as principal [discrete] diff --git a/pkg/cmd/kafka/acl/admin/admin.go b/pkg/cmd/kafka/acl/admin/admin.go index 152cf137a..acca30c18 100644 --- a/pkg/cmd/kafka/acl/admin/admin.go +++ b/pkg/cmd/kafka/acl/admin/admin.go @@ -103,7 +103,7 @@ func NewAdminACLCommand(f *factory.Factory) *cobra.Command { }, } - fs := flagset.NewFlagSet(cmd, opts.localizer, opts.connection) + fs := flagset.NewFlagSet(cmd, f) fs.AddUser(&userID) fs.AddServiceAccount(&serviceAccount) diff --git a/pkg/cmd/kafka/acl/create/create.go b/pkg/cmd/kafka/acl/create/create.go index 5028183fa..12a9202e7 100644 --- a/pkg/cmd/kafka/acl/create/create.go +++ b/pkg/cmd/kafka/acl/create/create.go @@ -65,7 +65,7 @@ func NewCreateCommand(f *factory.Factory) *cobra.Command { }, } - flags := flagutil.NewFlagSet(cmd, opts.Localizer, opts.Connection) + flags := flagutil.NewFlagSet(cmd, f) _ = flags.AddPermissionCreate(&opts.Permission).Required() _ = flags.AddOperationCreate(&opts.Operation).Required() diff --git a/pkg/cmd/kafka/acl/delete/delete.go b/pkg/cmd/kafka/acl/delete/delete.go index c8b24b168..95f7b066b 100644 --- a/pkg/cmd/kafka/acl/delete/delete.go +++ b/pkg/cmd/kafka/acl/delete/delete.go @@ -66,7 +66,7 @@ func NewDeleteCommand(f *factory.Factory) *cobra.Command { }, } - flags := flagutil.NewFlagSet(cmd, opts.Localizer, opts.Connection) + flags := flagutil.NewFlagSet(cmd, f) _ = flags.AddPermissionFilter(&opts.Permission).Required() _ = flags.AddOperationFilter(&opts.Operation).Required() diff --git a/pkg/cmd/kafka/acl/flagutil/flagset.go b/pkg/cmd/kafka/acl/flagutil/flagset.go index e8b21aeee..0220923e5 100644 --- a/pkg/cmd/kafka/acl/flagutil/flagset.go +++ b/pkg/cmd/kafka/acl/flagutil/flagset.go @@ -23,19 +23,17 @@ var ResourceTypeFlagEntries []*localize.TemplateEntry = []*localize.TemplateEntr } type flagSet struct { - cmd *cobra.Command - localizer localize.Localizer - conn factory.ConnectionFunc + cmd *cobra.Command + factory *factory.Factory *flagutil.FlagSet } // NewFlagSet returns a new flag set with common Kafka ACL flags -func NewFlagSet(cmd *cobra.Command, localizer localize.Localizer, conn factory.ConnectionFunc) *flagSet { +func NewFlagSet(cmd *cobra.Command, f *factory.Factory) *flagSet { return &flagSet{ - cmd: cmd, - localizer: localizer, - conn: conn, - FlagSet: flagutil.NewFlagSet(cmd, localizer), + cmd: cmd, + factory: f, + FlagSet: flagutil.NewFlagSet(cmd, f.Localizer), } } @@ -54,7 +52,7 @@ func (fs *flagSet) AddResourceType(resourceType *string) *flagutil.FlagOptions { resourceType, flagName, aclutil.ResourceTypeANY, - flagutil.FlagDescription(fs.localizer, "kafka.acl.common.flag.resourceType", resourceTypes...), + flagutil.FlagDescription(fs.factory.Localizer, "kafka.acl.common.flag.resourceType", resourceTypes...), ) _ = fs.cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -79,7 +77,7 @@ func (fs *flagSet) AddOperationFilter(operationType *string) *flagutil.FlagOptio operationType, flagName, "", - flagutil.FlagDescription(fs.localizer, "kafka.acl.common.flag.operation.description", operations...), + flagutil.FlagDescription(fs.factory.Localizer, "kafka.acl.common.flag.operation.description", operations...), ) _ = fs.cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -104,7 +102,7 @@ func (fs *flagSet) AddOperationCreate(operationType *string) *flagutil.FlagOptio operationType, flagName, "", - flagutil.FlagDescription(fs.localizer, "kafka.acl.common.flag.operation.description", operations...), + flagutil.FlagDescription(fs.factory.Localizer, "kafka.acl.common.flag.operation.description", operations...), ) _ = fs.cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -129,7 +127,7 @@ func (fs *flagSet) AddPermissionFilter(permission *string) *flagutil.FlagOptions permission, flagName, aclutil.PermissionANY, - flagutil.FlagDescription(fs.localizer, "kafka.acl.common.flag.permission.description", permissions...), + flagutil.FlagDescription(fs.factory.Localizer, "kafka.acl.common.flag.permission.description", permissions...), ) _ = fs.cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -154,7 +152,7 @@ func (fs *flagSet) AddPermissionCreate(permission *string) *flagutil.FlagOptions permission, flagName, "", - flagutil.FlagDescription(fs.localizer, "kafka.acl.common.flag.permission.description", permissions...), + flagutil.FlagDescription(fs.factory.Localizer, "kafka.acl.common.flag.permission.description", permissions...), ) _ = fs.cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -172,8 +170,10 @@ func (fs *flagSet) AddTopic(topic *string) { topic, flagName, "", - fs.localizer.MustLocalize("kafka.acl.common.flag.topic.description"), + fs.factory.Localizer.MustLocalize("kafka.acl.common.flag.topic.description"), ) + + _ = flagutil.RegisterTopicCompletionFunc(fs.cmd, fs.factory) } // AddConsumerGroup adds a flag for setting the consumer group ID @@ -184,8 +184,10 @@ func (fs *flagSet) AddConsumerGroup(group *string) { group, flagName, "", - fs.localizer.MustLocalize("kafka.acl.common.flag.group.description"), + fs.factory.Localizer.MustLocalize("kafka.acl.common.flag.group.description"), ) + + _ = flagutil.RegisterGroupCompletionFunc(fs.cmd, fs.factory) } // AddTransactionalID adds a flag for setting the consumer group ID @@ -196,7 +198,7 @@ func (fs *flagSet) AddTransactionalID(id *string) { id, flagName, "", - fs.localizer.MustLocalize("kafka.acl.common.flag.transactionalID.description"), + fs.factory.Localizer.MustLocalize("kafka.acl.common.flag.transactionalID.description"), ) } @@ -208,7 +210,7 @@ func (fs *flagSet) AddPrefix(prefix *bool) { prefix, flagName, false, - fs.localizer.MustLocalize("kafka.acl.common.flag.prefix.description"), + fs.factory.Localizer.MustLocalize("kafka.acl.common.flag.prefix.description"), ) } @@ -220,7 +222,7 @@ func (fs *flagSet) AddCluster(prefix *bool) { prefix, flagName, false, - fs.localizer.MustLocalize("kafka.acl.common.flag.cluster.description"), + fs.factory.Localizer.MustLocalize("kafka.acl.common.flag.cluster.description"), ) } @@ -232,10 +234,10 @@ func (fs *flagSet) AddUser(userID *string) *flagutil.FlagOptions { userID, flagName, "", - fs.localizer.MustLocalize("kafka.acl.common.flag.user.description"), + fs.factory.Localizer.MustLocalize("kafka.acl.common.flag.user.description"), ) - _ = flagutil.RegisterUserCompletionFunc(fs.cmd, flagName, fs.conn) + _ = flagutil.RegisterUserCompletionFunc(fs.cmd, flagName, fs.factory) return flagutil.WithFlagOptions(fs.cmd, flagName) } @@ -248,10 +250,10 @@ func (fs *flagSet) AddServiceAccount(serviceAccountID *string) *flagutil.FlagOpt serviceAccountID, flagName, "", - fs.localizer.MustLocalize("kafka.acl.common.flag.serviceAccount.description"), + fs.factory.Localizer.MustLocalize("kafka.acl.common.flag.serviceAccount.description"), ) - _ = flagutil.RegisterServiceAccountCompletionFunc(fs.cmd, flagName, fs.conn) + _ = flagutil.RegisterServiceAccountCompletionFunc(fs.cmd, fs.factory) return flagutil.WithFlagOptions(fs.cmd, flagName) } @@ -264,7 +266,7 @@ func (fs *flagSet) AddInstanceID(id *string) { id, flagName, "", - fs.localizer.MustLocalize("kafka.common.flag.instanceID.description"), + fs.factory.Localizer.MustLocalize("kafka.common.flag.instanceID.description"), ) } @@ -276,6 +278,6 @@ func (fs *flagSet) AddAllAccounts(allAccounts *bool) { allAccounts, flagName, false, - fs.localizer.MustLocalize("kafka.acl.common.flag.allAccounts.description"), + fs.factory.Localizer.MustLocalize("kafka.acl.common.flag.allAccounts.description"), ) } diff --git a/pkg/cmd/kafka/acl/grant/grant.go b/pkg/cmd/kafka/acl/grant/grant.go index 80f37466f..fef2dac52 100644 --- a/pkg/cmd/kafka/acl/grant/grant.go +++ b/pkg/cmd/kafka/acl/grant/grant.go @@ -89,7 +89,7 @@ func NewGrantPermissionsACLCommand(f *factory.Factory) *cobra.Command { }, } - flags := flagutil.NewFlagSet(cmd, opts.localizer, f.Connection) + flags := flagutil.NewFlagSet(cmd, f) flags.AddInstanceID(&opts.kafkaID) flags.AddYes(&opts.force) diff --git a/pkg/cmd/kafka/acl/list/list.go b/pkg/cmd/kafka/acl/list/list.go index d2c44231a..c31139920 100644 --- a/pkg/cmd/kafka/acl/list/list.go +++ b/pkg/cmd/kafka/acl/list/list.go @@ -12,6 +12,7 @@ import ( "github.com/redhat-developer/app-services-cli/pkg/dump" "github.com/redhat-developer/app-services-cli/pkg/iostreams" "github.com/redhat-developer/app-services-cli/pkg/kafka/aclutil" + kafkacmdutil "github.com/redhat-developer/app-services-cli/pkg/kafka/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/localize" "github.com/redhat-developer/app-services-cli/pkg/logging" ) @@ -34,7 +35,12 @@ type options struct { size int32 kafkaID string principal string - output string + + topic string + group string + cluster bool + + output string } // NewListACLCommand creates a new command to list Kafka ACL rules @@ -107,7 +113,7 @@ func NewListACLCommand(f *factory.Factory) *cobra.Command { }, } - flags := flagutil.NewFlagSet(cmd, opts.localizer, opts.connection) + flags := flagutil.NewFlagSet(cmd, f) flags.AddInstanceID(&opts.kafkaID) flags.AddOutput(&opts.output) @@ -116,6 +122,17 @@ func NewListACLCommand(f *factory.Factory) *cobra.Command { flags.AddUser(&userID) flags.AddServiceAccount(&serviceAccount) flags.AddAllAccounts(&allAccounts) + flags.BoolVar(&opts.cluster, "cluster", false, opts.localizer.MustLocalize("kafka.acl.list.flag.cluster.description")) + flags.StringVar(&opts.topic, "topic", "", opts.localizer.MustLocalize("kafka.acl.list.flag.topic.description")) + flags.StringVar(&opts.group, "group", "", opts.localizer.MustLocalize("kafka.acl.list.flag.group.description")) + + _ = cmd.RegisterFlagCompletionFunc("topic", func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return kafkacmdutil.FilterValidTopicNameArgs(f, toComplete) + }) + + _ = cmd.RegisterFlagCompletionFunc("group", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return kafkacmdutil.FilterValidConsumerGroupIDs(f, toComplete) + }) return cmd } @@ -141,6 +158,40 @@ func runList(opts *options) (err error) { req = req.Principal(principalQuery) } + var selectedResourceTypeCount int + var resourceType string + var resourceName string + + if opts.topic != "" { + selectedResourceTypeCount++ + resourceType = aclutil.ResourceTypeTOPIC + resourceName = opts.topic + } + + if opts.group != "" { + selectedResourceTypeCount++ + resourceType = aclutil.ResourceTypeGROUP + resourceName = opts.group + } + + if opts.cluster { + selectedResourceTypeCount++ + resourceType = aclutil.ResourceTypeCLUSTER + resourceName = aclutil.KafkaCluster + } + + if selectedResourceTypeCount > 1 { + return opts.localizer.MustLocalizeError("kafka.acl.list.error.oneResourceTypeAllowed", flagutil.ResourceTypeFlagEntries...) + } + + if resourceType != "" { + req = req.ResourceType(aclutil.GetMappedResourceTypeFilterValue(resourceType)) + } + + if resourceName != "" { + req = req.ResourceName(aclutil.GetResourceName(resourceName)) + } + permissionsData, httpRes, err := req.Execute() if httpRes != nil { defer httpRes.Body.Close() @@ -150,6 +201,12 @@ func runList(opts *options) (err error) { return err } + if permissionsData.GetTotal() == 0 && opts.output == "" { + opts.logger.Info(opts.localizer.MustLocalize("kafka.acl.list.log.info.noACLs", localize.NewEntry("InstanceName", kafkaInstance.GetName()))) + + return nil + } + switch opts.output { case dump.EmptyFormat: opts.logger.Info("") diff --git a/pkg/cmd/kafka/consumergroup/delete/delete.go b/pkg/cmd/kafka/consumergroup/delete/delete.go index 2b15fd5a0..652757908 100644 --- a/pkg/cmd/kafka/consumergroup/delete/delete.go +++ b/pkg/cmd/kafka/consumergroup/delete/delete.go @@ -10,9 +10,9 @@ import ( "github.com/redhat-developer/app-services-cli/internal/config" "github.com/redhat-developer/app-services-cli/pkg/cmd/factory" "github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/flagutil" - "github.com/redhat-developer/app-services-cli/pkg/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/connection" "github.com/redhat-developer/app-services-cli/pkg/iostreams" + kafkacmdutil "github.com/redhat-developer/app-services-cli/pkg/kafka/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/localize" "github.com/redhat-developer/app-services-cli/pkg/logging" ) @@ -76,7 +76,7 @@ func NewDeleteConsumerGroupCommand(f *factory.Factory) *cobra.Command { // flag based completions for ID _ = cmd.RegisterFlagCompletionFunc("id", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdutil.FilterValidConsumerGroupIDs(f, toComplete) + return kafkacmdutil.FilterValidConsumerGroupIDs(f, toComplete) }) return cmd diff --git a/pkg/cmd/kafka/consumergroup/describe/describe.go b/pkg/cmd/kafka/consumergroup/describe/describe.go index d8591432f..8ed7d0183 100644 --- a/pkg/cmd/kafka/consumergroup/describe/describe.go +++ b/pkg/cmd/kafka/consumergroup/describe/describe.go @@ -11,12 +11,12 @@ import ( cgutil "github.com/redhat-developer/app-services-cli/pkg/kafka/consumergroup" - "github.com/redhat-developer/app-services-cli/pkg/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/localize" "github.com/spf13/cobra" "github.com/redhat-developer/app-services-cli/internal/config" + "github.com/redhat-developer/app-services-cli/pkg/cmd/factory" "github.com/redhat-developer/app-services-cli/pkg/cmd/flag" "github.com/redhat-developer/app-services-cli/pkg/cmdutil/flagutil" @@ -24,6 +24,7 @@ import ( "github.com/redhat-developer/app-services-cli/pkg/connection" "github.com/redhat-developer/app-services-cli/pkg/dump" "github.com/redhat-developer/app-services-cli/pkg/iostreams" + kafkacmdutil "github.com/redhat-developer/app-services-cli/pkg/kafka/cmdutil" ) type options struct { @@ -97,7 +98,7 @@ func NewDescribeConsumerGroupCommand(f *factory.Factory) *cobra.Command { // flag based completions for ID _ = cmd.RegisterFlagCompletionFunc("id", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdutil.FilterValidConsumerGroupIDs(f, toComplete) + return kafkacmdutil.FilterValidConsumerGroupIDs(f, toComplete) }) flagutil.EnableOutputFlagCompletion(cmd) diff --git a/pkg/cmd/kafka/consumergroup/list/list.go b/pkg/cmd/kafka/consumergroup/list/list.go index be8d671eb..9deeee11d 100644 --- a/pkg/cmd/kafka/consumergroup/list/list.go +++ b/pkg/cmd/kafka/consumergroup/list/list.go @@ -13,6 +13,7 @@ import ( "github.com/redhat-developer/app-services-cli/pkg/cmd/flag" "github.com/redhat-developer/app-services-cli/pkg/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/cmdutil/flagutil" + kafkacmdutil "github.com/redhat-developer/app-services-cli/pkg/kafka/cmdutil" "github.com/spf13/cobra" @@ -101,7 +102,7 @@ func NewListConsumerGroupCommand(f *factory.Factory) *cobra.Command { flags.Int32VarP(&opts.size, "size", "", cmdutil.ConvertSizeValueToInt32(build.DefaultPageSize), opts.localizer.MustLocalize("kafka.consumerGroup.list.flag.size")) _ = cmd.RegisterFlagCompletionFunc("topic", func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdutil.FilterValidTopicNameArgs(f, toComplete) + return kafkacmdutil.FilterValidTopicNameArgs(f, toComplete) }) flagutil.EnableOutputFlagCompletion(cmd) diff --git a/pkg/cmd/kafka/consumergroup/resetoffset/reset_offset.go b/pkg/cmd/kafka/consumergroup/resetoffset/reset_offset.go index c4ecb3f31..895f55030 100644 --- a/pkg/cmd/kafka/consumergroup/resetoffset/reset_offset.go +++ b/pkg/cmd/kafka/consumergroup/resetoffset/reset_offset.go @@ -7,12 +7,12 @@ import ( "github.com/redhat-developer/app-services-cli/pkg/icon" "github.com/AlecAivazis/survey/v2" + kafkacmdutil "github.com/redhat-developer/app-services-cli/pkg/kafka/cmdutil" kafkainstanceclient "github.com/redhat-developer/app-services-sdk-go/kafkainstance/apiv1internal/client" "github.com/spf13/cobra" "github.com/redhat-developer/app-services-cli/internal/config" "github.com/redhat-developer/app-services-cli/pkg/cmd/factory" - "github.com/redhat-developer/app-services-cli/pkg/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/cmdutil/flagutil" "github.com/redhat-developer/app-services-cli/pkg/connection" "github.com/redhat-developer/app-services-cli/pkg/iostreams" @@ -107,12 +107,12 @@ func NewResetOffsetConsumerGroupCommand(f *factory.Factory) *cobra.Command { // flag based completions for ID _ = cmd.RegisterFlagCompletionFunc("id", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdutil.FilterValidConsumerGroupIDs(f, toComplete) + return kafkacmdutil.FilterValidConsumerGroupIDs(f, toComplete) }) // flag based completions for topic _ = cmd.RegisterFlagCompletionFunc("topic", func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdutil.FilterValidTopicNameArgs(f, toComplete) + return kafkacmdutil.FilterValidTopicNameArgs(f, toComplete) }) flagutil.EnableOutputFlagCompletion(cmd) diff --git a/pkg/cmd/kafka/topic/delete/delete.go b/pkg/cmd/kafka/topic/delete/delete.go index b64e2167c..353b21dd6 100644 --- a/pkg/cmd/kafka/topic/delete/delete.go +++ b/pkg/cmd/kafka/topic/delete/delete.go @@ -7,8 +7,8 @@ import ( "github.com/AlecAivazis/survey/v2" "github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/flagutil" - "github.com/redhat-developer/app-services-cli/pkg/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/connection" + kafkacmdutil "github.com/redhat-developer/app-services-cli/pkg/kafka/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/localize" "github.com/redhat-developer/app-services-cli/pkg/iostreams" @@ -82,7 +82,7 @@ func NewDeleteTopicCommand(f *factory.Factory) *cobra.Command { _ = cmd.MarkFlagRequired("name") _ = cmd.RegisterFlagCompletionFunc("name", func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdutil.FilterValidTopicNameArgs(f, toComplete) + return kafkacmdutil.FilterValidTopicNameArgs(f, toComplete) }) flags.AddYes(&opts.force) diff --git a/pkg/cmd/kafka/topic/describe/describe.go b/pkg/cmd/kafka/topic/describe/describe.go index a8e557675..3bd99db25 100644 --- a/pkg/cmd/kafka/topic/describe/describe.go +++ b/pkg/cmd/kafka/topic/describe/describe.go @@ -4,8 +4,8 @@ import ( "context" "net/http" - "github.com/redhat-developer/app-services-cli/pkg/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/connection" + kafkacmdutil "github.com/redhat-developer/app-services-cli/pkg/kafka/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/localize" "github.com/redhat-developer/app-services-cli/pkg/cmd/flag" @@ -84,7 +84,7 @@ func NewDescribeTopicCommand(f *factory.Factory) *cobra.Command { flags.StringVar(&opts.name, "name", "", opts.localizer.MustLocalize("kafka.topic.common.flag.output.description")) _ = cmd.RegisterFlagCompletionFunc("name", func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdutil.FilterValidTopicNameArgs(f, toComplete) + return kafkacmdutil.FilterValidTopicNameArgs(f, toComplete) }) _ = cmd.MarkFlagRequired("name") diff --git a/pkg/cmd/kafka/topic/update/update.go b/pkg/cmd/kafka/topic/update/update.go index c583cf377..d875a9731 100644 --- a/pkg/cmd/kafka/topic/update/update.go +++ b/pkg/cmd/kafka/topic/update/update.go @@ -7,8 +7,8 @@ import ( "github.com/AlecAivazis/survey/v2" - "github.com/redhat-developer/app-services-cli/pkg/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/connection" + kafkacmdutil "github.com/redhat-developer/app-services-cli/pkg/kafka/cmdutil" "github.com/redhat-developer/app-services-cli/pkg/localize" "github.com/redhat-developer/app-services-cli/pkg/cmdutil/flagutil" @@ -158,7 +158,7 @@ func NewUpdateTopicCommand(f *factory.Factory) *cobra.Command { flags.StringVar(&opts.name, "name", "", opts.localizer.MustLocalize("kafka.topic.common.flag.name.description")) _ = cmd.RegisterFlagCompletionFunc("name", func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cmdutil.FilterValidTopicNameArgs(f, toComplete) + return kafkacmdutil.FilterValidTopicNameArgs(f, toComplete) }) _ = cmd.MarkFlagRequired("name") diff --git a/pkg/cmd/kafka/update/update.go b/pkg/cmd/kafka/update/update.go index abf390935..8cbe27d22 100644 --- a/pkg/cmd/kafka/update/update.go +++ b/pkg/cmd/kafka/update/update.go @@ -119,7 +119,7 @@ func NewUpdateCommand(f *factory.Factory) *cobra.Command { flags.StringVar(&opts.name, "name", "", opts.localizer.MustLocalize("kafka.update.flag.name")) _ = kafkacmdutil.RegisterNameFlagCompletionFunc(cmd, f) - _ = flagutil.RegisterUserCompletionFunc(cmd, "owner", f.Connection) + _ = flagutil.RegisterUserCompletionFunc(cmd, "owner", f) return cmd } diff --git a/pkg/cmdutil/cmdutil.go b/pkg/cmdutil/cmdutil.go index 75b44bfde..44562b06a 100644 --- a/pkg/cmdutil/cmdutil.go +++ b/pkg/cmdutil/cmdutil.go @@ -3,98 +3,8 @@ package cmdutil import ( "fmt" "strconv" - - "github.com/spf13/cobra" - - "github.com/redhat-developer/app-services-cli/pkg/cmd/factory" - "github.com/redhat-developer/app-services-cli/pkg/connection" ) -// FilterValidTopicNameArgs filters topics from the API and returns the names -// This is used in for dynamic completion of topic names -func FilterValidTopicNameArgs(f *factory.Factory, toComplete string) (validNames []string, directive cobra.ShellCompDirective) { - validNames = []string{} - directive = cobra.ShellCompDirectiveNoSpace - - cfg, err := f.Config.Load() - if err != nil { - return validNames, directive - } - - instanceID, ok := cfg.GetKafkaIdOk() - if !ok { - return validNames, directive - } - - conn, err := f.Connection(connection.DefaultConfigRequireMasAuth) - if err != nil { - return validNames, directive - } - - api, _, err := conn.API().KafkaAdmin(instanceID) - if err != nil { - return validNames, directive - } - req := api.TopicsApi.GetTopics(f.Context) - if toComplete != "" { - req = req.Filter(toComplete) - } - - topicRes, _, err := req.Execute() - if err != nil { - return validNames, directive - } - - items := topicRes.GetItems() - for _, topic := range items { - validNames = append(validNames, topic.GetName()) - } - - return validNames, directive -} - -// FilterValidConsumerGroups returns the list of consumer group IDs from the API -func FilterValidConsumerGroupIDs(f *factory.Factory, toComplete string) (validIDs []string, directive cobra.ShellCompDirective) { - validIDs = []string{} - directive = cobra.ShellCompDirectiveNoSpace - - cfg, err := f.Config.Load() - if err != nil { - return validIDs, directive - } - - instanceID, ok := cfg.GetKafkaIdOk() - if !ok { - return validIDs, directive - } - - conn, err := f.Connection(connection.DefaultConfigRequireMasAuth) - if err != nil { - return validIDs, directive - } - - api, _, err := conn.API().KafkaAdmin(instanceID) - if err != nil { - return validIDs, directive - } - req := api.GroupsApi.GetConsumerGroups(f.Context) - if toComplete != "" { - req = req.GroupIdFilter(toComplete) - } - - cgRes, _, err := req.Execute() - if err != nil { - return validIDs, directive - } - - items := cgRes.GetItems() - for _, cg := range items { - validIDs = append(validIDs, cg.GetGroupId()) - } - - return validIDs, directive -} - func ConvertPageValueToInt32(s string) int32 { val, err := strconv.ParseInt(s, 10, 32) diff --git a/pkg/cmdutil/flagutil/completions.go b/pkg/cmdutil/flagutil/completions.go index c5419e931..fe1171a49 100644 --- a/pkg/cmdutil/flagutil/completions.go +++ b/pkg/cmdutil/flagutil/completions.go @@ -7,6 +7,7 @@ import ( "github.com/redhat-developer/app-services-cli/pkg/api/rbac/rbacutil" "github.com/redhat-developer/app-services-cli/pkg/cmd/factory" "github.com/redhat-developer/app-services-cli/pkg/connection" + kafkacmdutil "github.com/redhat-developer/app-services-cli/pkg/kafka/cmdutil" "github.com/spf13/cobra" ) @@ -29,12 +30,12 @@ func EnableOutputFlagCompletion(cmd *cobra.Command) { } // RegisterUserCompletionFunc adds the user list to flag dynamic completion -func RegisterUserCompletionFunc(cmd *cobra.Command, flagName string, connFunc factory.ConnectionFunc) error { +func RegisterUserCompletionFunc(cmd *cobra.Command, flagName string, f *factory.Factory) error { return cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { var usernames []string directive := cobra.ShellCompDirectiveNoSpace - conn, err := connFunc(connection.DefaultConfigSkipMasAuth) + conn, err := f.Connection(connection.DefaultConfigSkipMasAuth) if err != nil { return usernames, directive } @@ -53,13 +54,13 @@ func RegisterUserCompletionFunc(cmd *cobra.Command, flagName string, connFunc fa }) } -// RegisterNameFlagCompletionFunc adds the user list to flag dynamic completion -func RegisterServiceAccountCompletionFunc(cmd *cobra.Command, flagName string, connFunc factory.ConnectionFunc) error { - return cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { +// RegisterServiceAccountCompletionFunc adds the service account list to flag dynamic completion +func RegisterServiceAccountCompletionFunc(cmd *cobra.Command, f *factory.Factory) error { + return cmd.RegisterFlagCompletionFunc("service-account", func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { var emptyList []string directive := cobra.ShellCompDirectiveNoSpace - conn, err := connFunc(connection.DefaultConfigSkipMasAuth) + conn, err := f.Connection(connection.DefaultConfigSkipMasAuth) if err != nil { return emptyList, directive } @@ -84,3 +85,17 @@ func RegisterServiceAccountCompletionFunc(cmd *cobra.Command, flagName string, c return cachedServiceAccounts, directive }) } + +// RegisterTopicCompletionFunc enables dynamic autocompletion for topic flag +func RegisterTopicCompletionFunc(cmd *cobra.Command, f *factory.Factory) error { + return cmd.RegisterFlagCompletionFunc("topic", func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return kafkacmdutil.FilterValidTopicNameArgs(f, toComplete) + }) +} + +// RegisterGroupCompletionFunc enables dynamic autocompletion for group flag +func RegisterGroupCompletionFunc(cmd *cobra.Command, f *factory.Factory) error { + return cmd.RegisterFlagCompletionFunc("group", func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return kafkacmdutil.FilterValidConsumerGroupIDs(f, toComplete) + }) +} diff --git a/pkg/kafka/cmdutil/util.go b/pkg/kafka/cmdutil/util.go index c2ab40a7e..6a5923a22 100644 --- a/pkg/kafka/cmdutil/util.go +++ b/pkg/kafka/cmdutil/util.go @@ -90,3 +90,88 @@ func GetEnabledCloudRegionIDs(regions []kafkamgmtclient.CloudRegion) []string { } return regionIDs } + +// FilterValidTopicNameArgs filters topics from the API and returns the names +// This is used in for dynamic completion of topic names +func FilterValidTopicNameArgs(f *factory.Factory, toComplete string) (validNames []string, directive cobra.ShellCompDirective) { + validNames = []string{} + directive = cobra.ShellCompDirectiveNoSpace + + cfg, err := f.Config.Load() + if err != nil { + return validNames, directive + } + + instanceID, ok := cfg.GetKafkaIdOk() + if !ok { + return validNames, directive + } + + conn, err := f.Connection(connection.DefaultConfigRequireMasAuth) + if err != nil { + return validNames, directive + } + + api, _, err := conn.API().KafkaAdmin(instanceID) + if err != nil { + return validNames, directive + } + req := api.TopicsApi.GetTopics(f.Context) + if toComplete != "" { + req = req.Filter(toComplete) + } + + topicRes, _, err := req.Execute() + if err != nil { + return validNames, directive + } + + items := topicRes.GetItems() + for _, topic := range items { + validNames = append(validNames, topic.GetName()) + } + + return validNames, directive +} + +// FilterValidConsumerGroups returns the list of consumer group IDs from the API +func FilterValidConsumerGroupIDs(f *factory.Factory, toComplete string) (validIDs []string, directive cobra.ShellCompDirective) { + validIDs = []string{} + directive = cobra.ShellCompDirectiveNoSpace + + cfg, err := f.Config.Load() + if err != nil { + return validIDs, directive + } + + instanceID, ok := cfg.GetKafkaIdOk() + if !ok { + return validIDs, directive + } + + conn, err := f.Connection(connection.DefaultConfigRequireMasAuth) + if err != nil { + return validIDs, directive + } + + api, _, err := conn.API().KafkaAdmin(instanceID) + if err != nil { + return validIDs, directive + } + req := api.GroupsApi.GetConsumerGroups(f.Context) + if toComplete != "" { + req = req.GroupIdFilter(toComplete) + } + + cgRes, _, err := req.Execute() + if err != nil { + return validIDs, directive + } + + items := cgRes.GetItems() + for _, cg := range items { + validIDs = append(validIDs, cg.GetGroupId()) + } + + return validIDs, directive +} diff --git a/pkg/localize/locales/en/cmd/acl.en.toml b/pkg/localize/locales/en/cmd/acl.en.toml index 8bfab6b82..24d06f73f 100644 --- a/pkg/localize/locales/en/cmd/acl.en.toml +++ b/pkg/localize/locales/en/cmd/acl.en.toml @@ -126,6 +126,15 @@ $ rhoas kafka acl list --user foo_user # Display Kafka ACL rules for a specific service account $ rhoas kafka acl list --service-account srvc-acct-f20a7561-7426-4f5a-b5e7-0ef2db31e15b + +# Display Kafka ACL rules for a specific topic +$ rhoas kafka acl list --topic foo_topic_name + +# Display Kafka ACL rules for a specific consumer group +$ rhoas kafka acl list --group foo_group_id + +# Display Kafka ACL rules for a specific consumer group and user +$ rhoas kafka acl list --group foo_group_id --user foo_user ''' [kafka.acl.list.cmd.shortDescription] @@ -151,8 +160,14 @@ These ACLs allow all accounts in the organization to view the Kafka instance per The ACLs are displayed in a table by default. Alternatively, you can display them as JSON or YAML. ''' -[kafka.acl.list.flag.output.description] -one = 'Format in which to display the Kafka ACL rules (choose from: "json", "yml", "yaml")' +[kafka.acl.list.flag.cluster.description] +one = 'Set filter to cluster resource' + +[kafka.acl.list.flag.topic.description] +one = 'Text search to filter ACL rules for topics by name' + +[kafka.acl.list.flag.group.description] +one = 'Text search to filter ACL rules for consumer groups by id' [kafka.acl.list.allAccounts] one = 'All Accounts' @@ -163,6 +178,12 @@ one = 'is' [kafka.acl.list.startsWith] one = 'starts with' +[kafka.acl.list.log.info.noACLs] +one = 'No ACLs found in Kafka instance "{{.InstanceName}}"' + +[kafka.acl.list.error.oneResourceTypeAllowed] +one = 'provide only one resource type from "--{{.ClusterFlag}}", "--{{.TopicFlag}}" or "--{{.GroupFlag}}"' + [kafka.acl.grantPermissions] [kafka.acl.grantPermissions.cmd.shortDescription]