Skip to content

Commit 1721672

Browse files
committed
Use --crd-dir path to read and parse owned CRDs
1 parent 27a8499 commit 1721672

File tree

7 files changed

+169
-112
lines changed

7 files changed

+169
-112
lines changed

cmd/operator-sdk/generate/csv.go

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,18 @@ has already generated a CSV manifest you want to use as a base, supply its
5858
version to --from-version. Otherwise the SDK will scaffold a new CSV manifest.
5959
6060
CSV input flags:
61-
--deploy-dir: The CSV file contents will be generated from the operator manifests
62-
present in this directory.
63-
64-
--apis-dir: The CSV annotation comments will be parsed from the Go types under this path to
65-
fill out metadata for owned APIs in spec.customresourcedefinitions.owned.
66-
67-
--crd-dir: The CRD manifests are updated from this path to the CSV bundle directory.
68-
Note: The CSV generator only uses this to copy the CRD manifests.
69-
The CSV contents for spec.customresourcedefinitions.owned will still be generated
70-
from the CRD manifests in the deploy directory specified by --deploy-dir.
71-
If unset, it defaults to the same value as --deploy-dir.
72-
61+
--deploy-dir:
62+
The CSV's install strategy and permissions will be generated from the operator manifests
63+
(Deployment and Role/ClusterRole) present in this directory.
64+
65+
--apis-dir:
66+
The CSV annotation comments will be parsed from the Go types under this path to
67+
fill out metadata for owned APIs in spec.customresourcedefinitions.owned.
68+
69+
--crd-dir:
70+
The CSV's spec.customresourcedefinitions.owned field is generated from the CRD manifests
71+
in this path.These CRD manifests are also copied over to the bundle directory if --update-crds is set.
72+
Additionally the CR manifests will be used to populate the CSV example CRs.
7373
`,
7474
Example: `
7575
##### Generate CSV from default input paths #####
@@ -170,12 +170,16 @@ CSV input flags:
170170
cmd.Flags().StringVar(&c.fromVersion, "from-version", "",
171171
"Semantic version of an existing CSV to use as a base")
172172

173+
// TODO: Allow multiple paths
174+
// Deployment and RBAC manifests might be in different dirs e.g kubebuilder
173175
cmd.Flags().StringVar(&c.deployDir, "deploy-dir", "deploy",
174-
`Project relative path to root directory for operator manifests (Deployment, RBAC, CRDs)`)
176+
`Project relative path to root directory for operator manifests (Deployment and RBAC)`)
175177
cmd.Flags().StringVar(&c.apisDir, "apis-dir", filepath.Join("pkg", "apis"),
176178
`Project relative path to root directory for API type defintions`)
177-
cmd.Flags().StringVar(&c.crdDir, "crd-dir", "",
178-
`Project relative path to root directory for for CRD manifests`)
179+
// TODO: Allow multiple paths
180+
// CRD and CR manifests might be in different dirs e.g kubebuilder
181+
cmd.Flags().StringVar(&c.crdDir, "crd-dir", filepath.Join("deploy", "crds"),
182+
`Project relative path to root directory for CRD and CR manifests`)
179183

