Skip to content

Commit 9a32696

Browse files
author
Eric Stroczynski
authored
internal/generate: align CRD generator with controller-gen (#2201)
This commit aligns the CRD generator with controller-gen's. * cmd/operator-sdk: replace CRD scaffold with CRD generator * internal/generate/gen: Config configures Generator's using common data they may find useful. Config should be passed to a Generator's Constructor. See internal/generate/crd.NewCRDGo for an example * internal/generate/crd: CRD generator for Go and non-Go operators * test/test-framework: update operator types with storageversion annotations * internal/util/fileutil: DotPath prepends "./" or ".\" to a local path, depending on OS * doc: discuss storage versions in CRDs
1 parent 49b53b0 commit 9a32696

File tree

20 files changed

+860
-623
lines changed

20 files changed

+860
-623
lines changed

cmd/operator-sdk/add/api.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ func apiRun(cmd *cobra.Command, args []string) error {
124124
&scaffold.Register{Resource: r},
125125
&scaffold.Doc{Resource: r},
126126
&scaffold.CR{Resource: r},
127-
&scaffold.CRD{Resource: r, IsOperatorGo: projutil.IsOperatorGo()},
128127
)
129128
if err != nil {
130129
return fmt.Errorf("api scaffold failed: (%v)", err)

cmd/operator-sdk/add/crd.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"path/filepath"
2121
"strings"
2222

23+
gencrd "github.com/operator-framework/operator-sdk/internal/generate/crd"
24+
gen "github.com/operator-framework/operator-sdk/internal/generate/gen"
2325
"github.com/operator-framework/operator-sdk/internal/scaffold"
2426
"github.com/operator-framework/operator-sdk/internal/scaffold/input"
2527
"github.com/operator-framework/operator-sdk/internal/util/projutil"
@@ -74,7 +76,7 @@ func crdFunc(cmd *cobra.Command, args []string) error {
7476
return err
7577
}
7678

77-
log.Infof("Generating Custom Resource Definition (CRD) version %s for kind %s.", apiVersion, kind)
79+
log.Infof("Generating CustomResourceDefinition (CRD) version %s for kind %s.", apiVersion, kind)
7880

7981
// generate CR/CRD file
8082
resource, err := scaffold.NewResource(apiVersion, kind)
@@ -84,11 +86,6 @@ func crdFunc(cmd *cobra.Command, args []string) error {
8486

8587
s := scaffold.Scaffold{}
8688
err = s.Execute(cfg,
87-
&scaffold.CRD{
88-
Input: input.Input{IfExistsAction: input.Skip},
89-
Resource: resource,
90-
IsOperatorGo: projutil.IsOperatorGo(),
91-
},
9289
&scaffold.CR{
9390
Input: input.Input{IfExistsAction: input.Skip},
9491
Resource: resource,
@@ -98,6 +95,14 @@ func crdFunc(cmd *cobra.Command, args []string) error {
9895
return fmt.Errorf("crd scaffold failed: (%v)", err)
9996
}
10097

98+
// This command does not consider an APIs dir. Instead it adds a plain CRD
99+
// for the provided resource. We can use NewCRDNonGo to get this behavior.
100+
gcfg := gen.Config{}
101+
crd := gencrd.NewCRDNonGo(gcfg, *resource)
102+
if err := crd.Generate(); err != nil {
103+
return fmt.Errorf("error generating CRD for %s: %w", resource, err)
104+
}
105+
101106
// update deploy/role.yaml for the given resource r.
102107
if err := scaffold.UpdateRoleForResource(resource, cfg.AbsProjectPath); err != nil {
103108
return fmt.Errorf("failed to update the RBAC manifest for the resource (%v, %v): (%v)", resource.APIVersion, resource.Kind, err)

cmd/operator-sdk/generate/crds.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"fmt"
1919

2020
"github.com/operator-framework/operator-sdk/cmd/operator-sdk/internal/genutil"
21+
22+
log "github.com/sirupsen/logrus"
2123
"github.com/spf13/cobra"
2224
)
2325

@@ -45,5 +47,10 @@ func crdsFunc(cmd *cobra.Command, args []string) error {
4547
return fmt.Errorf("command %s doesn't accept any arguments", cmd.CommandPath())
4648
}
4749

48-
return genutil.CRDGen()
50+
// Skip usage printing on error, since this command will never fail from
51+
// improper CLI usage.
52+
if err := genutil.CRDGen(); err != nil {
53+
log.Fatal(err)
54+
}
55+
return nil
4956
}

cmd/operator-sdk/internal/genutil/crds.go

Lines changed: 7 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@ package genutil
1616

1717
import (
1818
"fmt"
19-
"path/filepath"
20-
"strings"
2119

20+
gencrd "github.com/operator-framework/operator-sdk/internal/generate/crd"
21+
gen "github.com/operator-framework/operator-sdk/internal/generate/gen"
2222
"github.com/operator-framework/operator-sdk/internal/scaffold"
23-
"github.com/operator-framework/operator-sdk/internal/scaffold/input"
24-
"github.com/operator-framework/operator-sdk/internal/util/k8sutil"
2523
"github.com/operator-framework/operator-sdk/internal/util/projutil"
2624

2725
log "github.com/sirupsen/logrus"
@@ -31,49 +29,12 @@ import (
3129
func CRDGen() error {
3230
projutil.MustInProjectRoot()
3331

34-
absProjectPath := projutil.MustGetwd()
35-
repoPkg := projutil.GetGoPkg()
32+
log.Info("Running CRD generator.")
3633

37-
gvMap, err := k8sutil.ParseGroupSubpackages(scaffold.ApisDir)
38-
if err != nil {
39-
return fmt.Errorf("failed to parse group versions: (%v)", err)
40-
}
41-
gvb := &strings.Builder{}
42-
for g, vs := range gvMap {
43-
gvb.WriteString(fmt.Sprintf("%s:%v, ", g, vs))
44-
}
45-
46-
log.Infof("Running CRD generation for Custom Resource group versions: [%v]\n", gvb.String())
47-
48-
s := &scaffold.Scaffold{}
49-
cfg := &input.Config{
50-
Repo: repoPkg,
51-
AbsProjectPath: absProjectPath,
52-
ProjectName: filepath.Base(absProjectPath),
53-
}
54-
crds, err := k8sutil.GetCRDs(scaffold.CRDsDir)
55-
if err != nil {
56-
return err
57-
}
58-
for _, crd := range crds {
59-
g, v, k := crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Kind
60-
if v == "" {
61-
if len(crd.Spec.Versions) != 0 {
62-
v = crd.Spec.Versions[0].Name
63-
} else {
64-
return fmt.Errorf("crd of group %s kind %s has no version", g, k)
65-
}
66-
}
67-
r, err := scaffold.NewResource(g+"/"+v, k)
68-
if err != nil {
69-
return err
70-
}
71-
err = s.Execute(cfg,
72-
&scaffold.CRD{Resource: r, IsOperatorGo: projutil.IsOperatorGo()},
73-
)
74-
if err != nil {
75-
return err
76-
}
34+
cfg := gen.Config{}
35+
crd := gencrd.NewCRDGo(cfg)
36+
if err := crd.Generate(); err != nil {
37+
return fmt.Errorf("error generating CRDs from APIs in %s: %w", scaffold.ApisDir, err)
7738
}
7839

7940
log.Info("CRD generation complete.")

cmd/operator-sdk/new/cmd.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import (
2222
"path/filepath"
2323
"strings"
2424

25+
gencrd "github.com/operator-framework/operator-sdk/internal/generate/crd"
26+
gen "github.com/operator-framework/operator-sdk/internal/generate/gen"
2527
"github.com/operator-framework/operator-sdk/internal/scaffold"
2628
"github.com/operator-framework/operator-sdk/internal/scaffold/ansible"
2729
"github.com/operator-framework/operator-sdk/internal/scaffold/helm"
@@ -231,7 +233,6 @@ func doAnsibleScaffold() error {
231233
&scaffold.ServiceAccount{},
232234
&scaffold.Role{},
233235
&scaffold.RoleBinding{},
234-
&scaffold.CRD{Resource: resource},
235236
&scaffold.CR{Resource: resource},
236237
&ansible.BuildDockerfile{GeneratePlaybook: generatePlaybook},
237238
&ansible.RolesReadme{Resource: *resource},
@@ -267,6 +268,10 @@ func doAnsibleScaffold() error {
267268
return fmt.Errorf("new ansible scaffold failed: (%v)", err)
268269
}
269270

271+
if err = generateCRDNonGo(projectName, *resource); err != nil {
272+
return err
273+
}
274+
270275
// Remove placeholders from empty directories
271276
err = os.Remove(filepath.Join(s.AbsProjectPath, roleFiles.Path))
272277
if err != nil {
@@ -343,7 +348,6 @@ func doHelmScaffold() error {
343348
&roleScaffold,
344349
&scaffold.RoleBinding{IsClusterScoped: roleScaffold.IsClusterScoped},
345350
&helm.Operator{},
346-
&scaffold.CRD{Resource: resource},
347351
&scaffold.CR{
348352
Resource: resource,
349353
Spec: crSpec,
@@ -353,12 +357,30 @@ func doHelmScaffold() error {
353357
return fmt.Errorf("new helm scaffold failed: %w", err)
354358
}
355359

360+
if err = generateCRDNonGo(projectName, *resource); err != nil {
361+
return err
362+
}
363+
356364
if err := scaffold.UpdateRoleForResource(resource, cfg.AbsProjectPath); err != nil {
357365
return fmt.Errorf("failed to update the RBAC manifest for resource (%v, %v): %w", resource.APIVersion, resource.Kind, err)
358366
}
359367
return nil
360368
}
361369

370+
func generateCRDNonGo(projectName string, resource scaffold.Resource) error {
371+
crdsDir := filepath.Join(projectName, scaffold.CRDsDir)
372+
gcfg := gen.Config{
373+
Inputs: map[string]string{gencrd.CRDsDirKey: crdsDir},
374+
OutputDir: crdsDir,
375+
}
376+
crd := gencrd.NewCRDNonGo(gcfg, resource)
377+
if err := crd.Generate(); err != nil {
378+
return fmt.Errorf("error generating CRD for %s: %w", resource, err)
379+
}
380+
log.Info("Generated CustomResourceDefinition manifests.")
381+
return nil
382+
}
383+
362384
func verifyFlags() error {
363385
if operatorType != projutil.OperatorTypeGo && operatorType != projutil.OperatorTypeAnsible && operatorType != projutil.OperatorTypeHelm {
364386
return errors.Wrap(projutil.ErrUnknownOperatorType{Type: operatorType}, "value of --type can only be `go`, `ansible`, or `helm`")

doc/user-guide.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ type MemcachedSpec struct {
9696
Size int32 `json:"size"`
9797
}
9898
type MemcachedStatus struct {
99-
// Nodes are the names of the memcached pods
99+
// Nodes are the names of the memcached pods
100100
Nodes []string `json:"nodes"`
101101
}
102102
```
@@ -107,7 +107,22 @@ After modifying the `*_types.go` file always run the following command to update
107107
$ operator-sdk generate k8s
108108
```
109109

110-
### OpenAPI validation
110+
### Updating CRD manifests
111+
112+
Now that `MemcachedSpec` and `MemcachedStatus` have fields and possibly annotations, the CRD corresponding to the API's group and kind must be updated. To do so, run the following command:
113+
114+
```console
115+
$ operator-sdk generate crds
116+
```
117+
118+
**Notes:**
119+
- - Your CRD *must* specify exactly one [storage version][crd-storage-version]. Use the `+kubebuilder:storageversion` [marker][crd-markers] to indicate the GVK that should be used to store data by the API server. This marker should be in a comment above your `Memcached` type.
120+
121+
[crd-storage-version]:https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definition-versioning/#writing-reading-and-updating-versioned-customresourcedefinition-objects
122+
[crd-markers]:https://book.kubebuilder.io/reference/markers/crd.html
123+
[api-rules]: https://github.com/kubernetes/kubernetes/tree/36981002246682ed7dc4de54ccc2a96c1a0cbbdb/api/api-rules
124+
125+
#### OpenAPI validation
111126

112127
OpenAPIv3 schemas are added to CRD manifests in the `spec.validation` block when the manifests are generated. This validation block allows Kubernetes to validate the properties in a Memcached Custom Resource when it is created or updated.
113128

@@ -146,7 +161,6 @@ To learn more about OpenAPI v3.0 validation schemas in Custom Resource Definitio
146161
[generating-crd]: https://book.kubebuilder.io/reference/generating-crd.html
147162
[markers]: https://book.kubebuilder.io/reference/markers.html
148163
[crd-markers]: https://book.kubebuilder.io/reference/markers/crd-validation.html
149-
[api-rules]: https://github.com/kubernetes/kubernetes/tree/36981002246682ed7dc4de54ccc2a96c1a0cbbdb/api/api-rules
150164
151165
## Add a new Controller
152166
@@ -679,4 +693,4 @@ When the operator is not running in a cluster, the Manager will return an error
679693
[result_go_doc]: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/reconcile#Result
680694
[metrics_doc]: ./user/metrics/README.md
681695
[quay_link]: https://quay.io
682-
[multi-namespaced-cache-builder]: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/cache#MultiNamespacedCacheBuilder
696+
[multi-namespaced-cache-builder]: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/cache#MultiNamespacedCacheBuilder

doc/user/migrating-existing-apis.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,13 +207,12 @@ This command will automatically update all CRD manifests.
207207

208208
#### CRD Versioning
209209

210-
<!-- TODO: change SDK version to the last release before controller-tools v0.2.0 refactor -->
210+
Kubernetes 1.11+ supports CRD [`spec.versions`][crd-versions] and `spec.version` is [deprecated][crd-version-deprecated] as of Kubernetes 1.12. SDK versions `v0.10.x` and below leverage [`[email protected]`][controller-tools]' CRD generator which generates a now-deprecated `spec.version` value based on the version contained in an API's import path. Names of CRD manifest files generated by those SDK versions contain the `spec.version`, i.e. one CRD manifest is created *per version in a group* with the file name format `<group>_<version>_<kind>_crd.yaml`. SDK versions `v0.11+` use `controller-tools@v0.2.x`, which generates `spec.versions` but not `spec.version` by default, and use the file name format `<full_group>_<resource>_crd.yaml`.
211211

212-
Kubernetes 1.11+ supports CRD [`spec.versions`][crd-versions] and `spec.version` is [deprecated][crd-version-deprecated] as of Kubernetes 1.12. SDK versions `v0.10.x` and below leverage [`controller-tools`][controller-tools]' CRD generator `v0.1.x` which generates a now-deprecated `spec.version` value based on the version contained in an APIs import path. Names of CRD manifest files generated by those SDK versions contain the `spec.version`, i.e. one CRD manifest is created *per version in a group* with the form `<group>_<version>_<kind>_crd.yaml`. The SDK is in the process of upgrading to `controller-tools` `v0.2.x`, which generates `spec.versions` but not `spec.version` by default. Once the upgrade is complete, future SDK versions will place all versions in a group in `spec.versions`. File names will then have the format `<full_group>_<resource>_crd.yaml`.
213-
214-
**Note**: `<full group>` is the full group name of your CRD while `<group>` is the last subdomain of `<full group>`, ex. `foo.bar.com` vs `foo`. `<resource>` is the plural lower-case of CRD `Kind` specified at `spec.names.plural`.
215-
216-
**Note:** If your operator does not have custom data manually added to its CRD's, you can skip to the [following section](#golang-api-migrations-types-and-commonalities); `operator-sdk generate crds` will handle CRD updates in that case.
212+
**Notes:**
213+
- `<full group>` is the full group name of your CRD while `<group>` is the last subdomain of `<full group>`, ex. `foo.bar.com` vs `foo`. `<resource>` is the plural lower-case of CRD `Kind` specified at `spec.names.plural`.
214+
- Your CRD *must* specify exactly one [storage version][crd-storage-version]. Use the `+kubebuilder:storageversion` [marker][crd-markers] to indicate the GVK that should be used to store data by the API server. This marker should be in a comment above your `CatalogSourceConfig` type.
215+
- If your operator does not have custom data manually added to its CRD's, you can skip to the [following section](#golang-api-migrations-types-and-commonalities); `operator-sdk generate crds` will handle CRD updates in that case.
217216

218217
Upgrading from `spec.version` to `spec.versions` will be demonstrated using the following CRD manifest example:
219218

@@ -400,6 +399,8 @@ TODO
400399
[k8s-versioning]:https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-versioning
401400
[deepcopy-gen]:https://godoc.org/k8s.io/gengo/examples/deepcopy-gen
402401
[client-gen]:https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/generating-clientset.md
402+
[crd-storage-version]:https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definition-versioning/#writing-reading-and-updating-versioned-customresourcedefinition-objects
403+
[crd-markers]:https://book.kubebuilder.io/reference/markers/crd.html
403404
[controller-tools]:https://github.com/kubernetes-sigs/controller-tools
404405
[crd-versions]:https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definition-versioning/
405406
[crd-conv]:https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definition-versioning/#webhook-conversion

0 commit comments

Comments
 (0)