From 53e280c2f7f716f7136783b54da2466adbe998b6 Mon Sep 17 00:00:00 2001 From: Chris Hein Date: Wed, 4 Nov 2020 21:01:32 -0800 Subject: [PATCH] adding support for scaffolding a componentconfig type Signed-off-by: Chris Hein --- pkg/model/file/funcmap.go | 11 ++++ .../v2/scaffolds/internal/templates/main.go | 24 +------- pkg/plugin/v3/init.go | 9 ++- pkg/plugin/v3/scaffolds/init.go | 21 +++++-- .../config/kdefault/auth_proxy_patch.go | 24 ++++++-- .../config/kdefault/config_manager_patch.go | 61 +++++++++++++++++++ .../templates/config/kdefault/kustomize.go | 10 ++- .../config/manager/componentconfig.go | 57 +++++++++++++++++ .../templates/config/manager/config.go | 18 +++++- .../templates/config/manager/kustomization.go | 10 +++ .../v3/scaffolds/internal/templates/main.go | 60 +++++++++--------- 11 files changed, 234 insertions(+), 71 deletions(-) create mode 100644 pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/config_manager_patch.go create mode 100644 pkg/plugin/v3/scaffolds/internal/templates/config/manager/componentconfig.go 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..ccda6293707 100644 --- a/pkg/plugin/v3/init.go +++ b/pkg/plugin/v3/init.go @@ -40,8 +40,9 @@ type initPlugin struct { commandName string // boilerplate options - license string - owner string + license string + owner string + componentConfig bool // flags fetchDeps bool @@ -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, "componentconfig", 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..f264afc855f 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,18 @@ 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, @@ -118,11 +127,13 @@ func (s *initScaffolder) scaffold() error { &templates.DockerignoreFile{}, &kdefault.Kustomize{}, &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/config/kdefault/auth_proxy_patch.go b/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/auth_proxy_patch.go index 4126549ece8..c57a9e0a884 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 @@ -17,6 +17,7 @@ limitations under the License. package kdefault import ( + "fmt" "path/filepath" "sigs.k8s.io/kubebuilder/pkg/model/file" @@ -28,6 +29,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 @@ -36,14 +40,25 @@ func (f *AuthProxyPatch) SetTemplateDefaults() error { f.Path = filepath.Join("config", "default", "manager_auth_proxy_patch.yaml") } - f.TemplateBody = kustomizeAuthProxyPatchTemplate + args := argsFragment + if f.ComponentConfig { + args = "" + } + + f.TemplateBody = fmt.Sprintf(kustomizeAuthProxyPatchTemplate, args) f.IfExistsAction = file.Error return nil } -const kustomizeAuthProxyPatchTemplate = `# This patch inject a sidecar container which is a HTTP proxy for the +const argsFragment = `- name: manager + args: + - "--metrics-addr=127.0.0.1:8080" + - "--enable-leader-election" +` + +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 +79,5 @@ spec: ports: - containerPort: 8443 name: https - - name: manager - args: - - "--metrics-addr=127.0.0.1:8080" - - "--enable-leader-election" + %s ` 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..d314e186ae2 100644 --- a/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/kustomize.go +++ b/pkg/plugin/v3/scaffolds/internal/templates/config/kdefault/kustomize.go @@ -70,11 +70,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 +- 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..6979ad70f27 100644 --- a/pkg/plugin/v3/scaffolds/internal/templates/config/manager/config.go +++ b/pkg/plugin/v3/scaffolds/internal/templates/config/manager/config.go @@ -17,6 +17,7 @@ limitations under the License. package manager import ( + "fmt" "path/filepath" "sigs.k8s.io/kubebuilder/pkg/model/file" @@ -30,6 +31,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 @@ -38,11 +42,20 @@ func (f *Config) SetTemplateDefaults() error { f.Path = filepath.Join("config", "manager", "manager.yaml") } - f.TemplateBody = configTemplate + args := argsFragment + if f.ComponentConfig { + args = "" + } + + f.TemplateBody = fmt.Sprintf(configTemplate, args) return nil } +const argsFragment = `args: + - --enable-leader-election +` + const configTemplate = `apiVersion: v1 kind: Namespace metadata: @@ -72,8 +85,7 @@ spec: containers: - command: - /manager - args: - - --enable-leader-election + %s 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..d8bede22f13 100644 --- a/pkg/plugin/v3/scaffolds/internal/templates/config/manager/kustomization.go +++ b/pkg/plugin/v3/scaffolds/internal/templates/config/manager/kustomization.go @@ -27,6 +27,8 @@ var _ file.Template = &Kustomization{} // Kustomization scaffolds the Kustomization file in manager folder. type Kustomization struct { file.TemplateMixin + + ComponentConfig bool } // SetTemplateDefaults implements input.Template @@ -44,4 +46,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..41fa7915cd9 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 @@ -44,30 +44,27 @@ func (f *Main) SetTemplateDefaults() error { f.Path = filepath.Join(defaultMainPath) } + flags := flagsFragment + options := optionsFragment + componentConfig := "" + if f.ComponentConfig { + flags = "" + options = "" + componentConfig = ".AndFromOrDie(ctrl.ConfigFile())" + } + f.TemplateBody = fmt.Sprintf(mainTemplate, file.NewMarkerFor(f.Path, importMarker), file.NewMarkerFor(f.Path, addSchemeMarker), + flags, + options, + componentConfig, file.NewMarkerFor(f.Path, setupMarker), ) 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 @@ -113,6 +110,18 @@ const ( multiGroupControllerImportCodeFragment = `%scontrollers "%s/controllers/%s" ` addschemeCodeFragment = `utilruntime.Must(%s.AddToScheme(scheme)) +` + flagsFragment = `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.") +` + optionsFragment = `MetricsBindAddress: metricsAddr, + Port: 9443, + LeaderElection: enableLeaderElection, + LeaderElectionID: "{{ hash .Repo }}.{{ .Domain }}", ` reconcilerSetupCodeFragment = `if err = (&controllers.%sReconciler{ Client: mgr.GetClient(), @@ -226,13 +235,7 @@ func init() { } func main() { - 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.") - + %s opts := zap.Options{ Development: true, } @@ -243,11 +246,8 @@ func main() { mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: 9443, - LeaderElection: enableLeaderElection, - LeaderElectionID: "{{ hash .Repo }}.{{ .Domain }}", - }) + %s + }%s) if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1)