180184
cmd.Flags().StringVar(&c.outputDir, "output-dir", scaffold.DeployDir,
181185
"Base directory to output generated CSV. The resulting CSV bundle directory "+
@@ -206,6 +210,7 @@ func (c csvCmd) run() error {
206210
Inputs: map[string]string{
207211
gencatalog.DeployDirKey: c.deployDir,
208212
gencatalog.APIsDirKey: c.apisDir,
213+
gencatalog.CRDsDirKey: c.crdDir,
209214
},
210215
OutputDir: c.outputDir,
211216
}
@@ -221,11 +226,6 @@ func (c csvCmd) run() error {
221226

222227
// Write CRD's to the new or updated CSV package dir.
223228
if c.updateCRDs {
224-
// TODO(hasbro17): Reconsider the crd-dir flag since it only lets you control
225-
// where the CRD manifests are copied from but the CSV generator above
226-
// will uses the CRD manifests in deploy dir to build the CSV contents for
227-
// spec.customresourcedefinitions.owned
228-
// Need to reconcile this disparity.
229229
crdManifestSet, err := findCRDFileSet(c.crdDir)
230230
if err != nil {
231231
return fmt.Errorf("failed to update CRD's: %v", err)
@@ -277,28 +277,43 @@ func validateVersion(version string) error {
277277
return nil
278278
}
279279

280-
// findCRDFileSet searches all directories and files in path for CRD manifests,
280+
// findCRDFileSet searches files in the given directory path for CRD manifests,
281281
// returning a map of paths to file contents.
282-
func findCRDFileSet(path string) (map[string][]byte, error) {
282+
func findCRDFileSet(crdDir string) (map[string][]byte, error) {
283283
crdFileSet := map[string][]byte{}
284-
info, err := os.Stat(path)
284+
info, err := os.Stat(crdDir)
285285
if err != nil {
286286
return nil, err
287287
}
288288
if !info.IsDir() {
289-
return nil, fmt.Errorf("crd's must be read from a directory. %s is a file", path)
289+
return nil, fmt.Errorf("crd's must be read from a directory. %s is a file", crdDir)
290290
}
291-
292-
// Get CRD manifest paths from path recursively but ignore olm-catalog subdir
293-
// if it is present in the search directory
294-
crdPaths, err := k8sutil.GetCRDManifestPaths(path, gencatalog.OLMCatalogChildDir)
291+
files, err := ioutil.ReadDir(crdDir)
295292
if err != nil {
296293
return nil, err
297294
}
298-
for _, crdPath := range crdPaths {
295+
296+
wd := projutil.MustGetwd()
297+
for _, f := range files {
298+
if f.IsDir() {
299+
continue
300+
}
301+
302+
crdPath := filepath.Join(wd, crdDir, f.Name())
299303
b, err := ioutil.ReadFile(crdPath)
300304
if err != nil {
301-
return nil, err
305+
return nil, fmt.Errorf("error reading manifest %s: %v", crdPath, err)
306+
}
307+
// Skip files in crdsDir that aren't k8s manifests since we do not know
308+
// what other files are in crdsDir.
309+
typeMeta, err := k8sutil.GetTypeMetaFromBytes(b)
310+
if err != nil {
311+
log.Debugf("Skipping non-manifest file %s: %v", crdPath, err)
312+
continue
313+
}
314+
if typeMeta.Kind != "CustomResourceDefinition" {
315+
log.Debugf("Skipping non CRD manifest %s", crdPath)
316+
continue
302317
}
303318
crdFileSet[filepath.Base(crdPath)] = b
304319
}

doc/cli/operator-sdk_generate_csv.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ has already generated a CSV manifest you want to use as a base, supply its
1212
version to --from-version. Otherwise the SDK will scaffold a new CSV manifest.
1313

1414
CSV input flags:
15-
--deploy-dir: The CSV file contents will be generated from the operator manifests
16-
present in this directory.
15+
--deploy-dir:
16+
The CSV's install strategy and permissions will be generated from the operator manifests
17+
(Deployment and Role/ClusterRole) present in this directory.
1718

18-
--apis-dir: The CSV annotation comments will be parsed from the Go types under this path to
19-
fill out metadata for owned APIs in spec.customresourcedefinitions.owned.
20-
21-
--crd-dir: The CRD manifests are updated from this path to the CSV bundle directory.
22-
Note: The CSV generator only uses this to copy the CRD manifests.
23-
The CSV contents for spec.customresourcedefinitions.owned will still be generated
24-
from the CRD manifests in the deploy directory specified by --deploy-dir.
25-
If unset, it defaults to the same value as --deploy-dir.
19+
--apis-dir:
20+
The CSV annotation comments will be parsed from the Go types under this path to
21+
fill out metadata for owned APIs in spec.customresourcedefinitions.owned.
2622

23+
--crd-dir:
24+
The CSV's spec.customresourcedefinitions.owned field is generated from the CRD manifests
25+
in this path.These CRD manifests are also copied over to the bundle directory if --update-crds is set.
26+
Additionally the CR manifests will be used to populate the CSV example CRs.
2727

2828

2929
```
@@ -105,11 +105,11 @@ operator-sdk generate csv [flags]
105105

106106
```
107107
--apis-dir string Project relative path to root directory for API type defintions (default "pkg/apis")
108-
--crd-dir string Project relative path to root directory for for CRD manifests
108+
--crd-dir string Project relative path to root directory for CRD and CR manifests (default "deploy/crds")
109109
--csv-channel string Channel the CSV should be registered under in the package manifest
110110
--csv-version string Semantic version of the CSV
111111
--default-channel Use the channel passed to --csv-channel as the package manifests' default channel. Only valid when --csv-channel is set
112-
--deploy-dir string Project relative path to root directory for operator manifests (Deployment, RBAC, CRDs) (default "deploy")
112+
--deploy-dir string Project relative path to root directory for operator manifests (Deployment and RBAC) (default "deploy")
113113
--from-version string Semantic version of an existing CSV to use as a base
114114
-h, --help help for csv
115115
--operator-name string Operator name to use while generating CSV

internal/generate/olm-catalog/csv.go

Lines changed: 91 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/operator-framework/operator-sdk/internal/scaffold"
2929
"github.com/operator-framework/operator-sdk/internal/util/fileutil"
3030
"github.com/operator-framework/operator-sdk/internal/util/k8sutil"
31+
"github.com/operator-framework/operator-sdk/internal/util/projutil"
3132
"github.com/operator-framework/operator-sdk/internal/util/yamlutil"
3233

3334
"github.com/blang/semver"
@@ -51,9 +52,17 @@ const (
5152
// Input keys for CSV generator whose values are the filepaths for the respective input directories
5253

5354
// DeployDirKey is for the location of the operator manifests directory e.g "deploy/production"
55+
// The Deployment and RBAC manifests from this directory will be used to populate the CSV
56+
// install strategy: spec.install
5457
DeployDirKey = "deploy"
5558
// APIsDirKey is for the location of the API types directory e.g "pkg/apis"
59+
// The CSV annotation comments will be parsed from the types under this path.
5660
APIsDirKey = "apis"
61+
// CRDsDirKey is for the location of the CRD manifests directory e.g "deploy/crds"
62+
// Both the CRD and CR manifests from this path will be used to populate CSV fields
63+
// metadata.annotations.alm-examples for CR examples
64+
// and spec.customresourcedefinitions.owned for owned CRDs
65+
CRDsDirKey = "crds"
5766
)
5867

5968
type csvGenerator struct {
@@ -107,6 +116,10 @@ func NewCSV(cfg gen.Config, csvVersion, fromVersion string) gen.Generator {
107116
g.Inputs[APIsDirKey] = scaffold.ApisDir
108117
}
109118

119+
if crdsDir, ok := g.Inputs[CRDsDirKey]; !ok || crdsDir == "" {
120+
g.Inputs[CRDsDirKey] = filepath.Join(g.Inputs[DeployDirKey], "crds")
121+
}
122+
110123
return g
111124
}
112125

@@ -366,60 +379,24 @@ func (g csvGenerator) updateCSVVersions(csv *olmapiv1alpha1.ClusterServiceVersio
366379
// user-defined manifests and updates csv.
367380
func (g csvGenerator) updateCSVFromManifests(csv *olmapiv1alpha1.ClusterServiceVersion) (err error) {
368381
kindManifestMap := map[schema.GroupVersionKind][][]byte{}
369-
crGVKSet := map[schema.GroupVersionKind]struct{}{}
370-
err = filepath.Walk(g.Inputs[DeployDirKey], func(path string, info os.FileInfo, werr error) error {
371-
if werr != nil {
372-
log.Debugf("Failed to walk dir: %v", werr)
373-
return werr
374-
}
375-
// Only read manifest from files, not directories
376-
if info.IsDir() {
377-
// Skip walking olm-catalog dir if it's present in the deploy directory
378-
if info.Name() == OLMCatalogChildDir {
379-
return filepath.SkipDir
380-
}
381-
return nil
382-
}
383382

384-
b, err := ioutil.ReadFile(path)
385-
if err != nil {
386-
return err
387-
}
388-
scanner := yamlutil.NewYAMLScanner(b)
389-
for scanner.Scan() {
390-
manifest := scanner.Bytes()
391-
typeMeta, err := k8sutil.GetTypeMetaFromBytes(manifest)
392-
if err != nil {
393-
log.Infof("No TypeMeta in %s, skipping file", path)
394-
continue
395-
}
396-
gvk := typeMeta.GroupVersionKind()
397-
kindManifestMap[gvk] = append(kindManifestMap[gvk], manifest)
398-
switch typeMeta.Kind {
399-
case "CustomResourceDefinition":
400-
// Collect CRD kinds to filter them out from unsupported manifest types.
401-
// The CRD version type doesn't matter as long as it has a group, kind,
402-
// and versions in the expected fields.
403-
crd := v1beta1.CustomResourceDefinition{}
404-
if err = yaml.Unmarshal(manifest, &crd); err != nil {
405-
return err
406-
}
407-
for _, ver := range crd.Spec.Versions {
408-
crGVK := schema.GroupVersionKind{
409-
Group: crd.Spec.Group,
410-
Version: ver.Name,
411-
Kind: crd.Spec.Names.Kind,
412-
}
413-
crGVKSet[crGVK] = struct{}{}
414-
}
415-
}
416-
}
417-
return scanner.Err()
418-
})
383+
// Read CRD and CR manifests from CRD dir
384+
if err := updateFromManifests(g.Inputs[CRDsDirKey], kindManifestMap); err != nil {
385+
return err
386+
}
387+
388+
// Get owned CRDs from CRD manifests
389+
ownedCRDs, err := getOwnedCRDs(kindManifestMap)
419390
if err != nil {
420-
return fmt.Errorf("failed to walk manifests directory for CSV updates: %v", err)
391+
return err
392+
}
393+
394+
// Read Deployment and RBAC manifests from Deploy dir
395+
if err := updateFromManifests(g.Inputs[DeployDirKey], kindManifestMap); err != nil {
396+
return err
421397
}
422398

399+
// Update CSV from all manifest types
423400
crUpdaters := crs{}
424401
for gvk, manifests := range kindManifestMap {
425402
// We don't necessarily care about sorting by a field value, more about
@@ -437,7 +414,8 @@ func (g csvGenerator) updateCSVFromManifests(csv *olmapiv1alpha1.ClusterServiceV
437414
case "CustomResourceDefinition":
438415
err = crds(manifests).apply(csv)
439416
default:
440-
if _, ok := crGVKSet[gvk]; ok {
417+
// Only update CR examples for owned CRD types
418+
if _, ok := ownedCRDs[gvk]; ok {
441419
crUpdaters = append(crUpdaters, crs(manifests)...)
442420
} else {
443421
log.Infof("Skipping manifest %s", gvk)
@@ -462,3 +440,65 @@ func (g csvGenerator) updateCSVFromManifests(csv *olmapiv1alpha1.ClusterServiceV
462440
}
463441
return nil
464442
}
443+
444+
func updateFromManifests(dir string, kindManifestMap map[schema.GroupVersionKind][][]byte) error {
445+
files, err := ioutil.ReadDir(dir)
446+
if err != nil {
447+
return err
448+
}
449+
// Read and scan all files into kindManifestMap
450+
wd := projutil.MustGetwd()
451+
for _, f := range files {
452+
if f.IsDir() {
453+
continue
454+
}
455+
path := filepath.Join(wd, dir, f.Name())
456+
b, err := ioutil.ReadFile(path)
457+
if err != nil {
458+
return err
459+
}
460+
scanner := yamlutil.NewYAMLScanner(b)
461+
for scanner.Scan() {
462+
manifest := scanner.Bytes()
463+
typeMeta, err := k8sutil.GetTypeMetaFromBytes(manifest)
464+
if err != nil {
465+
log.Infof("No TypeMeta in %s, skipping file", path)
466+
continue
467+
}
468+
469+
gvk := typeMeta.GroupVersionKind()
470+
kindManifestMap[gvk] = append(kindManifestMap[gvk], manifest)
471+
}
472+
if scanner.Err() != nil {
473+
return scanner.Err()
474+
}
475+
}
476+
return nil
477+
}
478+
479+
func getOwnedCRDs(kindManifestMap map[schema.GroupVersionKind][][]byte) (map[schema.GroupVersionKind]struct{}, error) {
480+
ownedCRDs := map[schema.GroupVersionKind]struct{}{}
481+
for gvk, manifests := range kindManifestMap {
482+
if gvk.Kind != "CustomResourceDefinition" {
483+
continue
484+
}
485+
// Collect CRD kinds to filter them out from unsupported manifest types.
486+
// The CRD version type doesn't matter as long as it has a group, kind,
487+
// and versions in the expected fields.
488+
for _, manifest := range manifests {
489+
crd := v1beta1.CustomResourceDefinition{}
490+
if err := yaml.Unmarshal(manifest, &crd); err != nil {
491+
return ownedCRDs, err
492+
}
493+
for _, ver := range crd.Spec.Versions {
494+
crGVK := schema.GroupVersionKind{
495+
Group: crd.Spec.Group,
496+
Version: ver.Name,
497+
Kind: crd.Spec.Names.Kind,
498+
}
499+
ownedCRDs[crGVK] = struct{}{}
500+
}
501+
}
502+
}
503+
return ownedCRDs, nil
504+
}

0 commit comments

Comments
 (0)