1 package manifest
2
3 import (
4 "bytes"
5 _ "embed"
6 "errors"
7 "io/fs"
8 "os"
9 "strings"
10 "testing"
11
12 "github.com/spf13/afero"
13 "github.com/stretchr/testify/assert"
14 "github.com/stretchr/testify/require"
15 corev1 "k8s.io/api/core/v1"
16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17 "k8s.io/apimachinery/pkg/runtime"
18 )
19
20 var (
21
22 podBytes []byte
23
24
25 updatedPodBytes []byte
26 )
27
28 var podObject = &corev1.Pod{
29 TypeMeta: metav1.TypeMeta{
30 APIVersion: "v1",
31 Kind: "Pod",
32 },
33 ObjectMeta: metav1.ObjectMeta{
34 Name: "test-pod",
35 },
36 Spec: corev1.PodSpec{
37 Containers: []corev1.Container{
38 {
39 Name: "test-container",
40 Image: "test-image:1.14.2",
41 Ports: []corev1.ContainerPort{
42 {
43 ContainerPort: 80,
44 },
45 },
46 },
47 },
48 },
49 }
50
51 func updatedPodObject() *corev1.Pod {
52 pod := podObject.DeepCopy()
53 pod.Name = "updated-test-pod"
54 return pod
55 }
56
57 var (
58 testPodReadpath = "/test-pod.yaml"
59 testPodWritepath = "/test-pod-write.yaml"
60 invalidPodFilepath = "/invalid.yaml"
61 )
62
63 func TestMain(m *testing.M) {
64 os.Exit(m.Run())
65 }
66
67 func TestRead(t *testing.T) {
68 memFs, err := createMemFs(afero.NewOsFs())
69 require.NoError(t, err)
70
71 testCases := map[string]struct {
72 manifest Manifest
73 syncMode bool
74 expectedBytes []byte
75 expectedMode fs.FileMode
76 expectError bool
77 }{
78 "WithModeSync_Success": {
79 manifest: New(memFs, testPodReadpath, &corev1.Pod{}, 0666),
80 syncMode: true,
81 expectedBytes: podBytes,
82 expectedMode: fs.FileMode(0660),
83 expectError: false,
84 },
85 "WithoutModeSync_Success": {
86 manifest: New(memFs, testPodReadpath, &corev1.Pod{}, 0666),
87 syncMode: false,
88 expectedBytes: podBytes,
89 expectedMode: fs.FileMode(0666),
90 expectError: false,
91 },
92 "FileNotFound_Failure": {
93 manifest: New(memFs, invalidPodFilepath, &corev1.Pod{}, 0666),
94 expectError: true,
95 },
96 }
97
98 for name, tc := range testCases {
99 t.Run(name, func(t *testing.T) {
100 err := tc.manifest.Read(tc.syncMode)
101 if tc.expectError {
102 require.Error(t, err)
103 return
104 }
105 require.NoError(t, err)
106
107 gotBytes, err := tc.manifest.Bytes()
108 require.NoError(t, err)
109
110 assert.Equal(t, tc.expectedBytes, gotBytes)
111 assert.Equal(t, tc.expectedMode, tc.manifest.mode)
112 })
113 }
114 }
115
116 func TestLoad(t *testing.T) {
117 testCases := map[string]struct {
118 manifest Manifest
119 inputBytes []byte
120 expectedObj *corev1.Pod
121 expectError bool
122 }{
123 "Success": {
124 manifest: New(nil, "", &corev1.Pod{}, 0),
125 inputBytes: podBytes,
126 expectedObj: podObject,
127 expectError: false,
128 },
129 "InvalidYAML_Failure": {
130 manifest: New(nil, "", &corev1.Pod{}, 0),
131 inputBytes: []byte("hello"),
132 expectError: true,
133 },
134 "NoManifests_Failure": {
135 manifest: New(nil, "", &corev1.Pod{}, 0),
136 inputBytes: []byte{},
137 expectError: true,
138 },
139 }
140
141 for name, tc := range testCases {
142 t.Run(name, func(t *testing.T) {
143 err := tc.manifest.Load(tc.inputBytes)
144 if tc.expectError {
145 require.Error(t, err)
146 return
147 }
148 require.NoError(t, err)
149
150 gotObj := tc.manifest.Content().(*corev1.Pod)
151 assert.Equal(t, tc.expectedObj, gotObj)
152 })
153 }
154 }
155
156 func TestWrite(t *testing.T) {
157 memFs, err := createMemFs(afero.NewOsFs())
158 require.NoError(t, err)
159
160 testCases := map[string]struct {
161 manifest Manifest
162 inputBytes []byte
163 expectedBytes []byte
164 expectedMode fs.FileMode
165 expectError bool
166 }{
167 "Success": {
168 manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0666),
169 inputBytes: podBytes,
170 expectedBytes: podBytes,
171 expectedMode: fs.FileMode(0666),
172 expectError: false,
173 },
174 "NoBaseObject_Failure": {
175 manifest: New(memFs, testPodWritepath, nil, 0666),
176 inputBytes: []byte{},
177 expectError: true,
178 },
179 }
180
181 for name, tc := range testCases {
182 t.Run(name, func(t *testing.T) {
183 if !bytes.Equal(tc.inputBytes, []byte{}) {
184 err := tc.manifest.Load(tc.inputBytes)
185 require.NoError(t, err)
186 }
187
188 err := tc.manifest.Write()
189 if tc.expectError {
190 require.Error(t, err)
191 return
192 }
193 require.NoError(t, err)
194
195 gotBytes, err := afero.ReadFile(memFs, testPodWritepath)
196 require.NoError(t, err)
197
198 assert.Equal(t, tc.expectedBytes, gotBytes)
199 assert.Equal(t, tc.expectedMode, tc.manifest.mode)
200
201 require.NoError(t, memFs.RemoveAll(testPodWritepath))
202 })
203 }
204 }
205
206 func TestWithCreate(t *testing.T) {
207 memFs, err := createMemFs(afero.NewOsFs())
208 require.NoError(t, err)
209
210 testCases := map[string]struct {
211 manifest Manifest
212 fn func(runtime.Object) error
213 expectedBytes []byte
214 expectedMode fs.FileMode
215 expectError bool
216 }{
217 "DefaultMode_Success": {
218 manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0),
219 fn: func(obj runtime.Object) error {
220 podObject.DeepCopyInto(obj.(*corev1.Pod))
221 return nil
222 },
223 expectedBytes: podBytes,
224 expectedMode: fs.FileMode(0600),
225 expectError: false,
226 },
227 "OverwriteMode_Success": {
228 manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0666),
229 fn: func(obj runtime.Object) error {
230 podObject.DeepCopyInto(obj.(*corev1.Pod))
231 return nil
232 },
233 expectedBytes: podBytes,
234 expectedMode: fs.FileMode(0666),
235 expectError: false,
236 },
237 "Failure": {
238 manifest: New(memFs, testPodWritepath, nil, 0666),
239 fn: func(_ runtime.Object) error {
240 return errors.New("force error")
241 },
242 expectError: true,
243 },
244 }
245
246 for name, tc := range testCases {
247 t.Run(name, func(t *testing.T) {
248 err := tc.manifest.WithCreate(tc.fn)
249 switch tc.expectError {
250 case true:
251 require.Error(t, err)
252 case false:
253 require.NoError(t, err)
254 }
255
256 gotBytes, err := afero.ReadFile(memFs, testPodWritepath)
257 switch tc.expectError {
258 case true:
259 require.Error(t, err)
260 return
261 case false:
262 require.NoError(t, err)
263 }
264
265 assert.Equal(t, tc.expectedBytes, gotBytes)
266 assert.Equal(t, tc.expectedMode, tc.manifest.mode)
267
268 require.NoError(t, memFs.RemoveAll(testPodWritepath))
269 })
270 }
271 }
272
273 func TestWithUpdate(t *testing.T) {
274 memFs, err := createMemFs(afero.NewOsFs())
275 require.NoError(t, err)
276
277 testCases := map[string]struct {
278 manifest Manifest
279 fn func(runtime.Object) error
280 expectedBytes []byte
281 expectedMode fs.FileMode
282 expectError bool
283 }{
284 "PersistMode_Success": {
285 manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0),
286 fn: func(obj runtime.Object) error {
287 updatedPodObject().DeepCopyInto(obj.(*corev1.Pod))
288 return nil
289 },
290 expectedBytes: updatedPodBytes,
291 expectedMode: fs.FileMode(0660),
292 expectError: false,
293 },
294 "OverwriteMode_Success": {
295 manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0666),
296 fn: func(obj runtime.Object) error {
297 updatedPodObject().DeepCopyInto(obj.(*corev1.Pod))
298 return nil
299 },
300 expectedBytes: updatedPodBytes,
301 expectedMode: fs.FileMode(0666),
302 expectError: false,
303 },
304 "Failure": {
305 manifest: New(memFs, testPodWritepath, &corev1.Pod{}, 0666),
306 fn: func(_ runtime.Object) error {
307 return errors.New("force error")
308 },
309 expectedBytes: podBytes,
310 expectError: true,
311 },
312 }
313
314 for name, tc := range testCases {
315 t.Run(name, func(t *testing.T) {
316 require.NoError(t, afero.WriteFile(memFs, testPodWritepath, podBytes, 0660))
317
318 err := tc.manifest.WithUpdate(tc.fn)
319 switch tc.expectError {
320 case true:
321 require.Error(t, err)
322 case false:
323 require.NoError(t, err)
324 }
325
326 gotBytes, err := afero.ReadFile(memFs, testPodWritepath)
327 switch tc.expectError {
328 case true:
329 assert.Equal(t, tc.expectedBytes, gotBytes)
330 return
331 case false:
332 require.NoError(t, err)
333 }
334
335 assert.Equal(t, tc.expectedBytes, gotBytes)
336 assert.Equal(t, tc.expectedMode, tc.manifest.mode)
337
338 require.NoError(t, memFs.RemoveAll(testPodWritepath))
339 })
340 }
341 }
342
343 func createMemFs(osFs afero.Fs) (afero.Fs, error) {
344 memFs := afero.NewMemMapFs()
345 return memFs, afero.Walk(osFs, "testdata", func(path string, info fs.FileInfo, err error) error {
346 if strings.Contains(path, "/embed/") {
347 return nil
348 }
349 if err != nil {
350 return err
351 }
352 fsPath := strings.ReplaceAll(path, "testdata", "")
353 if info.IsDir() {
354 return memFs.MkdirAll(fsPath, info.Mode())
355 }
356 content, err := afero.ReadFile(osFs, path)
357 if err != nil {
358 return err
359 }
360 return afero.WriteFile(memFs, fsPath, content, 0660)
361 })
362 }
363
View as plain text