Skip to content

Commit

Permalink
fix: typo controller import alias
Browse files Browse the repository at this point in the history
  • Loading branch information
Camila Macedo committed May 14, 2020
1 parent f07a014 commit 399c53c
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 39 deletions.
2 changes: 1 addition & 1 deletion docs/book/src/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ kubebuilder create api --group webapp --version v1 --kind Guestbook
<h1>Press Options</h1>

If you press `y` for Create Resource [y/n] and for Create Controller [y/n] then this will create the files `api/v1/guestbook_types.go` where the API is defined
and the `controller/guestbook_controller.go` where the reconciliation business logic is implemented for this Kind(CRD).
and the `controllers/guestbook_controller.go` where the reconciliation business logic is implemented for this Kind(CRD).

</aside>

Expand Down
4 changes: 2 additions & 2 deletions docs/testing/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

This article explores steps to write and run integration tests for controllers created using Kubebuilder. Kubebuilder provides a template for writing integration tests. You can simply run all integration (and unit) tests within the project by running: `make test`

For example, there is a controller watches *Parent* objects. The *Parent* objects create *Child* objects. Note that the *Child* objects must have their `.ownerReferences` field setting to the `Parent` objects. You can find the template under `pkg/controller/parent/parent_controller_test.go`:
For example, there is a controller watches *Parent* objects. The *Parent* objects create *Child* objects. Note that the *Child* objects must have their `.ownerReferences` field setting to the `Parent` objects. You can find the template under `controllers/parent/parent_controller_test.go`:
```
package parent
Expand Down Expand Up @@ -79,4 +79,4 @@ func TestReconcile(t *testing.T) {

The manager is started as part of the test itself (`StartTestManager` function).

Both functions are located in `pkg/controller/parent/parent_controller_suite_test.go` file. The file also contains a `TestMain` function that allows you to specify CRD directory paths for the testing environment.
Both functions are located in `controllers/parent/parent_controller_suite_test.go` file. The file also contains a `TestMain` function that allows you to specify CRD directory paths for the testing environment.
2 changes: 1 addition & 1 deletion docs/using_an_external_type.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ dep ensure --add

Edit the `CRDDirectoryPaths` in your test suite by appending the path to their CRDs:

file pkg/controller/my_kind_controller_suite_test.go
file pkg/controllers/my_kind_controller_suite_test.go
```
var cfg *rest.Config
Expand Down
20 changes: 15 additions & 5 deletions pkg/scaffold/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"sigs.k8s.io/kubebuilder/pkg/model/resource"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/machinery"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates"
templatesv3 "sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates/v3"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates/controller"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates/crd"
)
Expand Down Expand Up @@ -119,11 +120,20 @@ func (s *apiScaffolder) scaffold() error {
}
}

