1
2
3
4 package krusty_test
5
6 import (
7 "bytes"
8 "encoding/base64"
9 "fmt"
10 "io"
11 "os"
12 "os/exec"
13 "path/filepath"
14 "strings"
15 "testing"
16
17 "github.com/stretchr/testify/assert"
18 "github.com/stretchr/testify/require"
19 "sigs.k8s.io/kustomize/api/internal/loader"
20 "sigs.k8s.io/kustomize/api/krusty"
21 "sigs.k8s.io/kustomize/api/resmap"
22 kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
23 "sigs.k8s.io/kustomize/kyaml/yaml"
24 )
25
26 func TestRemoteLoad_LocalProtocol(t *testing.T) {
27 type testRepos struct {
28 root string
29 simple string
30 noSuffix string
31 hash string
32 multiBaseDev string
33 withSubmodule string
34 }
35
36
37
38
39
40
41
42
43
44 createGitRepos := func(t *testing.T) testRepos {
45 t.Helper()
46
47 bash := func(script string) {
48 cmd := exec.Command("sh", "-c", script)
49 o, err := cmd.CombinedOutput()
50 if err != nil {
51 t.Fatalf("error running %v\nerr: %v\n%s", script, err, string(o))
52 }
53 }
54 root := t.TempDir()
55
56 hashPath, err := os.MkdirTemp(root, "hash-")
57 require.NoError(t, err)
58 hashDir := filepath.Base(hashPath)
59
60 bash(fmt.Sprintf(`
61 set -eux
62
63 export ROOT="%s"
64 export HASH_DIR="%s"
65 export GIT_AUTHOR_EMAIL=nobody@kustomize.io
66 export GIT_AUTHOR_NAME=Nobody
67 export GIT_COMMITTER_EMAIL=nobody@kustomize.io
68 export GIT_COMMITTER_NAME=Nobody
69
70 cp -r testdata/remoteload/simple $ROOT/simple.git
71 (
72 cd $ROOT/simple.git
73 git config --global protocol.file.allow always
74 git init --initial-branch=main
75 git add .
76 git commit -m "import"
77 git checkout -b change-image
78 cat >>kustomization.yaml <<EOF
79
80 images:
81 - name: nginx
82 newName: nginx
83 newTag: "2"
84 EOF
85 git commit -am "image change"
86 git checkout main
87 )
88 cp -r $ROOT/simple.git $ROOT/nosuffix
89 cp -r testdata/remoteload/multibase $ROOT/multibase.git
90 (
91 cd $ROOT/multibase.git
92 git init --initial-branch=main
93 git add .
94 git commit -m "import"
95 )
96 cp -r testdata/remoteload/with-submodule $ROOT/with-submodule.git # see README
97 cp -r $ROOT/simple.git/. $ROOT/$HASH_DIR
98 (
99 cd $ROOT/with-submodule.git
100 git init --initial-branch=main
101 git add .
102 git commit -m "import"
103 git checkout -b relative-submodule
104 git submodule add ../$HASH_DIR submodule
105 git commit -m "relative submodule"
106 git checkout main
107 git submodule add $ROOT/simple.git submodule
108 git commit -m "submodule"
109 )
110 `, root, hashDir))
111 return testRepos{
112 root: root,
113
114 simple: "simple.git",
115 noSuffix: "nosuffix",
116 hash: hashDir,
117 multiBaseDev: "multibase.git",
118 withSubmodule: "with-submodule.git",
119 }
120 }
121
122 const simpleBuild = `apiVersion: v1
123 kind: Pod
124 metadata:
125 labels:
126 app: myapp
127 name: myapp-pod
128 spec:
129 containers:
130 - image: nginx:1.7.9
131 name: nginx
132 `
133 var simpleBuildWithNginx2 = strings.ReplaceAll(simpleBuild, "nginx:1.7.9", "nginx:2")
134 var multibaseDevExampleBuild = strings.ReplaceAll(simpleBuild, "myapp-pod", "dev-myapp-pod")
135
136 repos := createGitRepos(t)
137 tests := []struct {
138 name string
139 kustomization string
140 expected string
141 err string
142 skip bool
143 }{
144 {
145 name: "simple",
146 kustomization: `
147 resources:
148 - file://$ROOT/simple.git
149 `,
150 expected: simpleBuild,
151 },
152 {
153 name: "without git suffix",
154 kustomization: `
155 resources:
156 - file://$ROOT/nosuffix
157 `,
158 expected: simpleBuild,
159 },
160 {
161 name: "has path",
162 kustomization: `
163 resources:
164 - file://$ROOT/multibase.git/dev
165 `,
166 expected: multibaseDevExampleBuild,
167 },
168 {
169 name: "has path without git suffix",
170 kustomization: `
171 resources:
172 - file://$ROOT/multibase//dev
173 `,
174 expected: multibaseDevExampleBuild,
175 },
176 {
177 name: "has ref",
178 kustomization: `
179 resources:
180 - "file://$ROOT/simple.git?ref=change-image"
181 `,
182
183 expected: simpleBuildWithNginx2,
184 },
185 {
186
187 name: "has version",
188 kustomization: `
189 resources:
190 - file://$ROOT/simple.git?version=change-image
191 `,
192 expected: simpleBuildWithNginx2,
193 },
194 {
195 name: "has submodule",
196 kustomization: `
197 resources:
198 - file://$ROOT/with-submodule.git/submodule
199 `,
200 expected: simpleBuild,
201 },
202 {
203 name: "has relative submodule",
204 kustomization: `
205 resources:
206 - file://$ROOT/with-submodule.git/submodule?ref=relative-submodule
207 `,
208
209 expected: simpleBuild,
210 },
211 {
212 name: "has timeout",
213 kustomization: `
214 resources:
215 - file://$ROOT/simple.git?timeout=500
216 `,
217 expected: simpleBuild,
218 },
219 {
220 name: "has submodule but not initialized",
221 kustomization: `
222 resources:
223 - file://$ROOT/with-submodule.git/submodule?submodules=0
224 `,
225 err: "unable to find one of 'kustomization.yaml', 'kustomization.yml' or 'Kustomization' in directory",
226 },
227 {
228 name: "has origin annotation",
229 skip: true,
230 kustomization: `
231 resources:
232 - file://$ROOT/simple.git
233 buildMetadata: [originAnnotations]
234 `,
235 expected: `apiVersion: v1
236 kind: Pod
237 metadata:
238 annotations:
239 config.kubernetes.io/origin: |
240 path: pod.yaml
241 repo: file://$ROOT/simple.git
242 labels:
243 app: myapp
244 name: myapp-pod
245 spec:
246 containers:
247 - image: nginx:1.7.9
248 name: nginx
249 `,
250 },
251 {
252 name: "has ref path timeout and origin annotation",
253 kustomization: `
254 resources:
255 - file://$ROOT/multibase.git/dev?version=main&timeout=500
256 buildMetadata: [originAnnotations]
257 `,
258 expected: `apiVersion: v1
259 kind: Pod
260 metadata:
261 annotations:
262 config.kubernetes.io/origin: |
263 path: base/pod.yaml
264 repo: file://$ROOT/multibase.git
265 ref: main
266 labels:
267 app: myapp
268 name: dev-myapp-pod
269 spec:
270 containers:
271 - image: nginx:1.7.9
272 name: nginx
273 `,
274 },
275 {
276 name: "repo does not exist ",
277 kustomization: `
278 resources:
279 - file:///not/a/real/repo
280 `,
281 err: "fatal: '/not/a/real/repo' does not appear to be a git repository",
282 },
283 }
284
285 for _, test := range tests {
286 t.Run(test.name, func(t *testing.T) {
287 if test.skip {
288 t.SkipNow()
289 }
290
291 kust := strings.ReplaceAll(test.kustomization, "$ROOT", repos.root)
292 fSys, tmpDir := kusttest_test.CreateKustDir(t, kust)
293
294 b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
295 m, err := b.Run(
296 fSys,
297 tmpDir.String())
298
299 if test.err != "" {
300 require.Error(t, err)
301 require.Regexp(t, test.err, err.Error())
302 } else {
303 require.NoError(t, err)
304 checkYaml(t, m, strings.ReplaceAll(test.expected, "$ROOT", repos.root))
305 }
306 })
307 }
308 }
309
310 func TestRemoteLoad_RemoteProtocols(t *testing.T) {
311
312
313 tests := []struct {
314 name string
315 kustomization string
316 err string
317 errT error
318 beforeTest func(t *testing.T)
319 }{
320 {
321 name: "https",
322 kustomization: `
323 resources:
324 - https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?submodules=0&ref=kustomize%2Fv4.5.7&timeout=300
325 `,
326 },
327 {
328 name: "https with explicit .git suffix",
329 kustomization: `
330 resources:
331 - https://github.com/kubernetes-sigs/kustomize.git//examples/multibases/dev/?submodules=0&ref=kustomize%2Fv4.5.7&timeout=300
332 `,
333 },
334 {
335 name: "git double-colon https",
336 kustomization: `
337 resources:
338 - git::https://github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?submodules=0&ref=kustomize%2Fv4.5.7&timeout=300
339 `,
340 },
341 {
342 name: "https raw remote file",
343 kustomization: `
344 resources:
345 - https://raw.githubusercontent.com/kubernetes-sigs/kustomize/v3.1.0/examples/multibases/base/pod.yaml?timeout=300
346 namePrefix: dev-
347 `,
348 },
349 {
350 name: "https without scheme",
351 kustomization: `
352 resources:
353 - github.com/kubernetes-sigs/kustomize/examples/multibases/dev?submodules=0&ref=kustomize%2Fv4.5.7&timeout=300
354 `,
355 },
356 {
357 name: "ssh",
358 beforeTest: configureGitSSHCommand,
359 kustomization: `
360 resources:
361 - git@github.com/kubernetes-sigs/kustomize/examples/multibases/dev?submodules=0&ref=kustomize%2Fv4.5.7&timeout=300
362 `,
363 },
364 {
365 name: "ssh with colon",
366 beforeTest: configureGitSSHCommand,
367 kustomization: `
368 resources:
369 - git@github.com:kubernetes-sigs/kustomize/examples/multibases/dev?submodules=0&ref=kustomize%2Fv4.5.7&timeout=300
370 `,
371 },
372 {
373 name: "ssh scheme",
374 beforeTest: configureGitSSHCommand,
375 kustomization: `
376 resources:
377 - ssh://git@github.com/kubernetes-sigs/kustomize/examples/multibases/dev?submodules=0&ref=kustomize%2Fv4.5.7&timeout=300
378 `,
379 },
380 {
381 name: "http error",
382 kustomization: `
383 resources:
384 - https://github.com/thisisa404.yaml
385 `,
386 err: "accumulating resources: accumulating resources from 'https://github.com/thisisa404.yaml': HTTP Error: status code 404 (Not Found)",
387 errT: loader.ErrHTTP,
388 },
389 }
390
391 for _, test := range tests {
392 t.Run(test.name, func(t *testing.T) {
393 if test.beforeTest != nil {
394 test.beforeTest(t)
395 }
396 fSys, tmpDir := kusttest_test.CreateKustDir(t, test.kustomization)
397
398 b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
399 m, err := b.Run(
400 fSys,
401 tmpDir.String())
402
403 if test.err != "" {
404 require.Error(t, err)
405 assert.Contains(t, err.Error(), test.err)
406 if test.errT != nil {
407 require.ErrorIs(t, err, test.errT)
408 }
409 } else {
410 require.NoError(t, err)
411 const multibaseDevExampleBuild = `apiVersion: v1
412 kind: Pod
413 metadata:
414 labels:
415 app: myapp
416 name: dev-myapp-pod
417 spec:
418 containers:
419 - image: nginx:1.7.9
420 name: nginx
421 `
422 checkYaml(t, m, multibaseDevExampleBuild)
423 }
424 })
425 }
426 }
427
428 func configureGitSSHCommand(t *testing.T) {
429 t.Helper()
430
431
432 node, err := yaml.ReadFile("testdata/repo_read_only_ssh_key.yaml")
433 require.NoError(t, err)
434 keyB64, err := node.GetString("key")
435 require.NoError(t, err)
436 key, err := base64.StdEncoding.DecodeString(keyB64)
437 require.NoError(t, err)
438
439
440 f, err := os.CreateTemp("", "kustomize_ssh")
441 require.NoError(t, err)
442 _, err = io.Copy(f, bytes.NewReader(key))
443 require.NoError(t, err)
444 cmd := fmt.Sprintf("ssh -i %s -o IdentitiesOnly=yes", f.Name())
445 const SSHCommandKey = "GIT_SSH_COMMAND"
446 t.Setenv(SSHCommandKey, cmd)
447 t.Cleanup(func() {
448 _ = os.Remove(f.Name())
449 })
450 }
451
452 func checkYaml(t *testing.T, actual resmap.ResMap, expected string) {
453 t.Helper()
454
455 yml, err := actual.AsYaml()
456 require.NoError(t, err)
457 assert.Equal(t, expected, string(yml))
458 }
459
View as plain text