diff --git a/pkg/model/file/funcmap.go b/pkg/model/file/funcmap.go index 800bb9c7bc3..b974b37d0bb 100644 --- a/pkg/model/file/funcmap.go +++ b/pkg/model/file/funcmap.go @@ -17,6 +17,8 @@ limitations under the License. package file import ( + "fmt" + "hash/fnv" "strings" "text/template" ) @@ -26,5 +28,14 @@ func DefaultFuncMap() template.FuncMap { return template.FuncMap{ "title": strings.Title, "lower": strings.ToLower, + "hash": hash, } } + +func hash(s string) (string, error) { + hasher := fnv.New32a() + if _, err := hasher.Write([]byte(s)); err != nil { + return "", err + } + return fmt.Sprintf("%x", hasher.Sum(nil)), nil +} diff --git a/pkg/plugin/v2/scaffolds/internal/templates/main.go b/pkg/plugin/v2/scaffolds/internal/templates/main.go index 8283f838b77..9230115664c 100644 --- a/pkg/plugin/v2/scaffolds/internal/templates/main.go +++ b/pkg/plugin/v2/scaffolds/internal/templates/main.go @@ -18,9 +18,7 @@ package templates import ( "fmt" - "hash/fnv" "path/filepath" - "text/template" "sigs.k8s.io/kubebuilder/pkg/model/file" ) @@ -28,7 +26,6 @@ import ( const defaultMainPath = "main.go" var _ file.Template = &Main{} -var _ file.UseCustomFuncMap = &Main{} // Main scaffolds the controller manager entry point type Main struct { @@ -53,21 +50,6 @@ func (f *Main) SetTemplateDefaults() error { return nil } -func hash(s string) (string, error) { - hasher := fnv.New32a() - if _, err := hasher.Write([]byte(s)); err != nil { - return "", err - } - return fmt.Sprintf("%x", hasher.Sum(nil)), nil -} - -// GetFuncMap implements file.UseCustomFuncMap -func (f *Main) GetFuncMap() template.FuncMap { - fm := file.DefaultFuncMap() - fm["hash"] = hash - return fm -} - var _ file.Inserter = &MainUpdater{} // MainUpdater updates main.go to run Controllers @@ -236,13 +218,13 @@ func main() { "Enabling this will ensure there is only one active controller manager.") flag.Parse() - ctrl.SetLogger(zap.New(zap.UseDevMode(true))) + ctrl.SetLogger(zap.New(zap.UseDevMode(true))) mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, MetricsBindAddress: metricsAddr, - Port: 9443, - LeaderElection: enableLeaderElection, + Port: 9443, + LeaderElection: enableLeaderElection, LeaderElectionID: "{{ hash .Repo }}.{{ .Domain }}", }) if err != nil { diff --git a/pkg/plugin/v3/init.go b/pkg/plugin/v3/init.go index 42fd6791543..07761fc2bb8 100644 --- a/pkg/plugin/v3/init.go +++ b/pkg/plugin/v3/init.go @@ -46,6 +46,7 @@ type initPlugin struct { // flags fetchDeps bool skipGoVersionCheck bool + componentConfig bool } var ( @@ -87,6 +88,8 @@ func (p *initPlugin) BindFlags(fs *pflag.FlagSet) { fs.StringVar(&p.license, "license", "apache2", "license to use to boilerplate, may be one of 'apache2', 'none'") fs.StringVar(&p.owner, "owner", "", "owner to add to the copyright") + fs.BoolVar(&p.componentConfig, "component-config", false, + "create a versioned ComponentConfig file, may be 'true' or 'false'") // project args fs.StringVar(&p.config.Repo, "repo", "", "name to use for go module (e.g., github.com/user/repo), "+ @@ -142,7 +145,7 @@ func (p *initPlugin) Validate() error { } func (p *initPlugin) GetScaffolder() (scaffold.Scaffolder, error) { - return scaffolds.NewInitScaffolder(p.config, p.license, p.owner), nil + return scaffolds.NewInitScaffolder(p.config, p.license, p.owner, p.componentConfig), nil } func (p *initPlugin) PostScaffold() error { diff --git a/pkg/plugin/v3/scaffolds/init.go b/pkg/plugin/v3/scaffolds/init.go index 5403bfe7384..a7d9f72094f 100644 --- a/pkg/plugin/v3/scaffolds/init.go +++ b/pkg/plugin/v3/scaffolds/init.go @@ -37,7 +37,7 @@ import ( const ( // ControllerRuntimeVersion is the kubernetes-sigs/controller-runtime version to be used in the project - ControllerRuntimeVersion = "v0.6.3" + ControllerRuntimeVersion = "v0.7.0-alpha.6" // ControllerToolsVersion is the kubernetes-sigs/controller-tools version to be used in the project ControllerToolsVersion = "v0.3.0" // KustomizeVersion is the kubernetes-sigs/kustomize version to be used in the project @@ -53,15 +53,17 @@ type initScaffolder struct { boilerplatePath string license string owner string + componentConfig bool } // NewInitScaffolder returns a new Scaffolder for project initialization operations -func NewInitScaffolder(config *config.Config, license, owner string) scaffold.Scaffolder { +func NewInitScaffolder(config *config.Config, license, owner string, componentConfig bool) scaffold.Scaffolder { return &initScaffolder{ config: config, boilerplatePath: filepath.Join("hack", "boilerplate.go.txt"), license: license, owner: owner, + componentConfig: componentConfig, } } @@ -101,11 +103,14 @@ func (s *initScaffolder) scaffold() error { &templates.GitIgnore{}, &rbac.AuthProxyRole{}, &rbac.AuthProxyRoleBinding{}, - &kdefault.AuthProxyPatch{}, + &kdefault.AuthProxyPatch{ComponentConfig: s.componentConfig}, &rbac.AuthProxyService{}, &rbac.ClientClusterRole{}, - &manager.Config{Image: imageName}, - &templates.Main{}, + &manager.Config{ + Image: imageName, + ComponentConfig: s.componentConfig, + }, + &templates.Main{ComponentConfig: s.componentConfig}, &templates.GoMod{ControllerRuntimeVersion: ControllerRuntimeVersion}, &templates.Makefile{ Image: imageName, @@ -115,14 +120,17 @@ func (s *initScaffolder) scaffold() error { ControllerRuntimeVersion: ControllerRuntimeVersion, }, &templates.Dockerfile{}, + &templates.ComponentConfig{}, &templates.DockerignoreFile{}, - &kdefault.Kustomize{}, + &kdefault.Kustomize{ComponentConfig: s.componentConfig}, &kdefault.ManagerWebhookPatch{}, + &kdefault.ManagerConfigPatch{}, &rbac.ManagerRoleBinding{}, &rbac.LeaderElectionRole{}, &rbac.LeaderElectionRoleBinding{}, &rbac.KustomizeRBAC{}, &manager.Kustomization{}, + &manager.ComponentConfig{}, &webhook.Kustomization{}, &webhook.KustomizeConfigWebhook{}, &webhook.Service{}, diff --git a/pkg/plugin/v3/scaffolds/internal/templates/componentconfig.go b/pkg/plugin/v3/scaffolds/internal/templates/componentconfig.go new file mode 100644 index 00000000000..64439bb795a --- /dev/null +++ b/pkg/plugin/v3/scaffolds/internal/templates/componentconfig.go @@ -0,0 +1,55 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package templates + +import ( + "sigs.k8s.io/kubebuilder/pkg/model/file" +) + +var _ file.Template = &ComponentConfig{} + +// ComponentConfig scaffolds the ComponentConfig file in manager folder. +type ComponentConfig struct { + file.TemplateMixin + file.DomainMixin + file.RepositoryMixin +} + +// SetTemplateDefaults implements input.Template +func (f *ComponentConfig) SetTemplateDefaults() error { + if f.Path == "" { + f.Path = "config.yaml" + } + + f.TemplateBody = componentConfigTemplate + + f.IfExistsAction = file.Error + + return nil +} + +const componentConfigTemplate = `apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 +kind: ControllerManagerConfiguration +metrics: + bindAddress: :8080 +webhook: + port: 9443 +leaderElection: + leaderElect: false + resourceLock: configmaps + resourceName: {{ hash .Repo }}.{{ .Domain }}", +` diff --git a/pkg/plugin/v3/scaffolds/internal/templates/config/controller/controller.go b/pkg/plugin/v3/scaffolds/internal/templates/config/controller/controller.go index c2dd25bc423..1e6d3868429 100644 --- a/pkg/plugin/v3/scaffolds/internal/templates/config/controller/controller.go +++ b/pkg/plugin/v3/scaffolds/internal/templates/config/controller/controller.go @@ -92,7 +92,7 @@ type {{ .Resource.Kind }}Reconciler struct { // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@{{ .ControllerRuntimeVersion }}/pkg/reconcile -func (r *{{ .Resource.Kind }}Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { +func (r *{{ .Resource.Kind }}Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = context.Background() _ = r.Log.WithValues("{{ .Resource.Kind | lower }}", req.NamespacedName) diff --git a/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go b/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go index 4126549ece8..384f517275a 100644 --- a/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go +++ b/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go @@ -28,6 +28,9 @@ var _ file.Template = &AuthProxyPatch{} // prometheus metrics for manager Pod. type AuthProxyPatch struct { file.TemplateMixin + + // ComponentConfig defines that this should be configured with a file + ComponentConfig bool } // SetTemplateDefaults implements input.Template @@ -43,7 +46,7 @@ func (f *AuthProxyPatch) SetTemplateDefaults() error { return nil } -const kustomizeAuthProxyPatchTemplate = `# This patch inject a sidecar container which is a HTTP proxy for the +const kustomizeAuthProxyPatchTemplate = `# This patch inject a sidecar container which is a HTTP proxy for the # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. apiVersion: apps/v1 kind: Deployment @@ -64,8 +67,10 @@ spec: ports: - containerPort: 8443 name: https - - name: manager + {{- if not .ComponentConfig }} + - name: manager args: - "--metrics-addr=127.0.0.1:8080" - "--enable-leader-election" + {{- end }} ` diff --git a/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/config_manager_patch.go b/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/config_manager_patch.go new file mode 100644 index 00000000000..3da2b7c3f65 --- /dev/null +++ b/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/config_manager_patch.go @@ -0,0 +1,61 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kdefault + +import ( + "path/filepath" + + "sigs.k8s.io/kubebuilder/pkg/model/file" +) + +var _ file.Template = &ManagerConfigPatch{} + +// ManagerConfigPatch scaffolds a ManagerConfigPatch for a Resource +type ManagerConfigPatch struct { + file.TemplateMixin +} + +// SetTemplateDefaults implements input.Template +func (f *ManagerConfigPatch) SetTemplateDefaults() error { + if f.Path == "" { + f.Path = filepath.Join("config", "default", "manager_config_patch.yaml") + } + + f.TemplateBody = managerConfigPatchTemplate + + return nil +} + +const managerConfigPatchTemplate = `apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + volumeMounts: + - name: config + mountPath: /config.yaml + subPath: config.yaml + volumes: + - name: config + configMap: + name: controller-config +` diff --git a/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/kustomize.go b/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/kustomize.go index b40d18444c6..b9e30fe46f3 100644 --- a/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/kustomize.go +++ b/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/kustomize.go @@ -28,6 +28,8 @@ var _ file.Template = &Kustomize{} type Kustomize struct { file.TemplateMixin file.ProjectNameMixin + + ComponentConfig bool } // SetTemplateDefaults implements input.Template @@ -70,11 +72,15 @@ bases: #- ../prometheus patchesStrategicMerge: - # Protect the /metrics endpoint by putting it behind auth. - # If you want your controller-manager to expose the /metrics - # endpoint w/o any authn/z, please comment the following line. +# Protect the /metrics endpoint by putting it behind auth. +# If you want your controller-manager to expose the /metrics +# endpoint w/o any authn/z, please comment the following line. - manager_auth_proxy_patch.yaml +# Mount the controller config file for loading manager configurations +# through a ComponentConfig type +{{ if .ComponentConfig }}#{{ end }}- manager_config_patch.yaml + # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml #- manager_webhook_patch.yaml diff --git a/pkg/plugin/v3/scaffolds/internal/templates/config/manager/componentconfig.go b/pkg/plugin/v3/scaffolds/internal/templates/config/manager/componentconfig.go new file mode 100644 index 00000000000..ea43e311489 --- /dev/null +++ b/pkg/plugin/v3/scaffolds/internal/templates/config/manager/componentconfig.go @@ -0,0 +1,57 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package manager + +import ( + "path/filepath" + + "sigs.k8s.io/kubebuilder/pkg/model/file" +) + +var _ file.Template = &ComponentConfig{} + +// ComponentConfig scaffolds the ComponentConfig file in manager folder. +type ComponentConfig struct { + file.TemplateMixin + file.DomainMixin + file.RepositoryMixin +} + +// SetTemplateDefaults implements input.Template +func (f *ComponentConfig) SetTemplateDefaults() error { + if f.Path == "" { + f.Path = filepath.Join("config", "manager", "config.yaml") + } + + f.TemplateBody = componentConfigTemplate + + f.IfExistsAction = file.Error + + return nil +} + +const componentConfigTemplate = `apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 +kind: ControllerManagerConfiguration +metrics: + bindAddress: :8080 +webhook: + port: 9443 +leaderElection: + leaderElect: true + resourceLock: configmaps + resourceName: {{ hash .Repo }}.{{ .Domain }}", +` diff --git a/pkg/plugin/v3/scaffolds/internal/templates/config/manager/config.go b/pkg/plugin/v3/scaffolds/internal/templates/config/manager/config.go index 3218812be68..89c31236c8a 100644 --- a/pkg/plugin/v3/scaffolds/internal/templates/config/manager/config.go +++ b/pkg/plugin/v3/scaffolds/internal/templates/config/manager/config.go @@ -30,6 +30,9 @@ type Config struct { // Image is controller manager image name Image string + + // ComponentConfig defines that this should be configured with a file + ComponentConfig bool } // SetTemplateDefaults implements input.Template @@ -72,8 +75,10 @@ spec: containers: - command: - /manager + {{- if not .ComponentConfig }} args: - --enable-leader-election + {{- end }} image: {{ .Image }} name: manager securityContext: diff --git a/pkg/plugin/v3/scaffolds/internal/templates/config/manager/kustomization.go b/pkg/plugin/v3/scaffolds/internal/templates/config/manager/kustomization.go index 4016d302ad8..aa24219f2d2 100644 --- a/pkg/plugin/v3/scaffolds/internal/templates/config/manager/kustomization.go +++ b/pkg/plugin/v3/scaffolds/internal/templates/config/manager/kustomization.go @@ -44,4 +44,12 @@ func (f *Kustomization) SetTemplateDefaults() error { const kustomizeManagerTemplate = `resources: - manager.yaml + +generatorOptions: + disableNameSuffixHash: true + +configMapGenerator: +- name: controller-config + files: + - config.yaml ` diff --git a/pkg/plugin/v3/scaffolds/internal/templates/main.go b/pkg/plugin/v3/scaffolds/internal/templates/main.go index afd6728b341..b33d3bbc360 100644 --- a/pkg/plugin/v3/scaffolds/internal/templates/main.go +++ b/pkg/plugin/v3/scaffolds/internal/templates/main.go @@ -18,9 +18,7 @@ package templates import ( "fmt" - "hash/fnv" "path/filepath" - "text/template" "sigs.k8s.io/kubebuilder/pkg/model/file" ) @@ -28,7 +26,6 @@ import ( const defaultMainPath = "main.go" var _ file.Template = &Main{} -var _ file.UseCustomFuncMap = &Main{} // Main scaffolds the controller manager entry point type Main struct { @@ -36,6 +33,9 @@ type Main struct { file.BoilerplateMixin file.DomainMixin file.RepositoryMixin + + // ComponentConfig is set if to enable ComponentConfig + ComponentConfig bool } // SetTemplateDefaults implements file.Template @@ -53,21 +53,6 @@ func (f *Main) SetTemplateDefaults() error { return nil } -func hash(s string) (string, error) { - hasher := fnv.New32a() - if _, err := hasher.Write([]byte(s)); err != nil { - return "", err - } - return fmt.Sprintf("%x", hasher.Sum(nil)), nil -} - -// GetFuncMap implements file.UseCustomFuncMap -func (f *Main) GetFuncMap() template.FuncMap { - fm := file.DefaultFuncMap() - fm["hash"] = hash - return fm -} - var _ file.Inserter = &MainUpdater{} // MainUpdater updates main.go to run Controllers @@ -226,13 +211,14 @@ func init() { } func main() { + {{ if not .ComponentConfig }} var metricsAddr string var enableLeaderElection bool flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, "Enable leader election for controller manager. " + "Enabling this will ensure there is only one active controller manager.") - + {{ end }} opts := zap.Options{ Development: true, } @@ -241,13 +227,24 @@ func main() { ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + {{- if not .ComponentConfig }} mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, MetricsBindAddress: metricsAddr, - Port: 9443, - LeaderElection: enableLeaderElection, + Port: 9443, + LeaderElection: enableLeaderElection, LeaderElectionID: "{{ hash .Repo }}.{{ .Domain }}", }) + {{- else }} + options, err := ctrl.Options{Scheme: scheme}.AndFrom(ctrl.ConfigFile()) + if err != nil { + setupLog.Error(err, "unable to load the config file") + os.Exit(1) + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options) + {{- end }} if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1)