-
Notifications
You must be signed in to change notification settings - Fork 88
/
openshift.go
138 lines (116 loc) · 4.62 KB
/
openshift.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package snapshot
import (
"context"
"strings"
"github.com/pkg/errors"
"github.com/replicatedhq/kots/pkg/k8sutil"
"github.com/replicatedhq/kots/pkg/kotsadmsnapshot/types"
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
rbacv1 "k8s.io/api/rbac/v1"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)
type k8sGetter interface {
GetClientSet() (kubernetes.Interface, error)
IsOpenShift(clientset kubernetes.Interface) bool
GetRole(namespace, roleName string, clientset kubernetes.Interface) (*rbacv1.Role, error)
GetRoleBinding(namespace, roleBindingName string, clientset kubernetes.Interface) (*rbacv1.RoleBinding, error)
}
type filterGetter struct {
}
func (g *filterGetter) GetClientSet() (kubernetes.Interface, error) {
cfg, err := config.GetConfig()
if err != nil {
return nil, errors.Wrap(err, "failed to get config")
}
clientset, err := kubernetes.NewForConfig(cfg)
if err != nil {
return nil, errors.Wrap(err, "failed to create clientset")
}
return clientset, nil
}
func (g *filterGetter) IsOpenShift(clientset kubernetes.Interface) bool {
return k8sutil.IsOpenShift(clientset)
}
func (g *filterGetter) GetRole(namespace, roleName string, clientset kubernetes.Interface) (*rbacv1.Role, error) {
role, err := clientset.RbacV1().Roles(namespace).Get(context.TODO(), roleName, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to get role")
}
return role, nil
}
func (g *filterGetter) GetRoleBinding(namespace, roleBindingName string, clientset kubernetes.Interface) (*rbacv1.RoleBinding, error) {
binding, err := clientset.RbacV1().RoleBindings(namespace).Get(context.TODO(), roleBindingName, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to get rolebinding")
}
return binding, nil
}
// Some warnings in OpenShift clusters are harmless and should not be shown to the user
func filterWarnings(restore *velerov1.Restore, warnings []types.SnapshotError, k8sGetter k8sGetter) ([]types.SnapshotError, error) {
clientset, err := k8sGetter.GetClientSet()
if err != nil {
return nil, errors.Wrap(err, "failed to get clientset")
}
if !k8sGetter.IsOpenShift(clientset) {
return warnings, nil
}
// OpenShift maintains a second copy of all Roles and RoleBindings under a different schema prefix
// They are a mirror copy of the roles and bindings that applications create.
// When one is deleted, the other one is deleted automatically.
//
// Velero will discover these roles and binding and include them in the snapshot.
// During the restore, they are the first one that get created and that causes the second copy to be created immediately.
// Later in the restore process, Velero will fail to restore the duplicates.
filtered := make([]types.SnapshotError, 0)
for _, warning := range warnings {
kind, name := parseOpenShiftWarning(warning)
switch kind {
case "role", "roles":
role, err := k8sGetter.GetRole(warning.Namespace, name, clientset)
if err != nil {
if kuberneteserrors.IsNotFound(errors.Cause(err)) {
continue
}
return nil, errors.Wrap(err, "failed to get role")
}
if role.CreationTimestamp.After(restore.CreationTimestamp.Time) {
continue
}
case "rolebinding", "rolebindings":
roleBinding, err := k8sGetter.GetRoleBinding(warning.Namespace, name, clientset)
if err != nil {
if kuberneteserrors.IsNotFound(errors.Cause(err)) {
continue
}
return nil, errors.Wrap(err, "failed to get rolebinding")
}
if roleBinding.CreationTimestamp.After(restore.CreationTimestamp.Time) {
continue
}
}
filtered = append(filtered, warning)
}
return filtered, nil
}
func parseOpenShiftWarning(warning types.SnapshotError) (string, string) {
// Only two strings we need to parse here at this time, so it's super dumb code
// could not restore, rolebindings.rbac.authorization.k8s.io "swimlane-backup-binding" already exists. Warning: the in-cluster version is different than the backed-up version.
// could not restore, roles.rbac.authorization.k8s.io "swimlane-backup" already exists. Warning: the in-cluster version is different than the backed-up version.
if !strings.HasPrefix(warning.Message, "could not restore, ") {
return "", ""
}
subStr := strings.TrimPrefix(warning.Message, "could not restore, ")
parts := strings.Split(subStr, " ")
if len(parts) < 3 {
return "", ""
}
schemaStr := parts[0]
objectName := parts[1]
parts = strings.Split(schemaStr, ".")
objectKind := parts[0]
objectName = strings.Trim(objectName, "\"")
return objectKind, objectName
}