@@ -17,8 +17,17 @@ limitations under the License.
17
17
package controller
18
18
19
19
import (
20
+ "fmt"
21
+ "regexp"
22
+ "strings"
23
+
24
+ "k8s.io/apimachinery/pkg/util/validation"
25
+ "k8s.io/client-go/util/jsonpath"
26
+ "k8s.io/klog"
27
+
20
28
apis "github.com/openebs/node-disk-manager/api/v1alpha1"
21
29
bd "github.com/openebs/node-disk-manager/blockdevice"
30
+
22
31
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23
32
)
24
33
@@ -67,13 +76,116 @@ type FSInfo struct {
67
76
68
77
// ToDevice convert deviceInfo struct to api.BlockDevice
69
78
// type which will be pushed to etcd
70
- func (di * DeviceInfo ) ToDevice () apis.BlockDevice {
79
+ func (di * DeviceInfo ) ToDevice (controller * Controller ) ( apis.BlockDevice , error ) {
71
80
blockDevice := apis.BlockDevice {}
72
81
blockDevice .Spec = di .getDeviceSpec ()
73
82
blockDevice .ObjectMeta = di .getObjectMeta ()
74
83
blockDevice .TypeMeta = di .getTypeMeta ()
75
84
blockDevice .Status = di .getStatus ()
76
- return blockDevice
85
+ err := addBdLabels (& blockDevice , controller )
86
+ if err != nil {
87
+ return blockDevice , fmt .Errorf ("error in adding labels to the blockdevice: %v" , err )
88
+ }
89
+ return blockDevice , nil
90
+ }
91
+
92
+ // addBdLabels add labels to block device that may be helpful for filtering the block device
93
+ // based on some generic attributes like drive-type, model, vendor etc.
94
+ func addBdLabels (bd * apis.BlockDevice , ctrl * Controller ) error {
95
+ var JsonPathFields []string
96
+
97
+ // get the labels to be added from the configmap
98
+ if ctrl .NDMConfig != nil {
99
+ for _ , metaConfig := range ctrl .NDMConfig .MetaConfigs {
100
+ if metaConfig .Key == deviceLabelsKey {
101
+ JsonPathFields = strings .Split (metaConfig .Type , "," )
102
+ }
103
+ }
104
+ }
105
+
106
+ if len (JsonPathFields ) > 0 {
107
+ for _ , jsonPath := range JsonPathFields {
108
+ // Parse jsonpath
109
+ fields , err := RelaxedJSONPathExpression (strings .TrimSpace (jsonPath ))
110
+ if err != nil {
111
+ klog .Errorf ("Error converting into a valid jsonpath expression: %+v" , err )
112
+ return err
113
+ }
114
+
115
+ j := jsonpath .New (jsonPath )
116
+ if err := j .Parse (fields ); err != nil {
117
+ klog .Errorf ("Error parsing jsonpath: %s, error: %+v" , fields , err )
118
+ return err
119
+ }
120
+
121
+ values , err := j .FindResults (bd )
122
+ if err != nil {
123
+ klog .Errorf ("Error finding results for jsonpath: %s, error: %+v" , fields , err )
124
+ return err
125
+ }
126
+
127
+ valueStrings := []string {}
128
+ var jsonPathFieldValue string
129
+
130
+ if len (values ) > 0 || len (values [0 ]) > 0 {
131
+ for arrIx := range values {
132
+ for valIx := range values [arrIx ] {
133
+ valueStrings = append (valueStrings , fmt .Sprintf ("%v" , values [arrIx ][valIx ].Interface ()))
134
+ }
135
+ }
136
+
137
+ // convert the string array into a single string
138
+ jsonPathFieldValue = strings .Join (valueStrings , "," )
139
+ jsonPathFieldValue = strings .TrimSuffix (jsonPathFieldValue , "," )
140
+
141
+ // the above string formed should adhere to k8s label rules inorder for it to be
142
+ // used as a label value for blockdevice object.
143
+ // Check this link for more info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
144
+ errs := validation .IsValidLabelValue (jsonPathFieldValue )
145
+ if len (errs ) > 0 {
146
+ return fmt .Errorf ("invalid Label found. Error: {%s}" , strings .Join (errs , "," ))
147
+ }
148
+
149
+ jsonPathFields := strings .Split (jsonPath , "." )
150
+ if len (jsonPathFields ) > 0 && jsonPathFields [len (jsonPathFields )- 1 ] != "" && jsonPathFieldValue != "" {
151
+ bd .Labels [NDMLabelPrefix + jsonPathFields [len (jsonPathFields )- 1 ]] = jsonPathFieldValue
152
+ }
153
+ }
154
+ }
155
+ klog .V (4 ).Infof ("successfully added device labels" )
156
+ }
157
+ return nil
158
+ }
159
+
160
+ // RelaxedJSONPathExpression attempts to be flexible with JSONPath expressions, it accepts:
161
+ // * metadata.name (no leading '.' or curly braces '{...}'
162
+ // * {metadata.name} (no leading '.')
163
+ // * .metadata.name (no curly braces '{...}')
164
+ // * {.metadata.name} (complete expression)
165
+ // And transforms them all into a valid jsonpath expression:
166
+ // {.metadata.name}
167
+ // NOTE: This code has been referenced from kubernetes kubectl github repo.
168
+ // Ref: https://github.com/kubernetes/kubectl/blob/caeb9274868c57d8a320014290cc7e3d1bcb9e46/pkg/cmd/get/customcolumn.go#L47
169
+ func RelaxedJSONPathExpression (pathExpression string ) (string , error ) {
170
+ var jsonRegexp = regexp .MustCompile (`^\{\.?([^{}]+)\}$|^\.?([^{}]+)$` )
171
+
172
+ if len (pathExpression ) == 0 {
173
+ return pathExpression , nil
174
+ }
175
+ submatches := jsonRegexp .FindStringSubmatch (pathExpression )
176
+ if submatches == nil {
177
+ return "" , fmt .Errorf ("unexpected path string, expected a 'name1.name2' or '.name1.name2' or '{name1.name2}' or '{.name1.name2}'" )
178
+ }
179
+ if len (submatches ) != 3 {
180
+ return "" , fmt .Errorf ("unexpected submatch list: %v" , submatches )
181
+ }
182
+ var fieldSpec string
183
+ if len (submatches [1 ]) != 0 {
184
+ fieldSpec = submatches [1 ]
185
+ } else {
186
+ fieldSpec = submatches [2 ]
187
+ }
188
+ return fmt .Sprintf ("{.%s}" , fieldSpec ), nil
77
189
}
78
190
79
191
// getObjectMeta returns ObjectMeta struct which contains
@@ -85,7 +197,14 @@ func (di *DeviceInfo) getObjectMeta() metav1.ObjectMeta {
85
197
Annotations : make (map [string ]string ),
86
198
Name : di .UUID ,
87
199
}
88
- objectMeta .Labels [KubernetesHostNameLabel ] = di .NodeAttributes [HostNameKey ]
200
+ //objectMeta.Labels[KubernetesHostNameLabel] = di.NodeAttributes[HostNameKey]
201
+ for k , v := range di .NodeAttributes {
202
+ if k == HostNameKey {
203
+ objectMeta .Labels [KubernetesHostNameLabel ] = v
204
+ } else {
205
+ objectMeta .Labels [k ] = v
206
+ }
207
+ }
89
208
objectMeta .Labels [NDMDeviceTypeKey ] = NDMDefaultDeviceType
90
209
objectMeta .Labels [NDMManagedKey ] = TrueString
91
210
// adding custom labels
0 commit comments