sigs.k8s.io/controller-runtime@v0.18.2/designs/component-config.md (about) 1 # ComponentConfig Controller Runtime Support 2 Author: @christopherhein 3 4 Last Updated on: 03/02/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 * [Embeddable ComponentConfig Type](#embeddable-componentconfig-type) 19 * [Default ComponentConfig Type](#default-componentconfig-type) 20 * [Using Flags w/ ComponentConfig](#using-flags-w-componentconfig) 21 * [Kubebuilder Scaffolding Example](#kubebuilder-scaffolding-example) 22 * [User Stories](#user-stories) 23 * [Controller Author with controller-runtime and default type](#controller-author-with-controller-runtime-and-default-type) 24 * [Controller Author with controller-runtime and custom type](#controller-author-with-controller-runtime-and-custom-type) 25 * [Controller Author with kubebuilder (tbd proposal for kubebuilder)](#controller-author-with-kubebuilder-tbd-proposal-for-kubebuilder) 26 * [Controller User without modifications to config](#controller-user-without-modifications-to-config) 27 * [Controller User with modifications to config](#controller-user-with-modifications-to-config) 28 * [Risks and Mitigations](#risks-and-mitigations) 29 * [Alternatives](#alternatives) 30 * [Implementation History](#implementation-history) 31 32 <!--te--> 33 34 ## Summary 35 36 Currently controllers that use `controller-runtime` need to configure the `ctrl.Manager` by using flags or hardcoding values into the initialization methods. Core Kubernetes has started to move away from using flags as a mechanism for configuring components and standardized on [`ComponentConfig` or Versioned Component Configuration Files](https://docs.google.com/document/d/1FdaEJUEh091qf5B98HM6_8MS764iXrxxigNIdwHYW9c/edit). This proposal is to bring `ComponentConfig` to `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. 37 38 ## Motivation 39 40 This change is important because: 41 - it will help make it easier for controllers to be configured by other machine processes 42 - it will reduce the required flags required to start a controller 43 - allow for configuration types which aren't natively supported by flags 44 - allow using and upgrading older configurations avoiding breaking changes in flags 45 46 ### Links to Open Issues 47 48 - [#518 Provide a ComponentConfig to tweak the Manager](https://github.com/kubernetes-sigs/controller-runtime/issues/518) 49 - [#207 Reduce command line flag boilerplate](https://github.com/kubernetes-sigs/controller-runtime/issues/207) 50 - [#722 Implement ComponentConfig by default & stop using (most) flags](https://github.com/kubernetes-sigs/kubebuilder/issues/722) 51 52 ### Goals 53 54 - Provide an interface for pulling configuration data out of exposed `ComponentConfig` types (see below for implementation) 55 - Provide a new `ctrl.NewFromComponentConfig()` function for initializing a manager 56 - Provide an embeddable `ControllerManagerConfiguration` type for easily authoring `ComponentConfig` types 57 - Provide an `DefaultControllerConfig` to make the switching easier for clients 58 59 ### Non-Goals/Future Work 60 61 - `kubebuilder` implementation and design in another PR 62 - Changing the default `controller-runtime` implementation 63 - Dynamically reloading `ComponentConfig` object 64 - Providing `flags` interface and overrides 65 66 ## Proposal 67 68 The `ctrl.Manager` _SHOULD_ support loading configurations from `ComponentConfig` like objects. 69 An interface for that object with getters for the specific configuration parameters is created to bridge existing patterns. 70 71 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 populate an internal `ctrl.Options{}` and pass that into `New()`. 72 73 ```golang 74 //pkg/manager/manager.go 75 76 // ManagerConfiguration defines what the ComponentConfig object for ControllerRuntime needs to support 77 type ManagerConfiguration interface { 78 GetSyncPeriod() *time.Duration 79 80 GetLeaderElection() bool 81 GetLeaderElectionNamespace() string 82 GetLeaderElectionID() string 83 84 GetLeaseDuration() *time.Duration 85 GetRenewDeadline() *time.Duration 86 GetRetryPeriod() *time.Duration 87 88 GetNamespace() string 89 GetMetricsBindAddress() string 90 GetHealthProbeBindAddress() string 91 92 GetReadinessEndpointName() string 93 GetLivenessEndpointName() string 94 95 GetPort() int 96 GetHost() string 97 98 GetCertDir() string 99 } 100 101 func NewFromComponentConfig(config *rest.Config, scheme *runtime.Scheme, filename string, managerconfig ManagerConfiguration) (Manager, error) { 102 codecs := serializer.NewCodecFactory(scheme) 103 if err := decodeComponentConfigFileInto(codecs, filename, managerconfig); err != nil { 104 105 } 106 options := Options{} 107 108 if scheme != nil { 109 options.Scheme = scheme 110 } 111 112 // Loop through getters 113 if managerconfig.GetLeaderElection() { 114 options.LeaderElection = managerconfig.GetLeaderElection() 115 } 116 // ... 117 118 return New(config, options) 119 } 120 ``` 121 122 #### ComponentConfig Load Order 123 124  125 126 #### Embeddable ComponentConfig Type 127 128 To make this easier for Controller authors `controller-runtime` can expose a set of `config.ControllerConfiguration` type that can be embedded similar to the way that `k8s.io/apimachinery/pkg/apis/meta/v1` works for `TypeMeta` and `ObjectMeta` these could live in `pkg/api/config/v1alpha1/types.go`. See the `DefaultComponentConfig` type below for and example implementation. 129 130 ```golang 131 // pkg/api/config/v1alpha1/types.go 132 package v1alpha1 133 134 import ( 135 "time" 136 137 configv1alpha1 "k8s.io/component-base/config/v1alpha1" 138 ) 139 140 // ControllerManagerConfiguration defines the embedded RuntimeConfiguration for controller-runtime clients. 141 type ControllerManagerConfiguration struct { 142 Namespace string `json:"namespace,omitempty"` 143 144 SyncPeriod *time.Duration `json:"syncPeriod,omitempty"` 145 146 LeaderElection configv1alpha1.LeaderElectionConfiguration `json:"leaderElection,omitempty"` 147 148 MetricsBindAddress string `json:"metricsBindAddress,omitempty"` 149 150 Health ControllerManagerConfigurationHealth `json:"health,omitempty"` 151 152 Port *int `json:"port,omitempty"` 153 Host string `json:"host,omitempty"` 154 155 CertDir string `json:"certDir,omitempty"` 156 } 157 158 // ControllerManagerConfigurationHealth defines the health configs 159 type ControllerManagerConfigurationHealth struct { 160 HealthProbeBindAddress string `json:"healthProbeBindAddress,omitempty"` 161 162 ReadinessEndpointName string `json:"readinessEndpointName,omitempty"` 163 LivenessEndpointName string `json:"livenessEndpointName,omitempty"` 164 } 165 ``` 166 167 168 169 #### Default ComponentConfig Type 170 171 To enable `controller-runtime` to have a default `ComponentConfig` struct which can be used instead of requiring each controller or extension to build its own `ComponentConfig` type, we can create a `DefaultControllerConfiguration` type which can exist in `pkg/api/config/v1alpha1/types.go`. This will allow the controller authors to use this before needing to implement their own type with additional configs. 172 173 ```golang 174 // pkg/api/config/v1alpha1/types.go 175 package v1alpha1 176 177 import ( 178 "time" 179 180 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 181 configv1alpha1 "sigs.k8s.io/controller-runtime/pkg/apis/config/v1alpha1" 182 ) 183 184 // DefaultControllerManagerConfiguration is the Schema for the DefaultControllerManagerConfigurations API 185 type DefaultControllerManagerConfiguration struct { 186 metav1.TypeMeta `json:",inline"` 187 188 Spec configv1alpha1.ControllerManagerConfiguration `json:"spec,omitempty"` 189 } 190 ``` 191 192 This would allow a controller author to use this struct with any config that supports the json/yaml structure. For example a controller author could define their `Kind` as `FoobarControllerConfiguration` and have it defined as the following. 193 194 ```yaml 195 # config.yaml 196 apiVersion: config.somedomain.io/v1alpha1 197 kind: FoobarControllerManagerConfiguration 198 spec: 199 port: 9443 200 metricsBindAddress: ":8080" 201 leaderElection: 202 leaderElect: false 203 ``` 204 205 Given the following config and `DefaultControllerManagerConfiguration` we'd be able to initialize the controller using the following. 206 207 208 ```golang 209 mgr, err := ctrl.NewManagerFromComponentConfig(ctrl.GetConfigOrDie(), scheme, configname, &defaultv1alpha1.DefaultControllerManagerConfiguration{}) 210 if err != nil { 211 // ... 212 } 213 ``` 214 215 The above example uses `configname` which is the name of the file to load the configuration from and uses `scheme` to get the specific serializer, eg `serializer.NewCodecFactory(scheme)`. This will allow the configuration to be unmarshalled into the `runtime.Object` type and passed into the 216 `ctrl.NewManagerFromComponentConfig()` as a `ManagerConfiguration` interface. 217 218 #### Using Flags w/ ComponentConfig 219 220 Since this design still requires setting up the initial `ComponentConfig` type and passing in a pointer to `ctrl.NewFromComponentConfig()` if you want to allow for the use of flags, your controller can use any of the different flagging interfaces. eg [`flag`](https://golang.org/pkg/flag/), [`pflag`](https://pkg.go.dev/github.com/spf13/pflag), [`flagnum`](https://pkg.go.dev/github.com/luci/luci-go/common/flag/flagenum) and set values on the `ComponentConfig` type prior to passing the pointer into the `ctrl.NewFromComponentConfig()`, example below. 221 222 ```golang 223 leaderElect := true 224 225 config := &defaultv1alpha1.DefaultControllerManagerConfiguration{ 226 Spec: configv1alpha1.ControllerManagerConfiguration{ 227 LeaderElection: configv1alpha1.LeaderElectionConfiguration{ 228 LeaderElect: &leaderElect, 229 }, 230 }, 231 } 232 mgr, err := ctrl.NewManagerFromComponentConfig(ctrl.GetConfigOrDie(), scheme, configname, config) 233 if err != nil { 234 // ... 235 } 236 ``` 237 238 #### Kubebuilder Scaffolding Example 239 240 Within expanded in a separate design _(link once created)_ this will allow controller authors to generate a type that implements the `ManagerConfiguration` interface. The following is a sample of what this looks like: 241 242 ```golang 243 package config 244 245 import ( 246 "time" 247 248 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 249 configv1alpha1 "sigs.k8s.io/controller-runtime/pkg/apis/config/v1alpha1" 250 ) 251 252 type ControllerNameConfigurationSpec struct { 253 configv1alpha1.ControllerManagerConfiguration `json:",inline"` 254 } 255 256 type ControllerNameConfiguration struct { 257 metav1.TypeMeta 258 259 Spec ControllerNameConfigurationSpec `json:"spec"` 260 } 261 ``` 262 263 Usage of this custom `ComponentConfig` type would require then changing the `ctrl.NewFromComponentConfig()` to use the new struct vs the `DefaultControllerManagerConfiguration`. 264 265 ## User Stories 266 267 ### Controller Author with `controller-runtime` and default type 268 269 - Mount `ConfigMap` 270 - Initialize `ctrl.Manager` with `NewFromComponentConfig` with config name and `DefaultControllerManagerConfiguration` type 271 - Build custom controller as usual 272 273 ### Controller Author with `controller-runtime` and custom type 274 275 - Implement `ComponentConfig` type 276 - Embed `ControllerManagerConfiguration` type 277 - Mount `ConfigMap` 278 - Initialize `ctrl.Manager` with `NewFromComponentConfig` with config name and `ComponentConfig` type 279 - Build custom controller as usual 280 281 ### Controller Author with `kubebuilder` (tbd proposal for `kubebuilder`) 282 283 - Initialize `kubebuilder` project using `--component-config-name=XYZConfiguration` 284 - Build custom controller as usual 285 286 ### Controller User without modifications to config 287 288 _Provided that the controller provides manifests_ 289 290 - Apply the controller to the cluster 291 - Deploy custom resources 292 293 ### Controller User with modifications to config 294 295 - _Following from previous example without changes_ 296 - Create a new `ConfigMap` for changes 297 - Modify the `controller-runtime` pod to use the new `ConfigMap` 298 - Apply the controller to the cluster 299 - Deploy custom resources 300 301 302 ## Risks and Mitigations 303 304 - Given that this isn't changing the core Manager initialization for `controller-runtime` it's fairly low risk 305 306 ## Alternatives 307 308 * `NewFromComponentConfig()` could load the object from disk based on the file name and hydrate the `ComponentConfig` type. 309 310 ## Implementation History 311 312 - [x] 02/19/2020: Proposed idea in an issue or [community meeting] 313 - [x] 02/24/2020: Proposal submitted to `controller-runtime` 314 - [x] 03/02/2020: Updated with default `DefaultControllerManagerConfiguration` 315 - [x] 03/04/2020: Updated with embeddable `RuntimeConfig` 316 - [x] 03/10/2020: Updated embeddable name to `ControllerManagerConfiguration` 317 318 319 <!-- Links --> 320 [community meeting]: https://docs.google.com/document/d/1Ih-2cgg1bUrLwLVTB9tADlPcVdgnuMNBGbUl4D-0TIk