Skip to content

Commit 041b120

Browse files
Adding component config proposal
Signed-off-by: Chris Hein <[email protected]>
1 parent 8507812 commit 041b120

File tree

2 files changed

+244
-0
lines changed

2 files changed

+244
-0
lines changed

designs/component-config.md

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# ComponentConfig Controller Runtime Support
2+
3+
Author: @christopherhein
4+
Last Updated on: 02/24/2020
5+
6+
## Table of Contents
7+
8+
<!--ts-->
9+
* [ComponentConfig Controller Runtime Support](#componentconfig-controller-runtime-support)
10+
* [Table of Contents](#table-of-contents)
11+
* [Summary](#summary)
12+
* [Motivation](#motivation)
13+
* [Links to Open Issues](#links-to-open-issues)
14+
* [Goals](#goals)
15+
* [Non-Goals/Future Work](#non-goalsfuture-work)
16+
* [Proposal](#proposal)
17+
* [ComponentConfig Load Order](#componentconfig-load-order)
18+
* [User Stories](#user-stories)
19+
* [Controller Author with controller-runtime](#controller-author-with-controller-runtime)
20+
* [Controller Author with kubebuilder (tbd proposal for kubebuilder)](#controller-author-with-kubebuilder-tbd-proposal-for-kubebuilder)
21+
* [Controller User without modifications to config](#controller-user-without-modifications-to-config)
22+
* [Controller User with modifications to config](#controller-user-with-modifications-to-config)
23+
* [Risks and Mitigations](#risks-and-mitigations)
24+
* [Alternatives](#alternatives)
25+
* [Implementation History](#implementation-history)
26+
27+
<!--te-->
28+
29+
## Summary
30+
31+
Currently controllers that use `controller-runtime` need to configure the `ctrl.Manager` by using flags or hard coding values into the initialization methods. Core Kubernetes has started to move away from using flags as a mechanism for configuring components and standardized along the pattern of [`ComponentConfig` or Versioned Component Configuration Files](https://docs.google.com/document/d/1FdaEJUEh091qf5B98HM6_8MS764iXrxxigNIdwHYW9c/edit). This proposal is to bring `ComponentConfig` patterns into `controller-runtime` to allow controller authors to make `go` types backed by apimachinery to unmarshal and configure the `ctrl.Manager` reducing the flags and allowing code based tools to easily configure controllers instead of requiring them to mutate CLI args.
32+
33+
34+
## Motivation
35+
36+
This change is important because:
37+
- it will help make it easier for controllers to be configured by other machine processes
38+
- it will reduce the upfront flags required to start a controller
39+
- allow for more configuration types which flags don't natively support
40+
- allow using and upgrading older configurations avoiding breaking changes in flags
41+
42+
### Links to Open Issues
43+
44+
- [Provide a ComponentConfig to tweak the Manager](https://github.com/kubernetes-sigs/controller-runtime/issues/518)
45+
- [Reduce command line flag boilerplate](https://github.com/kubernetes-sigs/controller-runtime/issues/207)
46+
- [Implement ComponentConfig by default & stop using (most) flags](https://github.com/kubernetes-sigs/kubebuilder/issues/722)
47+
48+
### Goals
49+
50+
- Provide an interface for pulling configuration data out of exposed `ComponentConfig` types (see below for implementation)
51+
- Provide a new `ctrl.NewFromComponentConfig()` function for initializing a manager
52+
53+
### Non-Goals/Future Work
54+
55+
- `kubebuilder` implementation and design in another PR
56+
- Changing the default `controller-runtime` implementation
57+
- Dynamically reloading ComponentConfig object
58+
59+
## Proposal
60+
61+
The `ctrl.Manager` _SHOULD_ support loading configurations from `ComponentConfig` like objects.
62+
An interface for that object with getters for the specific configuration parameters is created to bridge existing patterns.
63+
64+
Without breaking the current `ctrl.NewManager` which uses an exported `ctrl.Options{}` the `manager.go` can expose a new func, `NewFromComponentConfig()` this would be able to loop through the getters to hydrate an internal `ctrl.Options{}` and pass that into `New()`.
65+
66+
File: _`pkg/manager/manager.go`_
67+
```golang
68+
// ManagerConfiguration defines what the ComponentConfig object for ControllerRuntime needs to support
69+
type ManagerConfiguration interface {
70+
GetSyncPeriod() *time.Duration
71+
72+
GetLeaderElection() bool
73+
GetLeaderElectionNamespace() string
74+
GetLeaderElectionID() string
75+
76+
GetLeaseDuration() *time.Duration
77+
GetRenewDeadline() *time.Duration
78+
GetRetryPeriod() *time.Duration
79+
80+
GetNamespace() string
81+
GetMetricsBindAddress() string
82+
GetHealthProbeBindAddress() string
83+
84+
GetReadinessEndpointName() string
85+
GetLivenessEndpointName() string
86+
87+
GetPort() int
88+
GetHost() string
89+
90+
GetCertDir() string
91+
}
92+
93+
func NewFromComponentConfig(config *rest.Config, scheme *runtime.Scheme, managerconfig ManagerConfiguration) (Manager, error) {
94+
options := Options{}
95+
96+
if scheme != nil {
97+
options.Scheme = scheme
98+
}
99+
100+
// Loop through getters
101+
if managerconfig.GetLeaderElection() {
102+
managerconfig.GetLeaderElection()
103+
}
104+
// ...
105+
106+
return New(config, options)
107+
}
108+
```
109+
110+
#### ComponentConfig Load Order
111+
112+
![ComponentConfig Load Order](/designs/images/component-config-load.png)
113+
114+
Within a separate design (link once created) this will require controller authors to generate a type that implements the `ManagerConfiguration` interface. The following is a sample of what this looks like:
115+
116+
```golang
117+
package config
118+
119+
import (
120+
"time"
121+
122+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
123+
)
124+
125+
type ControllerNameConfigurationSpec struct {
126+
LeaderElection ControllerNameConfigurationLeaderElection `json:"leaderElection,omitempty"`
127+
128+
LeaseDuration *time.Duration `json:"leaseDuration,omitempty"`
129+
RenewDeadline *time.Duration `json:"renewDeadline,omitempty"`
130+
RetryPeriod *time.Duration`json:"retryPeriod,omitempty"`
131+
132+
Namespace string `json:"namespace,omitempty"`
133+
134+
MetricsBindAddress string `json:"metricsBindAddress,omitempty"`
135+
136+
Health ControllerNameConfigurationHealth
137+
138+
Port int `json:"port,omitempty"`
139+
Host string `json:"host,omitempty"`
140+
141+
CertDir string `json:"certDir,omitempty"`
142+
}
143+
144+
type ControllerNameConfigurationLeaderElection struct {
145+
Enabled bool `json:"enabled,omitempty"`
146+
Namespace string `json:"namespace,omitempty"`
147+
ID string `json:"id,omitempty"`
148+
}
149+
150+
type ControllerNameConfigurationHealth struct {
151+
HealthProbeBindAddress string `json:"healthProbeBindAddress,omitempty"`
152+
153+
ReadinessEndpointName string `json:"readinessEndpointName,omitempty"`
154+
LivenessEndpointName string `json:"livenessEndpointName,omitempty"`
155+
}
156+
157+
type ControllerNameConfiguration struct {
158+
metav1.TypeMeta
159+
160+
Spec ControllerNameConfigurationSpec `json:"spec"`
161+
}
162+
```
163+
164+
With the above `struct` we would need to implement the getters to satisfy the `ManagerConfiguration` interface, these could be automatically generated when you `init` or `create componentconfig` from the `kubebuilder` CLI. This could be something as simple as the following.
165+
166+
```golang
167+
package config
168+
169+
import (
170+
"time"
171+
)
172+
173+
func (in *ControllerNameConfiguration) GetSyncPeriod() *time.Duration {}
174+
func (in *ControllerNameConfiguration) GetLeaderElection() bool {}
175+
func (in *ControllerNameConfiguration) GetLeaderElectionNamespace() string {}
176+
func (in *ControllerNameConfiguration) GetLeaderElectionID() string {}
177+
178+
func (in *ControllerNameConfiguration) GetLeaseDuration() *time.Duration {}
179+
func (in *ControllerNameConfiguration) GetRenewDeadline() *time.Duration {}
180+
func (in *ControllerNameConfiguration) GetRetryPeriod() *time.Duration {}
181+
182+
func (in *ControllerNameConfiguration) GetNamespace() string {}
183+
func (in *ControllerNameConfiguration) GetMetricsBindAddress() string {}
184+
func (in *ControllerNameConfiguration) GetHealthProbeBindAddress() string {}
185+
186+
func (in *ControllerNameConfiguration) GetReadinessEndpointName() string {}
187+
func (in *ControllerNameConfiguration) GetLivenessEndpointName() string {}
188+
189+
func (in *ControllerNameConfiguration) GetPort() int {}
190+
func (in *ControllerNameConfiguration) GetHost() string {}
191+
192+
func (in *ControllerNameConfiguration) GetCertDir() string {}
193+
```
194+
195+
Besides the implementation of the `ComponentConfig` The controller author as it stands would also need to implement the unmarshalling of the `ConfigMap` into the `ComponentConfig`, for this `controller-runtime` could expose helper methods to load a file from disk, unmarshal to the struct and pass the pointer into the `NewFromComponentConfig()` to return the `ctrl.Manager`
196+
197+
### User Stories
198+
199+
#### Controller Author with `controller-runtime`
200+
201+
- Implement `ComponentConfig` type
202+
- Implement `ManagerConfiguration` interface for `ComponentConfig` object
203+
- Set up `ConfigMap` unmarshalling into `ComponentConfig` type
204+
- Initialize `ctrl.Manager` with `NewFromComponentConfig`
205+
- Build custom controller as usual
206+
207+
#### Controller Author with `kubebuilder` (tbd proposal for `kubebuilder`)
208+
209+
- Initialize `kubebuilder` project using `--component-config-name=XYZConfiguration`
210+
- Build custom controller as usual
211+
212+
#### Controller User without modifications to config
213+
214+
_Provided that the controller provides manifests_
215+
216+
- Apply the controller to the cluster
217+
- Deploy custom resources
218+
219+
#### Controller User with modifications to config
220+
221+
- _Following from previous example without changes_
222+
- Create a new `ConfigMap` for changes
223+
- Modify the `controller-runtime` pod to use the new `ConfigMap`
224+
- Apply the controller to the cluster
225+
- Deploy custom resources
226+
227+
228+
### Risks and Mitigations
229+
230+
- Given that this isn't changing the core Manager initialization for `controller-runtime` it's fairly low risk
231+
- If the underlaying `ctrl.Options{}`
232+
233+
## Alternatives
234+
235+
* `NewFromComponentConfig()` could load the object from disk and hydrate the `ComponentConfig` type
236+
237+
## Implementation History
238+
239+
- [x] 02/19/2020: Proposed idea in an issue or [community meeting]
240+
- [x] 02/24/2020: Proposal submitted to `controller-runtime`
241+
242+
243+
<!-- Links -->
244+
[community meeting]: https://docs.google.com/document/d/1Ih-2cgg1bUrLwLVTB9tADlPcVdgnuMNBGbUl4D-0TIk
27.8 KB
Loading

0 commit comments

Comments
 (0)