Skip to content

Commit 26ad5a7

Browse files
authored
Merge pull request #716 from knabben/file-crd-install
✨ CRD file path on CRDInstallOptions.Paths
2 parents 201a662 + 08de469 commit 26ad5a7

File tree

3 files changed

+139
-23
lines changed

3 files changed

+139
-23
lines changed

pkg/envtest/crd.go

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import (
3939

4040
// CRDInstallOptions are the options for installing CRDs
4141
type CRDInstallOptions struct {
42-
// Paths is a list of paths to the directories containing CRDs
42+
// Paths is a list of paths to the directories or files containing CRDs
4343
Paths []string
4444

4545
// CRDs is a list of CRDs to install
@@ -88,16 +88,12 @@ func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]*apiextensio
8888
// readCRDFiles reads the directories of CRDs in options.Paths and adds the CRD structs to options.CRDs
8989
func readCRDFiles(options *CRDInstallOptions) error {
9090
if len(options.Paths) > 0 {
91-
for _, path := range options.Paths {
92-
if _, err := os.Stat(path); !options.ErrorIfPathMissing && os.IsNotExist(err) {
93-
continue
94-
}
95-
new, err := readCRDs(path)
96-
if err != nil {
97-
return err
98-
}
99-
options.CRDs = append(options.CRDs, new...)
91+
crdList, err := renderCRDs(options)
92+
if err != nil {
93+
return err
10094
}
95+
96+
options.CRDs = append(options.CRDs, crdList...)
10197
}
10298
return nil
10399
}
@@ -232,28 +228,65 @@ func CreateCRDs(config *rest.Config, crds []*apiextensionsv1beta1.CustomResource
232228
return nil
233229
}
234230

235-
// readCRDs reads the CRDs from files and Unmarshals them into structs
236-
func readCRDs(path string) ([]*apiextensionsv1beta1.CustomResourceDefinition, error) {
237-
// Get the CRD files
238-
var files []os.FileInfo
239-
var err error
240-
log.V(1).Info("reading CRDs from path", "path", path)
241-
if files, err = ioutil.ReadDir(path); err != nil {
242-
return nil, err
231+
// renderCRDs iterate through options.Paths and extract all CRD files.
232+
func renderCRDs(options *CRDInstallOptions) ([]*apiextensionsv1beta1.CustomResourceDefinition, error) {
233+
var (
234+
err error
235+
info os.FileInfo
236+
crds []*apiextensionsv1beta1.CustomResourceDefinition
237+
files []os.FileInfo
238+
)
239+
240+
for _, path := range options.Paths {
241+
var filePath = path
242+
243+
// Return the error if ErrorIfPathMissing exists
244+
if info, err = os.Stat(path); os.IsNotExist(err) {
245+
if options.ErrorIfPathMissing {
246+
return nil, err
247+
}
248+
continue
249+
}
250+
251+
if !info.IsDir() {
252+
filePath, files = filepath.Dir(path), append(files, info)
253+
} else {
254+
if files, err = ioutil.ReadDir(path); err != nil {
255+
return nil, err
256+
}
257+
}
258+
259+
log.V(1).Info("reading CRDs from path", "path", path)
260+
crdList, err := readCRDs(filePath, files)
261+
if err != nil {
262+
return nil, err
263+
}
264+
265+
// If CRD already in the list, skip it.
266+
if existsCRDs(crds, crdList) {
267+
continue
268+
}
269+
crds = append(crds, crdList...)
243270
}
244271

272+
return crds, nil
273+
}
274+
275+
// readCRDs reads the CRDs from files and Unmarshals them into structs
276+
func readCRDs(basePath string, files []os.FileInfo) ([]*apiextensionsv1beta1.CustomResourceDefinition, error) {
277+
var crds []*apiextensionsv1beta1.CustomResourceDefinition
278+
245279
// White list the file extensions that may contain CRDs
246280
crdExts := sets.NewString(".json", ".yaml", ".yml")
247281

248-
var crds []*apiextensionsv1beta1.CustomResourceDefinition
249282
for _, file := range files {
250283
// Only parse whitelisted file types
251284
if !crdExts.Has(filepath.Ext(file.Name())) {
252285
continue
253286
}
254287

255288
// Unmarshal CRDs from file into structs
256-
docs, err := readDocuments(filepath.Join(path, file.Name()))
289+
docs, err := readDocuments(filepath.Join(basePath, file.Name()))
257290
if err != nil {
258291
return nil, err
259292
}

pkg/envtest/envtest_test.go

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ var _ = Describe("Test", func() {
6969
})
7070

7171
Describe("InstallCRDs", func() {
72-
It("should install the CRDs into the cluster", func(done Done) {
73-
72+
It("should install the CRDs into the cluster using directory", func(done Done) {
7473
crds, err = InstallCRDs(env.Config, CRDInstallOptions{
7574
Paths: []string{filepath.Join(".", "testdata")},
7675
})
@@ -163,6 +162,65 @@ var _ = Describe("Test", func() {
163162
close(done)
164163
}, 5)
165164

165+
It("should install the CRDs into the cluster using file", func(done Done) {
166+
crds, err = InstallCRDs(env.Config, CRDInstallOptions{
167+
Paths: []string{filepath.Join(".", "testdata", "examplecrd2.yaml")},
168+
})
169+
Expect(err).NotTo(HaveOccurred())
170+
171+
crd := &v1beta1.CustomResourceDefinition{}
172+
err = c.Get(context.TODO(), types.NamespacedName{Name: "bazs.qux.example.com"}, crd)
173+
Expect(err).NotTo(HaveOccurred())
174+
Expect(crd.Spec.Names.Kind).To(Equal("Baz"))
175+
176+
err = WaitForCRDs(env.Config, []*v1beta1.CustomResourceDefinition{
177+
{
178+
Spec: v1beta1.CustomResourceDefinitionSpec{
179+
Group: "qux.example.com",
180+
Version: "v1beta1",
181+
Names: v1beta1.CustomResourceDefinitionNames{
182+
Plural: "bazs",
183+
}},
184+
},
185+
},
186+
CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
187+
)
188+
Expect(err).NotTo(HaveOccurred())
189+
190+
close(done)
191+
}, 10)
192+
193+
It("should filter out already existent CRD", func(done Done) {
194+
crds, err = InstallCRDs(env.Config, CRDInstallOptions{
195+
Paths: []string{
196+
filepath.Join(".", "testdata"),
197+
filepath.Join(".", "testdata", "examplecrd1.yaml"),
198+
},
199+
})
200+
Expect(err).NotTo(HaveOccurred())
201+
202+
crd := &v1beta1.CustomResourceDefinition{}
203+
err = c.Get(context.TODO(), types.NamespacedName{Name: "foos.bar.example.com"}, crd)
204+
Expect(err).NotTo(HaveOccurred())
205+
Expect(crd.Spec.Names.Kind).To(Equal("Foo"))
206+
207+
err = WaitForCRDs(env.Config, []*v1beta1.CustomResourceDefinition{
208+
{
209+
Spec: v1beta1.CustomResourceDefinitionSpec{
210+
Group: "bar.example.com",
211+
Version: "v1beta1",
212+
Names: v1beta1.CustomResourceDefinitionNames{
213+
Plural: "foos",
214+
}},
215+
},
216+
},
217+
CRDInstallOptions{MaxTime: 50 * time.Millisecond, PollInterval: 15 * time.Millisecond},
218+
)
219+
Expect(err).NotTo(HaveOccurred())
220+
221+
close(done)
222+
}, 10)
223+
166224
It("should not return an not error if the directory doesn't exist", func(done Done) {
167225
crds, err = InstallCRDs(env.Config, CRDInstallOptions{Paths: []string{"fake"}})
168226
Expect(err).NotTo(HaveOccurred())
@@ -177,6 +235,15 @@ var _ = Describe("Test", func() {
177235
close(done)
178236
}, 5)
179237

238+
It("should return an error if the file doesn't exist", func(done Done) {
239+
crds, err = InstallCRDs(env.Config, CRDInstallOptions{Paths: []string{
240+
filepath.Join(".", "testdata", "fake.yaml")}, ErrorIfPathMissing: true,
241+
})
242+
Expect(err).To(HaveOccurred())
243+
244+
close(done)
245+
}, 5)
246+
180247
It("should return an error if the resource group version isn't found", func(done Done) {
181248
// Wait for a CRD where the Group and Version don't exist
182249
err := WaitForCRDs(env.Config,

pkg/envtest/helper.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package envtest
22

3-
import apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
3+
import (
4+
"reflect"
5+
6+
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
7+
)
48

59
// mergePaths merges two string slices containing paths.
610
// This function makes no guarantees about order of the merged slice.
@@ -39,3 +43,15 @@ func mergeCRDs(s1, s2 []*apiextensionsv1beta1.CustomResourceDefinition) []*apiex
3943
}
4044
return merged
4145
}
46+
47+
// existsCRDs verify if a any CRD is common between two lists.
48+
func existsCRDs(s1, s2 []*apiextensionsv1beta1.CustomResourceDefinition) bool {
49+
for _, s1crd := range s1 {
50+
for _, s2crd := range s2 {
51+
if reflect.DeepEqual(s1crd, s2crd) {
52+
return true
53+
}
54+
}
55+
}
56+
return false
57+
}

0 commit comments

Comments
 (0)