master
go 727 lines 20.4 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package match
4
5 import (
6 "strings"
7 "testing"
8
9 "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/vsphere/resources"
10
11 "github.com/stretchr/testify/assert"
12 )
13
14 func TestHostIncludes_Parse(t *testing.T) {
15 tests := map[string]struct {
16 valid bool
17 cases map[string]struct {
18 host *resources.Host
19 want bool
20 }
21 }{
22 "": {valid: false},
23 "*/C1/H1": {valid: false},
24 "/": {
25 valid: true,
26 cases: map[string]struct {
27 host *resources.Host
28 want bool
29 }{
30 "does not match": {host: testHost("DC1", "Cluster1", "Host1"), want: false},
31 },
32 },
33 "/*": {
34 valid: true,
35 cases: map[string]struct {
36 host *resources.Host
37 want bool
38 }{
39 "matches": {host: testHost("DC1", "Cluster1", "Host1"), want: true},
40 },
41 },
42 "/!*": {
43 valid: true,
44 cases: map[string]struct {
45 host *resources.Host
46 want bool
47 }{
48 "does not match": {host: testHost("DC1", "Cluster1", "Host1"), want: false},
49 },
50 },
51 "/!*/": {
52 valid: true,
53 cases: map[string]struct {
54 host *resources.Host
55 want bool
56 }{
57 "does not match": {host: testHost("DC1", "Cluster1", "Host1"), want: false},
58 },
59 },
60 "/!*/ ": {
61 valid: true,
62 cases: map[string]struct {
63 host *resources.Host
64 want bool
65 }{
66 "does not match": {host: testHost("DC1", "Cluster1", "Host1"), want: false},
67 },
68 },
69 "/DC1* DC2* !*/Cluster*": {
70 valid: true,
71 cases: map[string]struct {
72 host *resources.Host
73 want bool
74 }{
75 "matches first datacenter": {host: testHost("DC1A", "Cluster1", "Host1"), want: true},
76 "matches second datacenter": {host: testHost("DC2A", "Cluster1", "Host1"), want: true},
77 "rejects other datacenter": {host: testHost("DC3A", "Cluster1", "Host1"), want: false},
78 "rejects different cluster": {host: testHost("DC1A", "Other", "Host1"), want: false},
79 "matches any host below path": {host: testHost("DC1A", "Cluster1", "Other"), want: true},
80 },
81 },
82 "/DC1*/Cluster*": {
83 valid: true,
84 cases: map[string]struct {
85 host *resources.Host
86 want bool
87 }{
88 "matches datacenter and cluster": {host: testHost("DC1A", "Cluster1", "Host1"), want: true},
89 "rejects different datacenter": {host: testHost("DC2A", "Cluster1", "Host1"), want: false},
90 "rejects different cluster": {host: testHost("DC1A", "Other", "Host1"), want: false},
91 "matches any host below two levels": {host: testHost("DC1A", "Cluster1", "Other"), want: true},
92 },
93 },
94 "/*/*/HOST1*": {
95 valid: true,
96 cases: map[string]struct {
97 host *resources.Host
98 want bool
99 }{
100 "matches host": {host: testHost("DC1", "Cluster1", "HOST10"), want: true},
101 "rejects different host": {host: testHost("DC1", "Cluster1", "OTHER"), want: false},
102 },
103 },
104 "/*/*/HOST1*/*/*": {
105 valid: true,
106 cases: map[string]struct {
107 host *resources.Host
108 want bool
109 }{
110 "ignores extra segments": {host: testHost("DC1", "Cluster1", "HOST10"), want: true},
111 },
112 },
113 "[/DC1*,/DC2*]": {
114 valid: true,
115 cases: map[string]struct {
116 host *resources.Host
117 want bool
118 }{
119 "matches first include": {host: testHost("DC1A", "Cluster1", "Host1"), want: true},
120 "matches second include": {host: testHost("DC2A", "Cluster1", "Host1"), want: true},
121 "rejects other": {host: testHost("DC3A", "Cluster1", "Host1"), want: false},
122 },
123 },
124 "[/DC1*,/DC2*,/DC3*/Cluster1*/H*]": {
125 valid: true,
126 cases: map[string]struct {
127 host *resources.Host
128 want bool
129 }{
130 "matches first include": {host: testHost("DC1A", "Other", "Other"), want: true},
131 "matches second include": {host: testHost("DC2A", "Other", "Other"), want: true},
132 "matches third include": {host: testHost("DC3A", "Cluster10", "Host1"), want: true},
133 "rejects third bad host": {host: testHost("DC3A", "Cluster10", "Other"), want: false},
134 "rejects third bad cluster": {host: testHost("DC3A", "Other", "Host1"), want: false},
135 },
136 },
137 }
138
139 for name, test := range tests {
140 t.Run(name, func(t *testing.T) {
141 includes := prepareIncludes(name)
142 m, err := HostIncludes(includes).Parse()
143
144 if !test.valid {
145 assert.Error(t, err)
146 } else {
147 assert.NoError(t, err)
148 for caseName, tc := range test.cases {
149 t.Run(caseName, func(t *testing.T) {
150 assert.Equal(t, tc.want, m.Match(tc.host))
151 })
152 }
153 }
154 })
155 }
156 }
157
158 func TestVMIncludes_Parse(t *testing.T) {
159 tests := map[string]struct {
160 valid bool
161 cases map[string]struct {
162 vm *resources.VM
163 want bool
164 }
165 }{
166 "": {valid: false},
167 "*/C1/H1/V1": {valid: false},
168 "/*": {
169 valid: true,
170 cases: map[string]struct {
171 vm *resources.VM
172 want bool
173 }{
174 "matches": {vm: testVM("DC1", "Cluster1", "Host1", "VM1"), want: true},
175 },
176 },
177 "/!*": {
178 valid: true,
179 cases: map[string]struct {
180 vm *resources.VM
181 want bool
182 }{
183 "does not match": {vm: testVM("DC1", "Cluster1", "Host1", "VM1"), want: false},
184 },
185 },
186 "/!*/": {
187 valid: true,
188 cases: map[string]struct {
189 vm *resources.VM
190 want bool
191 }{
192 "does not match": {vm: testVM("DC1", "Cluster1", "Host1", "VM1"), want: false},
193 },
194 },
195 "/!*/ ": {
196 valid: true,
197 cases: map[string]struct {
198 vm *resources.VM
199 want bool
200 }{
201 "does not match": {vm: testVM("DC1", "Cluster1", "Host1", "VM1"), want: false},
202 },
203 },
204 "/DC1* DC2* !*/Cluster*": {
205 valid: true,
206 cases: map[string]struct {
207 vm *resources.VM
208 want bool
209 }{
210 "matches first datacenter": {vm: testVM("DC1A", "Cluster1", "Host1", "VM1"), want: true},
211 "matches second datacenter": {vm: testVM("DC2A", "Cluster1", "Host1", "VM1"), want: true},
212 "rejects other datacenter": {vm: testVM("DC3A", "Cluster1", "Host1", "VM1"), want: false},
213 "rejects different cluster": {vm: testVM("DC1A", "Other", "Host1", "VM1"), want: false},
214 "matches any VM below path": {vm: testVM("DC1A", "Cluster1", "Other", "Other"), want: true},
215 },
216 },
217 "/DC1*/Cluster*": {
218 valid: true,
219 cases: map[string]struct {
220 vm *resources.VM
221 want bool
222 }{
223 "matches datacenter and cluster": {vm: testVM("DC1A", "Cluster1", "Host1", "VM1"), want: true},
224 "rejects different datacenter": {vm: testVM("DC2A", "Cluster1", "Host1", "VM1"), want: false},
225 "rejects different cluster": {vm: testVM("DC1A", "Other", "Host1", "VM1"), want: false},
226 "matches any VM below two levels": {vm: testVM("DC1A", "Cluster1", "Other", "Other"), want: true},
227 },
228 },
229 "/*/*/HOST1": {
230 valid: true,
231 cases: map[string]struct {
232 vm *resources.VM
233 want bool
234 }{
235 "matches host": {vm: testVM("DC1", "Cluster1", "HOST1", "VM1"), want: true},
236 "rejects different host": {vm: testVM("DC1", "Cluster1", "HOST2", "VM1"), want: false},
237 "matches any VM on host": {vm: testVM("DC1", "Cluster1", "HOST1", "Other"), want: true},
238 },
239 },
240 "/*/*/HOST1*/*/*": {
241 valid: true,
242 cases: map[string]struct {
243 vm *resources.VM
244 want bool
245 }{
246 "matches host and wildcard VM": {vm: testVM("DC1", "Cluster1", "HOST10", "VM1"), want: true},
247 "rejects different host": {vm: testVM("DC1", "Cluster1", "Other", "VM1"), want: false},
248 },
249 },
250 "[/DC1*,/DC2*]": {
251 valid: true,
252 cases: map[string]struct {
253 vm *resources.VM
254 want bool
255 }{
256 "matches first include": {vm: testVM("DC1A", "Cluster1", "Host1", "VM1"), want: true},
257 "matches second include": {vm: testVM("DC2A", "Cluster1", "Host1", "VM1"), want: true},
258 "rejects other": {vm: testVM("DC3A", "Cluster1", "Host1", "VM1"), want: false},
259 },
260 },
261 "[/DC1*,/DC2*,/DC3*/Cluster1*/H*/VM*]": {
262 valid: true,
263 cases: map[string]struct {
264 vm *resources.VM
265 want bool
266 }{
267 "matches first include": {vm: testVM("DC1A", "Other", "Other", "Other"), want: true},
268 "matches second include": {vm: testVM("DC2A", "Other", "Other", "Other"), want: true},
269 "matches third include": {vm: testVM("DC3A", "Cluster10", "Host1", "VM1"), want: true},
270 "rejects third bad VM": {vm: testVM("DC3A", "Cluster10", "Host1", "Other"), want: false},
271 "rejects third bad host": {vm: testVM("DC3A", "Cluster10", "Other", "VM1"), want: false},
272 },
273 },
274 }
275
276 for name, test := range tests {
277 t.Run(name, func(t *testing.T) {
278 includes := prepareIncludes(name)
279 m, err := VMIncludes(includes).Parse()
280
281 if !test.valid {
282 assert.Error(t, err)
283 } else {
284 assert.NoError(t, err)
285 for caseName, tc := range test.cases {
286 t.Run(caseName, func(t *testing.T) {
287 assert.Equal(t, tc.want, m.Match(tc.vm))
288 })
289 }
290 }
291 })
292 }
293 }
294
295 func TestDatastoreIncludes_Parse(t *testing.T) {
296 tests := map[string]struct {
297 valid bool
298 cases map[string]struct {
299 ds *resources.Datastore
300 want bool
301 }
302 }{
303 "": {valid: false},
304 "*/DS1": {valid: false},
305 "/": {
306 valid: true,
307 cases: map[string]struct {
308 ds *resources.Datastore
309 want bool
310 }{
311 "does not match": {ds: testDatastore("DC1", "DS1"), want: false},
312 },
313 },
314 "/*": {
315 valid: true,
316 cases: map[string]struct {
317 ds *resources.Datastore
318 want bool
319 }{
320 "matches": {ds: testDatastore("DC1", "DS1"), want: true},
321 },
322 },
323 "/!*": {
324 valid: true,
325 cases: map[string]struct {
326 ds *resources.Datastore
327 want bool
328 }{
329 "does not match": {ds: testDatastore("DC1", "DS1"), want: false},
330 },
331 },
332 "/!*/": {
333 valid: true,
334 cases: map[string]struct {
335 ds *resources.Datastore
336 want bool
337 }{
338 "does not match": {ds: testDatastore("DC1", "DS1"), want: false},
339 },
340 },
341 "/!*/ ": {
342 valid: true,
343 cases: map[string]struct {
344 ds *resources.Datastore
345 want bool
346 }{
347 "does not match": {ds: testDatastore("DC1", "DS1"), want: false},
348 },
349 },
350 "/DC1*/DS*": {
351 valid: true,
352 cases: map[string]struct {
353 ds *resources.Datastore
354 want bool
355 }{
356 "matches datacenter and datastore": {ds: testDatastore("DC1A", "DS1"), want: true},
357 "rejects different datacenter": {ds: testDatastore("DC2A", "DS1"), want: false},
358 "rejects different datastore": {ds: testDatastore("DC1A", "Other"), want: false},
359 },
360 },
361 "/*/*/extra": {
362 valid: true,
363 cases: map[string]struct {
364 ds *resources.Datastore
365 want bool
366 }{
367 "ignores extra segments": {ds: testDatastore("DC1", "DS1"), want: true},
368 },
369 },
370 "[/DC1*,/DC2*]": {
371 valid: true,
372 cases: map[string]struct {
373 ds *resources.Datastore
374 want bool
375 }{
376 "matches first include": {ds: testDatastore("DC1A", "DS1"), want: true},
377 "matches second include": {ds: testDatastore("DC2A", "DS1"), want: true},
378 "rejects other": {ds: testDatastore("DC3A", "DS1"), want: false},
379 },
380 },
381 "[/DC1*,/DC2*,/DC3*/DS*]": {
382 valid: true,
383 cases: map[string]struct {
384 ds *resources.Datastore
385 want bool
386 }{
387 "matches first include": {ds: testDatastore("DC1A", "Other"), want: true},
388 "matches second include": {ds: testDatastore("DC2A", "Other"), want: true},
389 "matches third include": {ds: testDatastore("DC3A", "DS1"), want: true},
390 "rejects third bad datastore": {ds: testDatastore("DC3A", "Other"), want: false},
391 "rejects third bad datacenter": {ds: testDatastore("Other", "DS1"), want: false},
392 },
393 },
394 }
395
396 for name, test := range tests {
397 t.Run(name, func(t *testing.T) {
398 includes := prepareIncludes(name)
399 m, err := DatastoreIncludes(includes).Parse()
400
401 if !test.valid {
402 assert.Error(t, err)
403 } else {
404 assert.NoError(t, err)
405 for caseName, tc := range test.cases {
406 t.Run(caseName, func(t *testing.T) {
407 assert.Equal(t, tc.want, m.Match(tc.ds))
408 })
409 }
410 }
411 })
412 }
413 }
414
415 func TestClusterIncludes_Parse(t *testing.T) {
416 tests := map[string]struct {
417 valid bool
418 cases map[string]struct {
419 cluster *resources.Cluster
420 want bool
421 }
422 }{
423 "": {valid: false},
424 "*/C1": {valid: false},
425 "/": {
426 valid: true,
427 cases: map[string]struct {
428 cluster *resources.Cluster
429 want bool
430 }{
431 "does not match": {cluster: testCluster("DC1", "Cluster1"), want: false},
432 },
433 },
434 "/*": {
435 valid: true,
436 cases: map[string]struct {
437 cluster *resources.Cluster
438 want bool
439 }{
440 "matches": {cluster: testCluster("DC1", "Cluster1"), want: true},
441 },
442 },
443 "/!*": {
444 valid: true,
445 cases: map[string]struct {
446 cluster *resources.Cluster
447 want bool
448 }{
449 "does not match": {cluster: testCluster("DC1", "Cluster1"), want: false},
450 },
451 },
452 "/!*/": {
453 valid: true,
454 cases: map[string]struct {
455 cluster *resources.Cluster
456 want bool
457 }{
458 "does not match": {cluster: testCluster("DC1", "Cluster1"), want: false},
459 },
460 },
461 "/!*/ ": {
462 valid: true,
463 cases: map[string]struct {
464 cluster *resources.Cluster
465 want bool
466 }{
467 "does not match": {cluster: testCluster("DC1", "Cluster1"), want: false},
468 },
469 },
470 "/DC1*/Cluster*": {
471 valid: true,
472 cases: map[string]struct {
473 cluster *resources.Cluster
474 want bool
475 }{
476 "matches datacenter and cluster": {cluster: testCluster("DC1A", "Cluster1"), want: true},
477 "rejects different datacenter": {cluster: testCluster("DC2A", "Cluster1"), want: false},
478 "rejects different cluster": {cluster: testCluster("DC1A", "Other"), want: false},
479 },
480 },
481 "/*/*/extra": {
482 valid: true,
483 cases: map[string]struct {
484 cluster *resources.Cluster
485 want bool
486 }{
487 "ignores extra segments": {cluster: testCluster("DC1", "Cluster1"), want: true},
488 },
489 },
490 "[/DC1*,/DC2*]": {
491 valid: true,
492 cases: map[string]struct {
493 cluster *resources.Cluster
494 want bool
495 }{
496 "matches first include": {cluster: testCluster("DC1A", "Cluster1"), want: true},
497 "matches second include": {cluster: testCluster("DC2A", "Cluster1"), want: true},
498 "rejects other": {cluster: testCluster("DC3A", "Cluster1"), want: false},
499 },
500 },
501 "[/DC1*,/DC2*,/DC3*/Cluster*]": {
502 valid: true,
503 cases: map[string]struct {
504 cluster *resources.Cluster
505 want bool
506 }{
507 "matches first include": {cluster: testCluster("DC1A", "Other"), want: true},
508 "matches second include": {cluster: testCluster("DC2A", "Other"), want: true},
509 "matches third include": {cluster: testCluster("DC3A", "Cluster1"), want: true},
510 "rejects third bad cluster": {cluster: testCluster("DC3A", "Other"), want: false},
511 "rejects third bad datacenter": {cluster: testCluster("Other", "Cluster1"), want: false},
512 },
513 },
514 }
515
516 for name, test := range tests {
517 t.Run(name, func(t *testing.T) {
518 includes := prepareIncludes(name)
519 m, err := ClusterIncludes(includes).Parse()
520
521 if !test.valid {
522 assert.Error(t, err)
523 } else {
524 assert.NoError(t, err)
525 for caseName, tc := range test.cases {
526 t.Run(caseName, func(t *testing.T) {
527 assert.Equal(t, tc.want, m.Match(tc.cluster))
528 })
529 }
530 }
531 })
532 }
533 }
534
535 func TestInventoryIncludes_ParseEmptyReturnsNil(t *testing.T) {
536 tests := map[string]struct {
537 parse func() (any, error)
538 }{
539 "host": {parse: func() (any, error) { return HostIncludes{}.Parse() }},
540 "VM": {parse: func() (any, error) { return VMIncludes{}.Parse() }},
541 "datastore": {parse: func() (any, error) { return DatastoreIncludes{}.Parse() }},
542 "cluster": {parse: func() (any, error) { return ClusterIncludes{}.Parse() }},
543 }
544
545 for name, tc := range tests {
546 t.Run(name, func(t *testing.T) {
547 m, err := tc.parse()
548
549 assert.NoError(t, err)
550 assert.Nil(t, m)
551 })
552 }
553 }
554
555 func TestDatastoreClusterIncludes_Parse(t *testing.T) {
556 pod := &resources.StoragePod{
557 ID: "group-p1",
558 Name: "Pod1",
559 Hier: resources.StoragePodHierarchy{DC: resources.HierarchyValue{Name: "DC1"}},
560 }
561 tests := map[string]struct {
562 includes DatastoreClusterIncludes
563 want bool
564 wantErr bool
565 }{
566 "invalid pattern": {includes: DatastoreClusterIncludes{"["}, wantErr: true},
567 "path match": {includes: DatastoreClusterIncludes{"/DC1/Pod1"}, want: true},
568 "name match": {includes: DatastoreClusterIncludes{"Pod1"}, want: true},
569 "id match": {includes: DatastoreClusterIncludes{"group-p1"}, want: true},
570 "no match": {includes: DatastoreClusterIncludes{"Pod2"}, want: false},
571 }
572
573 for name, tc := range tests {
574 t.Run(name, func(t *testing.T) {
575 m, err := tc.includes.Parse()
576
577 if tc.wantErr {
578 assert.Error(t, err)
579 return
580 }
581 assert.NoError(t, err)
582 assert.Equal(t, tc.want, m.Match(pod))
583 })
584 }
585 }
586
587 func TestVSANClusterIncludes_Parse(t *testing.T) {
588 cluster := &resources.Cluster{
589 ID: "domain-c1",
590 Name: "Cluster1",
591 Hier: resources.ClusterHierarchy{DC: resources.HierarchyValue{Name: "DC1"}},
592 VSANUUID: "cluster-uuid",
593 }
594 tests := map[string]struct {
595 includes VSANClusterIncludes
596 want bool
597 wantErr bool
598 }{
599 "invalid pattern": {includes: VSANClusterIncludes{"["}, wantErr: true},
600 "path match": {includes: VSANClusterIncludes{"/DC1/Cluster1"}, want: true},
601 "name match": {includes: VSANClusterIncludes{"Cluster1"}, want: true},
602 "id match": {includes: VSANClusterIncludes{"domain-c1"}, want: true},
603 "uuid match": {includes: VSANClusterIncludes{"vsan_uuid:cluster-uuid"}, want: true},
604 "no match": {includes: VSANClusterIncludes{"Cluster2"}, want: false},
605 }
606
607 for name, tc := range tests {
608 t.Run(name, func(t *testing.T) {
609 m, err := tc.includes.Parse()
610
611 if tc.wantErr {
612 assert.Error(t, err)
613 return
614 }
615 assert.NoError(t, err)
616 assert.Equal(t, tc.want, m.Match(cluster))
617 })
618 }
619 }
620
621 func TestVSANHostIncludes_Parse(t *testing.T) {
622 host := &resources.Host{
623 ID: "host-1",
624 Name: "Host1",
625 Hier: resources.HostHierarchy{DC: resources.HierarchyValue{Name: "DC1"}, Cluster: resources.HierarchyValue{Name: "Cluster1"}},
626 VSANNodeUUID: "host-uuid",
627 }
628 tests := map[string]struct {
629 includes VSANHostIncludes
630 want bool
631 wantErr bool
632 }{
633 "invalid pattern": {includes: VSANHostIncludes{"["}, wantErr: true},
634 "path match": {includes: VSANHostIncludes{"/DC1/Cluster1/Host1"}, want: true},
635 "name match": {includes: VSANHostIncludes{"Host1"}, want: true},
636 "id match": {includes: VSANHostIncludes{"host-1"}, want: true},
637 "uuid match": {includes: VSANHostIncludes{"vsan_node_uuid:host-uuid"}, want: true},
638 "no match": {includes: VSANHostIncludes{"Host2"}, want: false},
639 }
640
641 for name, tc := range tests {
642 t.Run(name, func(t *testing.T) {
643 m, err := tc.includes.Parse()
644
645 if tc.wantErr {
646 assert.Error(t, err)
647 return
648 }
649 assert.NoError(t, err)
650 assert.Equal(t, tc.want, m.Match(host))
651 })
652 }
653 }
654
655 func TestVSANVMIncludes_Parse(t *testing.T) {
656 vm := &resources.VM{
657 ID: "vm-1",
658 Name: "VM1",
659 Hier: resources.VMHierarchy{DC: resources.HierarchyValue{Name: "DC1"}, Cluster: resources.HierarchyValue{Name: "Cluster1"}, Host: resources.HierarchyValue{Name: "Host1"}},
660 InstanceUUID: "vm-uuid",
661 }
662 tests := map[string]struct {
663 includes VSANVMIncludes
664 want bool
665 wantErr bool
666 }{
667 "invalid pattern": {includes: VSANVMIncludes{"["}, wantErr: true},
668 "path match": {includes: VSANVMIncludes{"/DC1/Cluster1/Host1/VM1"}, want: true},
669 "name match": {includes: VSANVMIncludes{"VM1"}, want: true},
670 "id match": {includes: VSANVMIncludes{"vm-1"}, want: true},
671 "uuid match": {includes: VSANVMIncludes{"instance_uuid:vm-uuid"}, want: true},
672 "no match": {includes: VSANVMIncludes{"VM2"}, want: false},
673 }
674
675 for name, tc := range tests {
676 t.Run(name, func(t *testing.T) {
677 m, err := tc.includes.Parse()
678
679 if tc.wantErr {
680 assert.Error(t, err)
681 return
682 }
683 assert.NoError(t, err)
684 assert.Equal(t, tc.want, m.Match(vm))
685 })
686 }
687 }
688
689 func testHost(dc, cluster, name string) *resources.Host {
690 return &resources.Host{
691 Name: name,
692 Hier: resources.HostHierarchy{
693 DC: resources.HierarchyValue{Name: dc},
694 Cluster: resources.HierarchyValue{Name: cluster},
695 },
696 }
697 }
698
699 func testVM(dc, cluster, host, name string) *resources.VM {
700 return &resources.VM{
701 Name: name,
702 Hier: resources.VMHierarchy{
703 DC: resources.HierarchyValue{Name: dc},
704 Cluster: resources.HierarchyValue{Name: cluster},
705 Host: resources.HierarchyValue{Name: host},
706 },
707 }
708 }
709
710 func testDatastore(dc, name string) *resources.Datastore {
711 return &resources.Datastore{
712 Name: name,
713 Hier: resources.DatastoreHierarchy{DC: resources.HierarchyValue{Name: dc}},
714 }
715 }
716
717 func testCluster(dc, name string) *resources.Cluster {
718 return &resources.Cluster{
719 Name: name,
720 Hier: resources.ClusterHierarchy{DC: resources.HierarchyValue{Name: dc}},
721 }
722 }
723
724 func prepareIncludes(include string) []string {
725 trimmed := strings.Trim(include, "[]")
726 return strings.Split(trimmed, ",")
727 }