if err := machinery.NewScaffold(s.plugins...).Execute(
s.newUniverse(),
&templates.MainUpdater{WireResource: s.doResource, WireController: s.doController},
); err != nil {
return fmt.Errorf("error updating main.go: %v", err)
if s.config.IsV2() {
if err := machinery.NewScaffold(s.plugins...).Execute(
s.newUniverse(),
&templates.MainUpdater{WireResource: s.doResource, WireController: s.doController},
); err != nil {
return fmt.Errorf("error updating main.go: %v", err)
}
} else {
if err := machinery.NewScaffold(s.plugins...).Execute(
s.newUniverse(),
&templatesv3.MainUpdater{WireResource: s.doResource, WireController: s.doController},
); err != nil {
return fmt.Errorf("error updating main.go: %v", err)
}
}

return nil
Expand Down
3 changes: 0 additions & 3 deletions pkg/scaffold/internal/templates/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,6 @@ const (
`
controllerImportCodeFragment = `"%s/controllers"
`
// TODO(v3): `&%scontrollers` should be used instead of `&%scontroller` as there may be multiple
// controller for different Kinds in the same group. However, this is a backwards incompatible
// change, and thus should be done for next project version.
multiGroupControllerImportCodeFragment = `%scontroller "%s/controllers/%s"
`
addschemeCodeFragment = `utilruntime.Must(%s.AddToScheme(scheme))
Expand Down
254 changes: 254 additions & 0 deletions pkg/scaffold/internal/templates/v3/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/*
Copyright 2019 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 v3

import (
"fmt"
"hash/fnv"
"path/filepath"
"text/template"

"sigs.k8s.io/kubebuilder/pkg/model/file"
)

const defaultMainPath = "main.go"

var _ file.Template = &Main{}
var _ file.UseCustomFuncMap = &Main{}

// Main scaffolds the controller manager entry point
type Main struct {
file.TemplateMixin
file.BoilerplateMixin
file.DomainMixin
file.RepositoryMixin
}

// SetTemplateDefaults implements file.Template
func (f *Main) SetTemplateDefaults() error {
if f.Path == "" {
f.Path = filepath.Join(defaultMainPath)
}

f.TemplateBody = fmt.Sprintf(mainTemplate,
file.NewMarkerFor(f.Path, importMarker),
file.NewMarkerFor(f.Path, addSchemeMarker),
file.NewMarkerFor(f.Path, setupMarker),
)

return nil
}

func hash(s string) (string, error) {
hasher := fnv.New32a()
hasher.Write([]byte(s)) // nolint:errcheck
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
type MainUpdater struct { //nolint:maligned
file.RepositoryMixin
file.MultiGroupMixin
file.ResourceMixin

// Flags to indicate which parts need to be included when updating the file
WireResource, WireController, WireWebhook bool
}

// GetPath implements Builder
func (*MainUpdater) GetPath() string {
return defaultMainPath
}

// GetIfExistsAction implements Builder
func (*MainUpdater) GetIfExistsAction() file.IfExistsAction {
return file.Overwrite
}

const (
importMarker = "imports"
addSchemeMarker = "scheme"
setupMarker = "builder"
)

// GetMarkers implements file.Inserter
func (f *MainUpdater) GetMarkers() []file.Marker {
return []file.Marker{
file.NewMarkerFor(defaultMainPath, importMarker),
file.NewMarkerFor(defaultMainPath, addSchemeMarker),
file.NewMarkerFor(defaultMainPath, setupMarker),
}
}

const (
apiImportCodeFragment = `%s "%s"
`
controllerImportCodeFragment = `"%s/controllers"
`

multiGroupControllerImportCodeFragment = `%scontrollers "%s/controllers/%s"
`
addschemeCodeFragment = `utilruntime.Must(%s.AddToScheme(scheme))
`
reconcilerSetupCodeFragment = `if err = (&controllers.%sReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("%s"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "%s")
os.Exit(1)
}
`
multiGroupReconcilerSetupCodeFragment = `if err = (&%scontrollers.%sReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("%s").WithName("%s"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "%s")
os.Exit(1)
}
`
webhookSetupCodeFragment = `if err = (&%s.%s{}).SetupWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "%s")
os.Exit(1)
}
`
)

// GetCodeFragments implements file.Inserter
func (f *MainUpdater) GetCodeFragments() file.CodeFragmentsMap {
fragments := make(file.CodeFragmentsMap, 3)

// If resource is not being provided we are creating the file, not updating it
if f.Resource == nil {
return fragments
}

// Generate import code fragments
imports := make([]string, 0)
imports = append(imports, fmt.Sprintf(apiImportCodeFragment, f.Resource.ImportAlias, f.Resource.Package))
if f.WireController {
if !f.MultiGroup {
imports = append(imports, fmt.Sprintf(controllerImportCodeFragment, f.Repo))
} else {
imports = append(imports, fmt.Sprintf(multiGroupControllerImportCodeFragment,
f.Resource.GroupPackageName, f.Repo, f.Resource.Group))
}
}

// Generate add scheme code fragments
addScheme := make([]string, 0)
addScheme = append(addScheme, fmt.Sprintf(addschemeCodeFragment, f.Resource.ImportAlias))

// Generate setup code fragments
setup := make([]string, 0)
if f.WireController {
if !f.MultiGroup {
setup = append(setup, fmt.Sprintf(reconcilerSetupCodeFragment,
f.Resource.Kind, f.Resource.Kind, f.Resource.Kind))
} else {
setup = append(setup, fmt.Sprintf(multiGroupReconcilerSetupCodeFragment,
f.Resource.GroupPackageName, f.Resource.Kind, f.Resource.Group, f.Resource.Kind, f.Resource.Kind))
}
}
if f.WireWebhook {
setup = append(setup, fmt.Sprintf(webhookSetupCodeFragment,
f.Resource.ImportAlias, f.Resource.Kind, f.Resource.Kind))
}

// Only store code fragments in the map if the slices are non-empty
if len(imports) != 0 {
fragments[file.NewMarkerFor(defaultMainPath, importMarker)] = imports
}
if len(addScheme) != 0 {
fragments[file.NewMarkerFor(defaultMainPath, addSchemeMarker)] = addScheme
}
if len(setup) != 0 {
fragments[file.NewMarkerFor(defaultMainPath, setupMarker)] = setup
}

return fragments
}

var mainTemplate = `{{ .Boilerplate }}
package main
import (
"flag"
"os"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
%s
)
var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
%s
}
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.")
flag.Parse()
ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
LeaderElection: enableLeaderElection,
LeaderElectionID: "{{ hash .Repo }}.{{ .Domain }}",
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
%s
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}
`
30 changes: 21 additions & 9 deletions pkg/scaffold/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ package scaffold

import (
"fmt"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/machinery"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates/webhook"
templatesv3 "sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates/v3"

"sigs.k8s.io/kubebuilder/pkg/model"
"sigs.k8s.io/kubebuilder/pkg/model/config"
"sigs.k8s.io/kubebuilder/pkg/model/resource"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/machinery"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates"
"sigs.k8s.io/kubebuilder/pkg/scaffold/internal/templates/webhook"
)

var _ Scaffolder = &webhookScaffolder{}
Expand Down Expand Up @@ -83,13 +84,24 @@ func (s *webhookScaffolder) scaffold() error {
You need to implement the conversion.Hub and conversion.Convertible interfaces for your CRD types.`)
}

if err := machinery.NewScaffold().Execute(
s.newUniverse(),
&webhook.Webhook{Defaulting: s.defaulting, Validating: s.validation},
&templates.MainUpdater{WireWebhook: true},
); err != nil {
return err
if s.config.IsV2() {
if err := machinery.NewScaffold().Execute(
s.newUniverse(),
&webhook.Webhook{Defaulting: s.defaulting, Validating: s.validation},
&templates.MainUpdater{WireWebhook: true},
); err != nil {
return err
}
} else {
if err := machinery.NewScaffold().Execute(
s.newUniverse(),
&webhook.Webhook{Defaulting: s.defaulting, Validating: s.validation},
&templatesv3.MainUpdater{WireWebhook: true},
); err != nil {
return err
}
}


return nil
}
Loading

0 comments on commit 399c53c

Please sign in to comment.