...
1
16
17 package internal
18
19 import (
20 "encoding/json"
21 "fmt"
22 "sort"
23 "strings"
24 "time"
25
26 "k8s.io/apimachinery/pkg/api/errors"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
29 "sigs.k8s.io/structured-merge-diff/v4/merge"
30 )
31
32
33 func NewConflictError(conflicts merge.Conflicts) *errors.StatusError {
34 causes := []metav1.StatusCause{}
35 for _, conflict := range conflicts {
36 causes = append(causes, metav1.StatusCause{
37 Type: metav1.CauseTypeFieldManagerConflict,
38 Message: fmt.Sprintf("conflict with %v", printManager(conflict.Manager)),
39 Field: conflict.Path.String(),
40 })
41 }
42 return errors.NewApplyConflict(causes, getConflictMessage(conflicts))
43 }
44
45 func getConflictMessage(conflicts merge.Conflicts) string {
46 if len(conflicts) == 1 {
47 return fmt.Sprintf("Apply failed with 1 conflict: conflict with %v: %v", printManager(conflicts[0].Manager), conflicts[0].Path)
48 }
49
50 m := map[string][]fieldpath.Path{}
51 for _, conflict := range conflicts {
52 m[conflict.Manager] = append(m[conflict.Manager], conflict.Path)
53 }
54
55 uniqueManagers := []string{}
56 for manager := range m {
57 uniqueManagers = append(uniqueManagers, manager)
58 }
59
60
61 sort.Strings(uniqueManagers)
62
63 messages := []string{}
64 for _, manager := range uniqueManagers {
65 messages = append(messages, fmt.Sprintf("conflicts with %v:", printManager(manager)))
66 for _, path := range m[manager] {
67 messages = append(messages, fmt.Sprintf("- %v", path))
68 }
69 }
70 return fmt.Sprintf("Apply failed with %d conflicts: %s", len(conflicts), strings.Join(messages, "\n"))
71 }
72
73 func printManager(manager string) string {
74 encodedManager := &metav1.ManagedFieldsEntry{}
75 if err := json.Unmarshal([]byte(manager), encodedManager); err != nil {
76 return fmt.Sprintf("%q", manager)
77 }
78 managerStr := fmt.Sprintf("%q", encodedManager.Manager)
79 if encodedManager.Subresource != "" {
80 managerStr = fmt.Sprintf("%s with subresource %q", managerStr, encodedManager.Subresource)
81 }
82 if encodedManager.Operation == metav1.ManagedFieldsOperationUpdate {
83 if encodedManager.Time == nil {
84 return fmt.Sprintf("%s using %v", managerStr, encodedManager.APIVersion)
85 }
86 return fmt.Sprintf("%s using %v at %v", managerStr, encodedManager.APIVersion, encodedManager.Time.UTC().Format(time.RFC3339))
87 }
88 return managerStr
89 }
90
View as plain text