-
Notifications
You must be signed in to change notification settings - Fork 463
/
test_resources.go
129 lines (110 loc) · 3.16 KB
/
test_resources.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
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors
//
// SPDX-License-Identifier: Apache-2.0
package test
import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"os"
"path/filepath"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/sets"
utilyaml "k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// EnsureTestResources reads test resources from path, applies them using the given client and returns the created
// objects.
func EnsureTestResources(ctx context.Context, c client.Client, namespaceName, path string) ([]client.Object, error) {
objects, err := ReadTestResources(c.Scheme(), namespaceName, path)
if err != nil {
return nil, fmt.Errorf("error decoding resources: %w", err)
}
for _, obj := range objects {
current := obj.DeepCopyObject().(client.Object)
if err := c.Get(ctx, client.ObjectKeyFromObject(current), current); err != nil {
if !apierrors.IsNotFound(err) {
return nil, err
}
// object doesn't exists, create it
if err := c.Create(ctx, obj); err != nil {
return nil, err
}
} else {
// object already exists, update it
if err := c.Patch(ctx, obj, client.MergeFromWithOptions(current, client.MergeFromWithOptimisticLock{})); err != nil {
return nil, err
}
}
}
return objects, nil
}
// ReadTestResources reads test resources from path, decodes them using the given scheme and returns the parsed objects.
// Objects are values of the proper API types, if registered in the given scheme, and *unstructured.Unstructured
// otherwise.
func ReadTestResources(scheme *runtime.Scheme, namespaceName, path string) ([]client.Object, error) {
decoder := serializer.NewCodecFactory(scheme).UniversalDeserializer()
files, err := os.ReadDir(path)
if err != nil {
return nil, err
}
// file extensions that may contain Webhooks
resourceExtensions := sets.New(".json", ".yaml", ".yml")
var objects []client.Object
for _, file := range files {
if file.IsDir() {
continue
}
// Only parse allowlisted file types
if !resourceExtensions.Has(filepath.Ext(file.Name())) {
continue
}
// Unmarshal Webhooks from file into structs
docs, err := readDocuments(filepath.Join(path, file.Name()))
if err != nil {
return nil, err
}
for _, doc := range docs {
obj, err := runtime.Decode(decoder, doc)
if err != nil {
return nil, err
}
clientObj, ok := obj.(client.Object)
if !ok {
return nil, fmt.Errorf("%T does not implement client.Object", obj)
}
if namespaceName != "" {
clientObj.SetNamespace(namespaceName)
}
objects = append(objects, clientObj)
}
}
return objects, nil
}
// readDocuments reads documents from file
func readDocuments(fp string) ([][]byte, error) {
b, err := os.ReadFile(fp)
if err != nil {
return nil, err
}
var (
docs [][]byte
reader = utilyaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(b)))
)
for {
// Read document
doc, err := reader.Read()
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
docs = append(docs, doc)
}
return docs, nil
}