Browse code

provide the ability to modify the prefix of error reporting and ignore some errors in cmd operations

pweil- authored on 2016/10/28 22:33:58
Showing 2 changed files
... ...
@@ -711,7 +711,7 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write
711 711
 		objects = append(objects,
712 712
 			&kapi.ServiceAccount{ObjectMeta: kapi.ObjectMeta{Name: cfg.ServiceAccount}},
713 713
 			&authapi.ClusterRoleBinding{
714
-				ObjectMeta: kapi.ObjectMeta{Name: fmt.Sprintf("router-%s-role", cfg.Name)},
714
+				ObjectMeta: kapi.ObjectMeta{Name: generateRoleBindingName(cfg.Name)},
715 715
 				Subjects: []kapi.ObjectReference{
716 716
 					{
717 717
 						Kind:      "ServiceAccount",
... ...
@@ -789,12 +789,49 @@ func RunCmdRouter(f *clientcmd.Factory, cmd *cobra.Command, out, errout io.Write
789 789
 		return defaultOutputErr
790 790
 	}
791 791
 
792
-	if errs := cfg.Action.WithMessage(fmt.Sprintf("Creating router %s", cfg.Name), "created").Run(list, namespace); len(errs) > 0 {
792
+	levelPrefixFilter := func(e error) string {
793
+		// only ignore SA/RB errors if we were creating the service account
794
+		if createServiceAccount && ignoreError(e, cfg.ServiceAccount, generateRoleBindingName(cfg.Name)) {
795
+			return "warning"
796
+		}
797
+		return "error"
798
+	}
799
+
800
+	cfg.Action.Bulk.IgnoreError = func(e error) bool {
801
+		return levelPrefixFilter(e) == "warning"
802
+	}
803
+
804
+	if errs := cfg.Action.WithMessageAndPrefix(fmt.Sprintf("Creating router %s", cfg.Name), "created", levelPrefixFilter).Run(list, namespace); len(errs) > 0 {
793 805
 		return cmdutil.ErrExit
794 806
 	}
795 807
 	return nil
796 808
 }
797 809
 
810
+// ignoreError will return true if the error is an already exists status error and
811
+// 1. it is for a cluster role binding named roleBindingName
812
+// 2. it is for a serivce account name saName
813
+func ignoreError(e error, saName string, roleBindingName string) bool {
814
+	if !errors.IsAlreadyExists(e) {
815
+		return false
816
+	}
817
+	statusError, ok := e.(*errors.StatusError)
818
+	if !ok {
819
+		return false
820
+	}
821
+	details := statusError.Status().Details
822
+	if details == nil {
823
+		return false
824
+	}
825
+	return (details.Kind == "serviceaccounts" && details.Name == saName) ||
826
+		(details.Kind == "clusterrolebinding" && details.Name == roleBindingName)
827
+}
828
+
829
+// generateRoleBindingName generates a name for the rolebinding object if it is
830
+// being created.
831
+func generateRoleBindingName(name string) string {
832
+	return fmt.Sprintf("router-%s-role", name)
833
+}
834
+
798 835
 // generateStatsPassword creates a random password.
799 836
 func generateStatsPassword() string {
800 837
 	allowableChars := []rune("abcdefghijlkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
... ...
@@ -35,13 +35,21 @@ type Mapper interface {
35 35
 	InfoForObject(obj runtime.Object, preferredGVKs []unversioned.GroupVersionKind) (*resource.Info, error)
36 36
 }
37 37
 
38
+// IgnoreErrorFunc provides a way to filter errors during the Bulk.Run.  If this function returns
39
+// true the error will NOT be added to the slice of errors returned by Bulk.Run.
40
+//
41
+// This may be used in conjunction with
42
+// BulkAction.WithMessageAndPrefix if you are reporting some errors as warnings.
43
+type IgnoreErrorFunc func(e error) bool
44
+
38 45
 // Bulk provides helpers for iterating over a list of items
39 46
 type Bulk struct {
40 47
 	Mapper Mapper
41 48
 
42
-	Op    OpFunc
43
-	After AfterFunc
44
-	Retry RetryFunc
49
+	Op          OpFunc
50
+	After       AfterFunc
51
+	Retry       RetryFunc
52
+	IgnoreError IgnoreErrorFunc
45 53
 }
46 54
 
47 55
 // Create attempts to create each item generically, gathering all errors in the
... ...
@@ -52,6 +60,10 @@ func (b *Bulk) Run(list *kapi.List, namespace string) []error {
52 52
 	if after == nil {
53 53
 		after = func(*resource.Info, error) bool { return false }
54 54
 	}
55
+	ignoreError := b.IgnoreError
56
+	if ignoreError == nil {
57
+		ignoreError = func(e error) bool { return false }
58
+	}
55 59
 
56 60
 	errs := []error{}
57 61
 	for i, item := range list.Items {
... ...
@@ -70,7 +82,9 @@ func (b *Bulk) Run(list *kapi.List, namespace string) []error {
70 70
 			}
71 71
 		}
72 72
 		if err != nil {
73
-			errs = append(errs, err)
73
+			if !ignoreError(err) {
74
+				errs = append(errs, err)
75
+			}
74 76
 			if after(info, err) {
75 77
 				break
76 78
 			}
... ...
@@ -85,22 +99,22 @@ func (b *Bulk) Run(list *kapi.List, namespace string) []error {
85 85
 	return errs
86 86
 }
87 87
 
88
-func NewPrintNameOrErrorAfterIndent(mapper meta.RESTMapper, short bool, operation string, out, errs io.Writer, dryRun bool, indent string) AfterFunc {
88
+func NewPrintNameOrErrorAfterIndent(mapper meta.RESTMapper, short bool, operation string, out, errs io.Writer, dryRun bool, indent string, prefixForError PrefixForError) AfterFunc {
89 89
 	return func(info *resource.Info, err error) bool {
90 90
 		if err == nil {
91 91
 			fmt.Fprintf(out, indent)
92 92
 			cmdutil.PrintSuccess(mapper, short, out, info.Mapping.Resource, info.Name, dryRun, operation)
93 93
 		} else {
94
-			fmt.Fprintf(errs, "%serror: %v\n", indent, err)
94
+			fmt.Fprintf(errs, "%s%s: %v\n", indent, prefixForError(err), err)
95 95
 		}
96 96
 		return false
97 97
 	}
98 98
 }
99 99
 
100
-func NewPrintErrorAfter(mapper meta.RESTMapper, errs io.Writer) func(*resource.Info, error) bool {
100
+func NewPrintErrorAfter(mapper meta.RESTMapper, errs io.Writer, prefixForError PrefixForError) func(*resource.Info, error) bool {
101 101
 	return func(info *resource.Info, err error) bool {
102 102
 		if err != nil {
103
-			fmt.Fprintf(errs, "error: %v\n", err)
103
+			fmt.Fprintf(errs, "%s: %v\n", prefixForError(err), err)
104 104
 		}
105 105
 		return false
106 106
 	}
... ...
@@ -186,17 +200,20 @@ func (b *BulkAction) DefaultIndent() string {
186 186
 	return ""
187 187
 }
188 188
 
189
-func (b BulkAction) WithMessage(action, individual string) Runner {
189
+// PrefixForError allows customization of the prefix that will be printed for any error that occurs in the BulkAction.
190
+type PrefixForError func(e error) string
191
+
192
+func (b BulkAction) WithMessageAndPrefix(action, individual string, prefixForError PrefixForError) Runner {
190 193
 	b.Action = action
191 194
 	switch {
192 195
 	// TODO: this should be b printer
193 196
 	case b.Output == "":
194
-		b.Bulk.After = NewPrintNameOrErrorAfterIndent(b.Bulk.Mapper, false, individual, b.Out, b.ErrOut, b.DryRun, b.DefaultIndent())
197
+		b.Bulk.After = NewPrintNameOrErrorAfterIndent(b.Bulk.Mapper, false, individual, b.Out, b.ErrOut, b.DryRun, b.DefaultIndent(), prefixForError)
195 198
 	// TODO: needs to be unified with the name printer (incremental vs exact execution), possibly by creating b synthetic printer?
196 199
 	case b.Output == "name":
197
-		b.Bulk.After = NewPrintNameOrErrorAfterIndent(b.Bulk.Mapper, true, individual, b.Out, b.ErrOut, b.DryRun, b.DefaultIndent())
200
+		b.Bulk.After = NewPrintNameOrErrorAfterIndent(b.Bulk.Mapper, true, individual, b.Out, b.ErrOut, b.DryRun, b.DefaultIndent(), prefixForError)
198 201
 	default:
199
-		b.Bulk.After = NewPrintErrorAfter(b.Bulk.Mapper, b.ErrOut)
202
+		b.Bulk.After = NewPrintErrorAfter(b.Bulk.Mapper, b.ErrOut, prefixForError)
200 203
 		if b.StopOnError {
201 204
 			b.Bulk.After = HaltOnError(b.Bulk.After)
202 205
 		}
... ...
@@ -204,6 +221,10 @@ func (b BulkAction) WithMessage(action, individual string) Runner {
204 204
 	return &b
205 205
 }
206 206
 
207
+func (b BulkAction) WithMessage(action, individual string) Runner {
208
+	return b.WithMessageAndPrefix(action, individual, func(e error) string { return "error" })
209
+}
210
+
207 211
 func (b *BulkAction) Run(list *kapi.List, namespace string) []error {
208 212
 	run := b.Bulk
209 213