Skip to content

Commit 91e6341

Browse files
committed
document thoughts on code generation
Adds a document discussing options for code generation and a diagram with how a multi-phase hybrid custom+controller-runtime generator might work. Issue #4
1 parent db9be49 commit 91e6341

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed

docs/code-generation.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Code generation
2+
3+
In order to keep the code for all the service controllers consistent, we will
4+
use a strategy of generating the custom resource definitions and controller
5+
code stubs for new AWS services.
6+
7+
To generate the CRDs and controller stub code, we investigated a number of
8+
options:
9+
10+
* home-grown custom code generator
11+
* [kudo](https://kudo.dev)
12+
* [kubebuilder](github.com/kubernetes-sigs/kubebuilder)
13+
* a hybrid custom code generator + sigs.kubernetes.io/controller-tools (CR)
14+
15+
The original AWS Service Operator used a [custom-built generator][1] that
16+
processed [YAML manifests][2] describing the AWS service and used
17+
[templates][3] to [generate CRDs][4], the [controller code][5] itself and the
18+
[Go types][6] that represent the CRDs in memory. It's worth noting that the
19+
CRDs *and* the controller code that was generated by the original ASO was very
20+
tightly coupled to CloudFormation. In fact, the CRDs for individual AWS
21+
services like S3 or RDS were thin wrappers around CloudFormation stacks that
22+
described the object being operated upon.
23+
24+
[1]: https://github.com/amazon-archives/aws-service-operator/tree/master/code-generation
25+
[2]: https://github.com/amazon-archives/aws-service-operator/tree/master/models
26+
[3]: https://github.com/amazon-archives/aws-service-operator/tree/master/code-generation/pkg/codegen/assets
27+
[4]: https://github.com/amazon-archives/aws-service-operator/blob/b4befd62322a57ac78aa39ea08771fc32912592a/code-generation/pkg/codegen/assets/aws-service-operator.yaml.templ#L13-L31
28+
[5]: https://github.com/amazon-archives/aws-service-operator/blob/master/code-generation/pkg/codegen/assets/operator.go.templ
29+
[6]: https://github.com/amazon-archives/aws-service-operator/blob/master/code-generation/pkg/codegen/assets/types.go.templ
30+
31+
kudo is a platform for building Kubernetes Operators. It stores state in its
32+
own kudo.dev CRDs and allows users to define "plans" for a deployed application
33+
to deploy itself. We determined that kudo was not a particularly good fit for
34+
ASO for a couple reasons. First, we needed a way to generate CRDs in several
35+
API groups (s3.aws.com and iam.aws.com for example) and the ASO controller code
36+
isn't deploying an "application" that needs to have a controlled deployment
37+
plan. Instead, ASO is a controller that facilitates creation and management of
38+
various AWS service objects using Kubernetes CRD instances.
39+
40+
kubebuilder is the recommended upstream tool for generating CRDs and controller
41+
stub code. It is a Go binary that creates the scaffolding for CRDs and
42+
controller Go code. Unfortunately, it doesn't really handle multiple API groups
43+
in the same source code repository, and we need that for ASO. For example, we
44+
will have an `s3.service-operator.aws` and an `iam.service-operator.aws` API
45+
group with CRDs representing those different API objects and interfaces.
46+
47+
That said, much of kubebuilder simply sets up some templates and then runs the
48+
`controller-gen` binary from the sigs.kubernetes.io/controller-tools repository
49+
to generate Go code.
50+
51+
Our final option was to build a hybrid custom code generator that used
52+
controller-runtime under the hood but allowed us to generate controller stub
53+
code for multiple API groups and place generated code [in directories][7] that
54+
represented Go best practices. This option gives us the flexibility to generate
55+
the files and content for multiple API groups but still stay within the
56+
recommended guardrails of the upstream Kubernetes community.
57+
58+
[7]: https://github.com/kubernetes-sigs/kubebuilder/issues/1268
59+
60+
## Hybrid custom+controller-runtime proposal
61+
62+
![Multi-phase approach to code generation for ASO](images/multi-phase-code-generation.png)
63+
64+
This approach uses multiple phases of code generation.
65+
66+
The first code generation phase consumes model information from a canonical
67+
source of truth about an AWS service and the objects and interfaces that
68+
service exposes and generates one or more files containing code that exposes
69+
Go types for those objects. These "type files" should be annotated with the
70+
marker and comments that will allow the core code generators and controller-gen
71+
to do its work.
72+
73+
Once we have generated the type files, we need to generate basic scaffolding
74+
for consumers of those types. The upstream code-generator project contains
75+
generators for this scaffolding. We should be able to make a modified version
76+
of the upstream [`generate-groups.sh`][8] script to generate all the defaults,
77+
deepcopy, informers, listers and clientset code.
78+
79+
[8]: https://github.com/kubernetes/code-generator/blob/6b257a9d6f461b5e15dc2f0d13e29731a5b5255a/generate-groups.sh
80+
81+
Next, we will need to generate some skeleton code for the reconciling
82+
controller handling the new API along with the YAML files representing the CRDs
83+
that will get loaded into kube-apiserver and the YAML files for RBAC and
84+
OpenAPI v3 schemas for the CRDs. This is where the [`controller-gen`][9] tool
85+
from the sigs.kubernetes.io/controller-tools project will come in handy.
86+
87+
[9]: https://github.com/kubernetes-sigs/controller-tools/blob/a5fa7b956b85a6e792bc7086fedf7107d62452b1/cmd/controller-gen/main.go
88+
89+
We also want to generate some stub code for a new reconciling controller into
90+
the primary aws-service-operator binary.
91+
92+
When we run `make generate $SERVICE $VERSION`, we should end up with a directory
93+
structure like this:
94+
95+
```
96+
/cmd
97+
/aws-service-operator
98+
main.go
99+
/crds
100+
/$SERVICE
101+
crds.yaml
102+
rbac.yaml
103+
/pkg
104+
/apis
105+
/$SERVICE
106+
/$VERSION
107+
doc.go
108+
register.go
109+
types.go
110+
deepcopy.go
111+
defaults.go
112+
/client
113+
/clientset
114+
/versioned
115+
/fake
116+
...
117+
/scheme
118+
...
119+
/typed
120+
/$SERVICE
121+
/$VERSION
122+
123+
/controllers
124+
/$SERVICE
125+
controller.go
126+
/informers
127+
/externalversions
128+
/$SERVICE
129+
/$VERSION
130+
...
131+
/internalinterfaces
132+
/listers
133+
/$SERVICE
134+
/$VERSION
135+
...
136+
```
51.4 KB
Loading

0 commit comments

Comments
 (0)