/
mutator.go
125 lines (105 loc) · 3.61 KB
/
mutator.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
package node
import (
"encoding/json"
"fmt"
"sort"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/runtime"
admissionregv1 "k8s.io/api/admissionregistration/v1"
"github.com/longhorn/longhorn-manager/datastore"
"github.com/longhorn/longhorn-manager/webhook/admission"
"github.com/longhorn/longhorn-manager/webhook/common"
longhorn "github.com/longhorn/longhorn-manager/k8s/pkg/apis/longhorn/v1beta2"
werror "github.com/longhorn/longhorn-manager/webhook/error"
)
type nodeMutator struct {
admission.DefaultMutator
ds *datastore.DataStore
}
func NewMutator(ds *datastore.DataStore) admission.Mutator {
return &nodeMutator{ds: ds}
}
func (n *nodeMutator) Resource() admission.Resource {
return admission.Resource{
Name: "nodes",
Scope: admissionregv1.NamespacedScope,
APIGroup: longhorn.SchemeGroupVersion.Group,
APIVersion: longhorn.SchemeGroupVersion.Version,
ObjectType: &longhorn.Node{},
OperationTypes: []admissionregv1.OperationType{
admissionregv1.Create,
admissionregv1.Update,
},
}
}
func (n *nodeMutator) Create(request *admission.Request, newObj runtime.Object) (admission.PatchOps, error) {
return mutate(newObj)
}
func (n *nodeMutator) Update(request *admission.Request, oldObj runtime.Object, newObj runtime.Object) (admission.PatchOps, error) {
return mutate(newObj)
}
// mutate contains functionality shared by Create and Update.
func mutate(newObj runtime.Object) (admission.PatchOps, error) {
node, ok := newObj.(*longhorn.Node)
if !ok {
return nil, werror.NewInvalidError(fmt.Sprintf("%v is not a *longhorn.Node", newObj), "")
}
var patchOps admission.PatchOps
if node.Spec.Tags == nil {
patchOps = append(patchOps, `{"op": "replace", "path": "/spec/tags", "value": []}`)
} else {
if len(node.Spec.Tags) > 0 {
tags := deduplicateTags(node.Spec.Tags)
bytes, err := json.Marshal(tags)
if err != nil {
err = errors.Wrapf(err, "failed to get JSON encoding for node %v tags", node.Name)
return nil, werror.NewInvalidError(err.Error(), "")
}
patchOps = append(patchOps, fmt.Sprintf(`{"op": "replace", "path": "/spec/tags", "value": %s}`, string(bytes)))
}
}
if node.Spec.Disks == nil {
patchOps = append(patchOps, `{"op": "replace", "path": "/spec/disks", "value": {}}`)
} else {
for name, disk := range node.Spec.Disks {
if disk.Tags == nil {
patchOps = append(patchOps, fmt.Sprintf(`{"op": "replace", "path": "/spec/disks/%s/tags", "value": []}`, name))
} else {
if len(disk.Tags) > 0 {
tags := deduplicateTags(disk.Tags)
bytes, err := json.Marshal(tags)
if err != nil {
err = errors.Wrapf(err, "failed to get JSON encoding for node %v disk labels", node.Name)
return nil, werror.NewInvalidError(err.Error(), "")
}
patchOps = append(patchOps, fmt.Sprintf(`{"op": "replace", "path": "/spec/disks/%s/tags", "value": %s}`, name, string(bytes)))
}
}
if disk.Type == "" {
patchOps = append(patchOps, fmt.Sprintf(`{"op": "replace", "path": "/spec/disks/%s/diskType", "value": "filesystem"}`, name))
}
}
}
patchOp, err := common.GetLonghornFinalizerPatchOpIfNeeded(node)
if err != nil {
err := errors.Wrapf(err, "failed to get finalizer patch for node %v", node.Name)
return nil, werror.NewInvalidError(err.Error(), "")
}
if patchOp != "" {
patchOps = append(patchOps, patchOp)
}
return patchOps, nil
}
func deduplicateTags(inputTags []string) []string {
foundTags := make(map[string]struct{})
var tags []string
for _, tag := range inputTags {
if _, ok := foundTags[tag]; ok {
continue
}
foundTags[tag] = struct{}{}
tags = append(tags, tag)
}
sort.Strings(tags)
return tags
}