crossplane

Форк
0
/
build_test.go 
363 строки · 9.4 Кб
1
/*
2
Copyright 2023 The Crossplane Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package xpkg
18

19
import (
20
	"archive/tar"
21
	"context"
22
	"io"
23
	"os"
24
	"sort"
25
	"testing"
26

27
	"github.com/google/go-cmp/cmp"
28
	"github.com/google/go-cmp/cmp/cmpopts"
29
	v1 "github.com/google/go-containerregistry/pkg/v1"
30
	"github.com/google/go-containerregistry/pkg/v1/mutate"
31
	"github.com/google/go-containerregistry/pkg/v1/partial"
32
	"github.com/spf13/afero"
33
	"github.com/spf13/afero/tarfs"
34

35
	"github.com/crossplane/crossplane-runtime/pkg/errors"
36
	"github.com/crossplane/crossplane-runtime/pkg/parser"
37
	"github.com/crossplane/crossplane-runtime/pkg/test"
38

39
	"github.com/crossplane/crossplane/internal/xpkg/parser/examples"
40
)
41

42
var (
43
	testCRD  []byte
44
	testMeta []byte
45
	testEx1  []byte
46
	testEx2  []byte
47
	testEx3  []byte
48
	testEx4  []byte
49

50
	_ parser.Backend = &MockBackend{}
51
)
52

53
func init() {
54
	testCRD, _ = afero.ReadFile(afero.NewOsFs(), "testdata/providerconfigs.helm.crossplane.io.yaml")
55
	testMeta, _ = afero.ReadFile(afero.NewOsFs(), "testdata/provider_meta.yaml")
56
	testEx1, _ = afero.ReadFile(afero.NewOsFs(), "testdata/examples/ec2/instance.yaml")
57
	testEx2, _ = afero.ReadFile(afero.NewOsFs(), "testdata/examples/ec2/internetgateway.yaml")
58
	testEx3, _ = afero.ReadFile(afero.NewOsFs(), "testdata/examples/ecr/repository.yaml")
59
	testEx4, _ = afero.ReadFile(afero.NewOsFs(), "testdata/examples/provider.yaml")
60
}
61

62
type MockBackend struct {
63
	MockInit func() (io.ReadCloser, error)
64
}
65

66
func NewMockInitFn(r io.ReadCloser, err error) func() (io.ReadCloser, error) {
67
	return func() (io.ReadCloser, error) { return r, err }
68
}
69

70
func (m *MockBackend) Init(_ context.Context, _ ...parser.BackendOption) (io.ReadCloser, error) {
71
	return m.MockInit()
72
}
73

74
var _ parser.Parser = &MockParser{}
75

76
type MockParser struct {
77
	MockParse func() (*parser.Package, error)
78
}
79

80
func NewMockParseFn(pkg *parser.Package, err error) func() (*parser.Package, error) {
81
	return func() (*parser.Package, error) { return pkg, err }
82
}
83

84
func (m *MockParser) Parse(context.Context, io.ReadCloser) (*parser.Package, error) {
85
	return m.MockParse()
86
}
87

88
func TestBuild(t *testing.T) {
89
	errBoom := errors.New("boom")
90

91
	type args struct {
92
		be parser.Backend
93
		ex parser.Backend
94
		p  parser.Parser
95
		e  *examples.Parser
96
	}
97
	cases := map[string]struct {
98
		reason string
99
		args   args
100
		want   error
101
	}{
102
		"ErrInitBackend": {
103
			reason: "Should return an error if we fail to initialize backend.",
104
			args: args{
105
				be: &MockBackend{
106
					MockInit: NewMockInitFn(nil, errBoom),
107
				},
108
			},
109
			want: errors.Wrap(errBoom, errInitBackend),
110
		},
111
		"ErrParse": {
112
			reason: "Should return an error if we fail to parse package.",
113
			args: args{
114
				be: parser.NewEchoBackend(""),
115
				ex: parser.NewEchoBackend(""),
116
				p: &MockParser{
117
					MockParse: NewMockParseFn(nil, errBoom),
118
				},
119
			},
120
			want: errors.Wrap(errBoom, errParserPackage),
121
		},
122
	}
123

124
	for name, tc := range cases {
125
		t.Run(name, func(t *testing.T) {
126
			builder := New(tc.args.be, tc.args.ex, tc.args.p, tc.args.e)
127

128
			_, _, err := builder.Build(context.TODO())
129

130
			if diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != "" {
131
				t.Errorf("\n%s\nBuild(...): -want err, +got err:\n%s", tc.reason, diff)
132
			}
133
		})
134
	}
135
}
136

137
func TestBuildExamples(t *testing.T) {
138
	pkgp, _ := yamlParser()
139

140
	defaultFilters := []parser.FilterFn{
141
		parser.SkipDirs(),
142
		parser.SkipNotYAML(),
143
		parser.SkipEmpty(),
144
	}
145

146
	type withFsFn func() afero.Fs
147

148
	type args struct {
149
		rootDir     string
150
		examplesDir string
151
		fs          withFsFn
152
	}
153
	type want struct {
154
		pkgExists bool
155
		exExists  bool
156
		labels    []string
157
		err       error
158
	}
159

160
	cases := map[string]struct {
161
		reason string
162
		args   args
163
		want   want
164
	}{
165
		"SuccessNoExamples": {
166
			args: args{
167
				rootDir:     "/ws",
168
				examplesDir: "/ws/examples",
169
				fs: func() afero.Fs {
170
					fs := afero.NewMemMapFs()
171
					_ = fs.Mkdir("/ws", os.ModePerm)
172
					_ = fs.Mkdir("/ws/crds", os.ModePerm)
173
					_ = afero.WriteFile(fs, "/ws/crossplane.yaml", testMeta, os.ModePerm)
174
					_ = afero.WriteFile(fs, "/ws/crds/crd.yaml", testCRD, os.ModePerm)
175
					return fs
176
				},
177
			},
178
			want: want{
179
				pkgExists: true,
180
				labels: []string{
181
					PackageAnnotation,
182
				},
183
			},
184
		},
185
		"SuccessExamplesAtRoot": {
186
			args: args{
187
				rootDir:     "/ws",
188
				examplesDir: "/ws/examples",
189
				fs: func() afero.Fs {
190
					fs := afero.NewMemMapFs()
191
					_ = fs.Mkdir("/ws", os.ModePerm)
192
					_ = afero.WriteFile(fs, "/ws/crossplane.yaml", testMeta, os.ModePerm)
193
					_ = afero.WriteFile(fs, "/ws/crds/crd.yaml", testCRD, os.ModePerm)
194
					_ = afero.WriteFile(fs, "/ws/examples/ec2/instance.yaml", testEx1, os.ModePerm)
195
					_ = afero.WriteFile(fs, "/ws/examples/ec2/internetgateway.yaml", testEx2, os.ModePerm)
196
					_ = afero.WriteFile(fs, "/ws/examples/ecr/repository.yaml", testEx3, os.ModePerm)
197
					_ = afero.WriteFile(fs, "/ws/examples/provider.yaml", testEx4, os.ModePerm)
198
					return fs
199
				},
200
			},
201
			want: want{
202
				pkgExists: true,
203
				exExists:  true,
204
				labels: []string{
205
					PackageAnnotation,
206
					ExamplesAnnotation,
207
				},
208
			},
209
		},
210
		"SuccessExamplesAtCustomDir": {
211
			args: args{
212
				rootDir:     "/ws",
213
				examplesDir: "/other_directory/examples",
214
				fs: func() afero.Fs {
215
					fs := afero.NewMemMapFs()
216
					_ = fs.Mkdir("/ws", os.ModePerm)
217
					_ = fs.Mkdir("/other_directory", os.ModePerm)
218
					_ = afero.WriteFile(fs, "/ws/crossplane.yaml", testMeta, os.ModePerm)
219
					_ = afero.WriteFile(fs, "/ws/crds/crd.yaml", testCRD, os.ModePerm)
220
					_ = afero.WriteFile(fs, "/other_directory/examples/ec2/instance.yaml", testEx1, os.ModePerm)
221
					_ = afero.WriteFile(fs, "/other_directory/examples/ec2/internetgateway.yaml", testEx2, os.ModePerm)
222
					_ = afero.WriteFile(fs, "/other_directory/examples/ecr/repository.yaml", testEx3, os.ModePerm)
223
					_ = afero.WriteFile(fs, "/other_directory/examples/provider.yaml", testEx4, os.ModePerm)
224
					return fs
225
				},
226
			},
227
			want: want{
228
				pkgExists: true,
229
				exExists:  true,
230
				labels: []string{
231
					PackageAnnotation,
232
					ExamplesAnnotation,
233
				},
234
			},
235
		},
236
	}
237

238
	for name, tc := range cases {
239
		t.Run(name, func(t *testing.T) {
240
			pkgBe := parser.NewFsBackend(
241
				tc.args.fs(),
242
				parser.FsDir(tc.args.rootDir),
243
				parser.FsFilters([]parser.FilterFn{
244
					parser.SkipDirs(),
245
					parser.SkipNotYAML(),
246
					parser.SkipEmpty(),
247
					SkipContains("examples/"), // don't try to parse the examples in the package
248
				}...),
249
			)
250
			pkgEx := parser.NewFsBackend(
251
				tc.args.fs(),
252
				parser.FsDir(tc.args.examplesDir),
253
				parser.FsFilters(defaultFilters...),
254
			)
255

256
			builder := New(pkgBe, pkgEx, pkgp, examples.New())
257

258
			img, _, err := builder.Build(context.TODO())
259

260
			if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
261
				t.Errorf("\n%s\nBuildExamples(...): -want err, +got err:\n%s", tc.reason, diff)
262
			}
263

264
			// validate the xpkg img has the correct annotations, etc
265
			contents, err := readImg(img)
266
			// sort the contents slice for test comparison
267
			sort.Strings(contents.labels)
268

269
			if diff := cmp.Diff(tc.want.pkgExists, len(contents.pkgBytes) != 0); diff != "" {
270
				t.Errorf("\n%s\nBuildExamples(...): -want err, +got err:\n%s", tc.reason, diff)
271
			}
272
			if diff := cmp.Diff(tc.want.exExists, len(contents.exBytes) != 0); diff != "" {
273
				t.Errorf("\n%s\nBuildExamples(...): -want err, +got err:\n%s", tc.reason, diff)
274
			}
275
			if diff := cmp.Diff(tc.want.labels, contents.labels, cmpopts.SortSlices(func(i, j int) bool {
276
				return contents.labels[i] < contents.labels[j]
277
			})); diff != "" {
278
				t.Errorf("\n%s\nBuildExamples(...): -want err, +got err:\n%s", tc.reason, diff)
279
			}
280
			if diff := cmp.Diff(nil, err, test.EquateErrors()); diff != "" {
281
				t.Errorf("\n%s\nBuildExamples(...): -want err, +got err:\n%s", tc.reason, diff)
282
			}
283
		})
284
	}
285
}
286

287
type xpkgContents struct {
288
	labels   []string
289
	pkgBytes []byte
290
	exBytes  []byte
291
}
292

293
func readImg(i v1.Image) (xpkgContents, error) {
294
	contents := xpkgContents{
295
		labels: make([]string, 0),
296
	}
297

298
	reader := mutate.Extract(i)
299
	fs := tarfs.New(tar.NewReader(reader))
300
	pkgYaml, err := fs.Open(StreamFile)
301
	if err != nil {
302
		return contents, err
303
	}
304

305
	pkgBytes, err := io.ReadAll(pkgYaml)
306
	if err != nil {
307
		return contents, err
308
	}
309
	contents.pkgBytes = pkgBytes
310

311
	exYaml, err := fs.Open(XpkgExamplesFile)
312
	if err != nil && !os.IsNotExist(err) {
313
		return contents, err
314
	}
315

316
	if exYaml != nil {
317
		exBytes, err := io.ReadAll(exYaml)
318
		if err != nil {
319
			return contents, err
320
		}
321
		contents.exBytes = exBytes
322
	}
323

324
	labels, err := allLabels(i)
325
	if err != nil {
326
		return contents, err
327
	}
328

329
	contents.labels = labels
330

331
	return contents, nil
332
}
333

334
func allLabels(i partial.WithConfigFile) ([]string, error) {
335
	labels := []string{}
336

337
	cfgFile, err := i.ConfigFile()
338
	if err != nil {
339
		return labels, err
340
	}
341

342
	cfg := cfgFile.Config
343

344
	for _, label := range cfg.Labels {
345
		labels = append(labels, label)
346
	}
347

348
	return labels, nil
349
}
350

351
// This is equivalent to yaml.New. Duplicated here to avoid an import cycle.
352
func yamlParser() (*parser.PackageParser, error) {
353
	metaScheme, err := BuildMetaScheme()
354
	if err != nil {
355
		panic(err)
356
	}
357
	objScheme, err := BuildObjectScheme()
358
	if err != nil {
359
		panic(err)
360
	}
361

362
	return parser.New(metaScheme, objScheme), nil
363
}
364

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.