Skip to content

Commit 8840632

Browse files
author
Enda Phelan
committed
feat: add confirmation dialog
1 parent 75e69fc commit 8840632

File tree

14 files changed

+203
-122
lines changed

14 files changed

+203
-122
lines changed

cmd/rhoas/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func initConfig(f *factory.Factory) error {
9191
func rootError(err error, localizer localize.Localizer) error {
9292
prefix := icon.ErrorPrefix()
9393
errMessage := err.Error()
94-
if prefix == icon.CrossMark {
94+
if prefix == icon.ErrorSymbol {
9595
errMessage = firstCharToUpper(errMessage)
9696
}
9797
return fmt.Errorf("%v %v. %v", icon.ErrorPrefix(), errMessage, localizer.MustLocalize("common.log.error.verboseModeHint"))

docs/commands/rhoas_kafka_acl.adoc

Lines changed: 3 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/commands/rhoas_kafka_acl_delete.adoc

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cmd/kafka/acl/acl.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package acl
22

33
import (
44
"github.com/redhat-developer/app-services-cli/pkg/cmd/factory"
5-
"github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/acl/grant"
65
"github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/acl/delete"
76
"github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/acl/grant"
87
"github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/acl/list"

pkg/cmd/kafka/acl/common/enums.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ var resourceTypeFilterMap = map[string]kafkainstanceclient.AclResourceTypeFilter
1212

1313
var operationFilterMap = map[string]kafkainstanceclient.AclOperationFilter{
1414
OperationFilterALL: kafkainstanceclient.ACLOPERATIONFILTER_ALL,
15-
OperationFilterANY: kafkainstanceclient.ACLOPERATIONFILTER_ANY,
1615
OperationFilterREAD: kafkainstanceclient.ACLOPERATIONFILTER_READ,
1716
OperationFilterWRITE: kafkainstanceclient.ACLOPERATIONFILTER_WRITE,
1817
OperationFilterCREATE: kafkainstanceclient.ACLOPERATIONFILTER_DELETE,

pkg/cmd/kafka/acl/common/flags.go

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ const (
2323

2424
const (
2525
OperationFilterALL = "all"
26-
OperationFilterANY = "any"
2726
OperationFilterREAD = "read"
2827
OperationFilterWRITE = "write"
2928
OperationFilterCREATE = "create"
@@ -41,9 +40,10 @@ const (
4140
)
4241

4342
const (
44-
TopicFlagName = "topic"
45-
GroupFlagName = "group"
46-
TransactionalIDFlag = "transactional-id"
43+
ClusterFlagName = "cluster"
44+
TopicFlagName = "topic"
45+
GroupFlagName = "group"
46+
TransactionalIDFlagName = "transactional-id"
4747
)
4848

4949
type flagSet struct {
@@ -157,7 +157,7 @@ func (fs *flagSet) AddConsumerGroup(group *string) {
157157

158158
// AddTransactionalID adds a flag for setting the consumer group ID
159159
func (fs *flagSet) AddTransactionalID(id *string) {
160-
flagName := TransactionalIDFlag
160+
flagName := TransactionalIDFlagName
161161

162162
fs.flags.StringVar(
163163
id,
@@ -179,28 +179,16 @@ func (fs *flagSet) AddPrefix(prefix *bool) {
179179
)
180180
}
181181

182-
// AddPatternType adds a flag to choose the ACL resource pattern type
183-
func (fs *flagSet) AddPatternType(patternType *string) *markRequiredOpt {
184-
flagName := "pattern-type"
182+
// AddPrefix adds a flag for sertting the "cluster" ACL resource type
183+
func (fs *flagSet) AddCluster(prefix *bool) {
184+
flagName := ClusterFlagName
185185

186-
// TODO: Add available options to description
187-
fs.flags.StringVar(
188-
patternType,
186+
fs.flags.BoolVar(
187+
prefix,
189188
flagName,
190-
"",
191-
"Determine the pattern type of the ACL resource",
189+
false,
190+
"Cluster ACL resource type",
192191
)
193-
194-
patternTypes := make([]string, 0, len(patternTypeFilterMap))
195-
for i := range patternTypeFilterMap {
196-
patternTypes = append(patternTypes, i)
197-
}
198-
199-
_ = fs.cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
200-
return patternTypes, cobra.ShellCompDirectiveNoSpace
201-
})
202-
203-
return withMarkRequiredFunc(fs.cmd, flagName)
204192
}
205193

206194
// AddUser adds a flag to pass a user ID principal

pkg/cmd/kafka/acl/common/util.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package common
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/redhat-developer/app-services-cli/pkg/kafka/acl"
8+
"github.com/redhat-developer/app-services-cli/pkg/localize"
9+
kafkainstanceclient "github.com/redhat-developer/app-services-sdk-go/kafkainstance/apiv1internal/client"
10+
)
11+
12+
type permissionsRow struct {
13+
Principal string `json:"principal,omitempty" header:"Principal"`
14+
Permission string `json:"permission,omitempty" header:"Permission"`
15+
Operation string `json:"operation,omitempty" header:"Operation"`
16+
Description string `json:"description,omitempty" header:"description"`
17+
}
18+
19+
func MapACLsToTable(bindings []kafkainstanceclient.AclBinding, localizer localize.Localizer) []permissionsRow {
20+
rows := make([]permissionsRow, len(bindings))
21+
22+
for i, p := range bindings {
23+
24+
description := buildDescription(p.PatternType, localizer)
25+
row := permissionsRow{
26+
Principal: formatPrincipal(p.GetPrincipal(), localizer),
27+
Permission: string(p.GetPermission()),
28+
Operation: string(p.GetOperation()),
29+
Description: fmt.Sprintf("%s %s \"%s\"", p.GetResourceType(), description, p.GetResourceName()),
30+
}
31+
rows[i] = row
32+
}
33+
return rows
34+
}
35+
36+
func formatPrincipal(principal string, localizer localize.Localizer) string {
37+
s := strings.Split(principal, ":")[1]
38+
39+
if s == acl.Wildcard {
40+
return localizer.MustLocalize("kafka.acl.list.allAccounts")
41+
}
42+
43+
return s
44+
}
45+
46+
func buildDescription(patternType kafkainstanceclient.AclPatternType, localizer localize.Localizer) string {
47+
if patternType == kafkainstanceclient.ACLPATTERNTYPE_LITERAL {
48+
return localizer.MustLocalize("kafka.acl.list.is")
49+
}
50+
51+
return localizer.MustLocalize("kafka.acl.list.startsWith")
52+
}
53+
54+
// FormatPrincipal formats the provided principal ID to "User:principal"
55+
func FormatPrincipal(userID string) string {
56+
return fmt.Sprintf("User:%s", userID)
57+
}

pkg/cmd/kafka/acl/delete/delete.go

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,26 @@ package delete
33
import (
44
"context"
55

6+
"github.com/AlecAivazis/survey/v2"
67
"github.com/redhat-developer/app-services-cli/internal/config"
78
"github.com/redhat-developer/app-services-cli/pkg/cmd/factory"
89
"github.com/redhat-developer/app-services-cli/pkg/cmd/kafka/acl/common"
910
"github.com/redhat-developer/app-services-cli/pkg/cmdutil"
1011
"github.com/redhat-developer/app-services-cli/pkg/connection"
12+
"github.com/redhat-developer/app-services-cli/pkg/dump"
13+
"github.com/redhat-developer/app-services-cli/pkg/icon"
1114
"github.com/redhat-developer/app-services-cli/pkg/iostreams"
1215
"github.com/redhat-developer/app-services-cli/pkg/localize"
1316
"github.com/redhat-developer/app-services-cli/pkg/logging"
1417
kafkainstanceclient "github.com/redhat-developer/app-services-sdk-go/kafkainstance/apiv1internal/client"
1518
"github.com/spf13/cobra"
1619
)
1720

21+
var (
22+
serviceAccount string
23+
userID string
24+
)
25+
1826
type options struct {
1927
Config config.IConfig
2028
Connection factory.ConnectionFunc
@@ -32,10 +40,10 @@ type options struct {
3240
group string
3341
topic string
3442
transactionalID string
35-
userID string
36-
serviceAccount string
43+
principal string
3744

38-
output string
45+
skipConfirm bool
46+
output string
3947
}
4048

4149
// NewDeleteCommand creates a new command to list Kafka ACL rules
@@ -82,14 +90,30 @@ func NewDeleteCommand(f *factory.Factory) *cobra.Command {
8290
resourceTypeFlagEntries := []*localize.TemplateEntry{
8391
localize.NewEntry("ClusterFlag", common.ClusterFlagName),
8492
localize.NewEntry("TopicFlag", common.TopicFlagName),
85-
localize.NewEntry("TransactionalIDFlag", common.TopicFlagName),
93+
localize.NewEntry("TransactionalIDFlag", common.TransactionalIDFlagName),
8694
localize.NewEntry("GroupFlag", common.GroupFlagName),
8795
}
8896

8997
if selectedResourceTypeCount != 1 {
9098
return opts.localizer.MustLocalizeError("kafka.acl.common.error.oneResourceTypeAllowed", resourceTypeFlagEntries...)
9199
}
92100

101+
// check if priincipal is provided
102+
if userID == "" && serviceAccount == "" {
103+
return opts.localizer.MustLocalizeError("kafka.acl.common.error.noPrincipalsSelected")
104+
}
105+
106+
// user and service account should not be provided together
107+
if userID != "" && serviceAccount != "" {
108+
return opts.localizer.MustLocalizeError("kafka.acl.common.error.bothPrincipalsSelected")
109+
}
110+
111+
if userID != "" {
112+
opts.principal = userID
113+
} else {
114+
opts.principal = serviceAccount
115+
}
116+
93117
cfg, err := opts.Config.Load()
94118
if err != nil {
95119
return err
@@ -109,16 +133,16 @@ func NewDeleteCommand(f *factory.Factory) *cobra.Command {
109133

110134
_ = fs.AddPermission(&opts.permission).Required()
111135
_ = fs.AddOperation(&opts.operation).Required()
112-
// TODO: Should not be required when resource type is cluster
113136

114137
fs.AddCluster(&opts.cluster)
115138
fs.AddPrefix(&opts.prefix)
116139
fs.AddTopic(&opts.topic)
117140
fs.AddConsumerGroup(&opts.group)
118141
fs.AddTransactionalID(&opts.transactionalID)
119142
fs.AddOutput(&opts.output)
120-
fs.AddUser(&opts.userID)
121-
fs.AddServiceAccount(&opts.serviceAccount)
143+
fs.AddUser(&userID)
144+
fs.AddServiceAccount(&serviceAccount)
145+
fs.AddYes(&opts.skipConfirm)
122146

123147
return cmd
124148
}
@@ -157,10 +181,50 @@ func runDelete(instanceID string, opts *options) error {
157181
if opts.prefix {
158182
patternType = kafkainstanceclient.ACLPATTERNTYPEFILTER_PREFIXED
159183
}
184+
185+
// TODO ensure ALL ACLs are fetched
186+
aclList, httpRes, err := adminAPI.AclsApi.GetAcls(opts.Context).
187+
ResourceType(common.GetResourceTypeFilter(opts.resourceType)).
188+
Principal(common.FormatPrincipal(opts.principal)).
189+
PatternType(patternType).
190+
ResourceName(opts.resourceName).
191+
Operation(common.GetOperationFilter(opts.operation)).
192+
Permission(common.GetPermissionFilter(opts.permission)).
193+
Execute()
194+
if httpRes != nil {
195+
defer httpRes.Body.Close()
196+
}
197+
if err != nil {
198+
return err
199+
}
200+
if aclList.GetTotal() == 0 {
201+
opts.Logger.Info("No ACLs matching this criteria to delete")
202+
return nil
203+
}
204+
205+
rows := common.MapACLsToTable(aclList.GetItems(), opts.localizer)
206+
opts.Logger.Info(icon.Warning(), "The following ACLs will be deleted:")
207+
opts.Logger.Info()
208+
dump.Table(opts.IO.ErrOut, rows)
209+
opts.Logger.Info()
210+
211+
if !opts.skipConfirm {
212+
prompt := &survey.Confirm{
213+
Message: "Are you sure you want to delete these ACLs?",
214+
}
215+
if err = survey.AskOne(prompt, &opts.skipConfirm); err != nil {
216+
return err
217+
}
218+
219+
if !opts.skipConfirm {
220+
opts.Logger.Debug("User has not confirmed there wish to delete ACLs, returning")
221+
return nil
222+
}
223+
}
224+
160225
bindingList, httpRes, err := adminAPI.AclsApi.DeleteAcls(opts.Context).
161226
ResourceType(common.GetResourceTypeFilter(opts.resourceType)).
162-
// TODO: Allow adding of principal via arguments
163-
Principal("User:enda1").
227+
Principal(common.FormatPrincipal(opts.principal)).
164228
PatternType(patternType).
165229
ResourceName(opts.resourceName).
166230
Operation(common.GetOperationFilter(opts.operation)).
@@ -175,7 +239,7 @@ func runDelete(instanceID string, opts *options) error {
175239
return err
176240
}
177241

178-
opts.Logger.Info("Deleted", len(bindingList.GetItems()), "ACLs")
242+
opts.Logger.Info("Deleted", bindingList.GetTotal(), "ACL(s)")
179243

180244
return nil
181245
}

0 commit comments

Comments
 (0)