Skip to content

Commit

Permalink
feat: add plugin v3 to fix controller typo alias and improve its log…
Browse files Browse the repository at this point in the history
…s (only V3)
  • Loading branch information
Camila Macedo committed Jun 5, 2020
1 parent 6e0d3d4 commit 99ed73b
Show file tree
Hide file tree
Showing 62 changed files with 1,061 additions and 77 deletions.
3 changes: 3 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@ import (
"sigs.k8s.io/kubebuilder/cmd/version"
"sigs.k8s.io/kubebuilder/pkg/cli"
pluginv2 "sigs.k8s.io/kubebuilder/pkg/plugin/v2"
pluginv3 "sigs.k8s.io/kubebuilder/pkg/plugin/v3"
)

func main() {
c, err := cli.New(
cli.WithPlugins(
&pluginv2.Plugin{},
&pluginv3.Plugin{},
),
cli.WithDefaultPlugins(
&pluginv2.Plugin{},
&pluginv3.Plugin{},
),
cli.WithExtraCommands(
newEditCmd(),
Expand Down
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
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module sigs.k8s.io/kubebuilder
go 1.13

require (
github.com/blang/semver v3.5.1+incompatible
github.com/gobuffalo/flect v0.2.1
github.com/onsi/ginkgo v1.12.0
github.com/onsi/gomega v1.9.0
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
Expand Down
29 changes: 0 additions & 29 deletions internal/config/config_suite_test.go

This file was deleted.

2 changes: 1 addition & 1 deletion pkg/plugin/v2/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
const pluginName = "go" + plugin.DefaultNameQualifier

var (
supportedProjectVersions = []string{config.Version2, config.Version3Alpha}
supportedProjectVersions = []string{config.Version2}
pluginVersion = plugin.Version{Number: 2}
)

Expand Down
8 changes: 4 additions & 4 deletions pkg/plugin/v2/scaffolds/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import (
"sigs.k8s.io/kubebuilder/pkg/model/resource"
"sigs.k8s.io/kubebuilder/pkg/plugin/internal/machinery"
"sigs.k8s.io/kubebuilder/pkg/plugin/scaffold"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates/controller"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates/crd"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates/controller"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates/crd"
)

// (used only to gen api with --pattern=addon)
Expand Down Expand Up @@ -72,7 +72,7 @@ func (s *apiScaffolder) Scaffold() error {
fmt.Println("Writing scaffold for you to edit...")

switch {
case s.config.IsV2(), s.config.IsV3():
case s.config.IsV2():
return s.scaffold()
default:
return fmt.Errorf("unknown project version %v", s.config.Version)
Expand Down
12 changes: 6 additions & 6 deletions pkg/plugin/v2/scaffolds/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ import (
"sigs.k8s.io/kubebuilder/pkg/model/config"
"sigs.k8s.io/kubebuilder/pkg/plugin/internal/machinery"
"sigs.k8s.io/kubebuilder/pkg/plugin/scaffold"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates/certmanager"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates/manager"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates/metricsauth"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates/prometheus"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates/webhook"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates/certmanager"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates/manager"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates/metricsauth"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates/prometheus"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates/webhook"
)

const (
Expand Down
File renamed without changes.
File renamed without changes.
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 All @@ -124,9 +121,6 @@ const (
os.Exit(1)
}
`
// TODO(v3): loggers for the same Kind controllers from different groups use the same logger.
// `.WithName("controllers").WithName(GROUP).WithName(KIND)` should be used instead. However,
// this is a backwards incompatible change, and thus should be done for next project version.
multiGroupReconcilerSetupCodeFragment = `if err = (&%scontroller.%sReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("%s"),
Expand Down
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions pkg/plugin/v2/scaffolds/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import (
"sigs.k8s.io/kubebuilder/pkg/model/resource"
"sigs.k8s.io/kubebuilder/pkg/plugin/internal/machinery"
"sigs.k8s.io/kubebuilder/pkg/plugin/scaffold"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/internal/templates/webhook"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates"
"sigs.k8s.io/kubebuilder/pkg/plugin/v2/scaffolds/templates/webhook"
)

var _ scaffold.Scaffolder = &webhookScaffolder{}
Expand Down Expand Up @@ -63,7 +63,7 @@ func (s *webhookScaffolder) Scaffold() error {
fmt.Println("Writing scaffold for you to edit...")

switch {
case s.config.IsV2(), s.config.IsV3():
case s.config.IsV2():
return s.scaffold()
default:
return fmt.Errorf("unknown project version %v", s.config.Version)
Expand Down
204 changes: 204 additions & 0 deletions pkg/plugin/v3/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/*
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 v3

import (
"bufio"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/spf13/pflag"

"sigs.k8s.io/kubebuilder/internal/cmdutil"
"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/plugin"
"sigs.k8s.io/kubebuilder/pkg/plugin/internal/util"
"sigs.k8s.io/kubebuilder/pkg/plugin/scaffold"
scaffoldsv3 "sigs.k8s.io/kubebuilder/pkg/plugin/v3/scaffolds"
"sigs.k8s.io/kubebuilder/plugins/addon"
)

type createAPIPlugin struct {
config *config.Config

// pattern indicates that we should use a plugin to build according to a pattern
pattern string

resource *resource.Options

// Check if we have to scaffold resource and/or controller
resourceFlag *pflag.Flag
controllerFlag *pflag.Flag
doResource bool
doController bool

// force indicates that the resource should be created even if it already exists
force bool

// runMake indicates whether to run make or not after scaffolding APIs
runMake bool
}

var (
_ plugin.CreateAPI = &createAPIPlugin{}
_ cmdutil.RunOptions = &createAPIPlugin{}
)

func (p createAPIPlugin) UpdateContext(ctx *plugin.Context) {
ctx.Description = `Scaffold a Kubernetes API by creating a Resource definition and / or a Controller.
create resource will prompt the user for if it should scaffold the Resource and / or Controller. To only
scaffold a Controller for an existing Resource, select "n" for Resource. To only define
the schema for a Resource without writing a Controller, select "n" for Controller.
After the scaffold is written, api will run make on the project.
`
ctx.Examples = fmt.Sprintf(` # Create a frigates API with Group: ship, Version: v1beta1 and Kind: Frigate
%s create api --group ship --version v1beta1 --kind Frigate
# Edit the API Scheme
nano api/v1beta1/frigate_types.go
# Edit the Controller
nano controllers/frigate/frigate_controller.go
# Edit the Controller Test
nano controllers/frigate/frigate_controller_test.go
# Install CRDs into the Kubernetes cluster using kubectl apply
make install
# Regenerate code and run against the Kubernetes cluster configured by ~/.kube/config
make run
`,
ctx.CommandName)
}

func (p *createAPIPlugin) BindFlags(fs *pflag.FlagSet) {
fs.BoolVar(&p.runMake, "make", true, "if true, run make after generating files")

fs.BoolVar(&p.doResource, "resource", true,
"if set, generate the resource without prompting the user")
p.resourceFlag = fs.Lookup("resource")
fs.BoolVar(&p.doController, "controller", true,
"if set, generate the controller without prompting the user")
p.controllerFlag = fs.Lookup("controller")

if os.Getenv("KUBEBUILDER_ENABLE_PLUGINS") != "" {
fs.StringVar(&p.pattern, "pattern", "",
"generates an API following an extension pattern (addon)")
}

fs.BoolVar(&p.force, "force", false,
"attempt to create resource even if it already exists")
p.resource = &resource.Options{}
fs.StringVar(&p.resource.Kind, "kind", "", "resource Kind")
fs.StringVar(&p.resource.Group, "group", "", "resource Group")
fs.StringVar(&p.resource.Version, "version", "", "resource Version")
fs.BoolVar(&p.resource.Namespaced, "namespaced", true, "resource is namespaced")
}

func (p *createAPIPlugin) InjectConfig(c *config.Config) {
p.config = c
}

func (p *createAPIPlugin) Run() error {
return cmdutil.Run(p)
}

func (p *createAPIPlugin) Validate() error {
if err := p.resource.Validate(); err != nil {
return err
}

reader := bufio.NewReader(os.Stdin)
if !p.resourceFlag.Changed {
fmt.Println("Create Resource [y/n]")
p.doResource = util.YesNo(reader)
}
if !p.controllerFlag.Changed {
fmt.Println("Create Controller [y/n]")
p.doController = util.YesNo(reader)
}

// In case we want to scaffold a resource API we need to do some checks
if p.doResource {
// Check that resource doesn't exist or flag force was set
if !p.force && p.config.HasResource(p.resource.GVK()) {
return errors.New("API resource already exists")
}

// Check that the provided group can be added to the project
if !p.config.MultiGroup && len(p.config.Resources) != 0 && !p.config.HasGroup(p.resource.Group) {
return fmt.Errorf("multiple groups are not allowed by default, to enable multi-group visit %s",
"kubebuilder.io/migration/multi-group.html")
}
}

return nil
}

func (p *createAPIPlugin) GetScaffolder() (scaffold.Scaffolder, error) {
// Load the boilerplate
bp, err := ioutil.ReadFile(filepath.Join("hack", "boilerplate.go.txt")) // nolint:gosec
if err != nil {
return nil, fmt.Errorf("unable to load boilerplate: %v", err)
}

// Load the requested plugins
plugins := make([]model.Plugin, 0)
switch strings.ToLower(p.pattern) {
case "":
// Default pattern
case "addon":
plugins = append(plugins, &addon.Plugin{})
default:
return nil, fmt.Errorf("unknown pattern %q", p.pattern)
}

// Create the actual resource from the resource options
res := p.resource.NewResource(p.config, p.doResource)
return scaffoldsv3.NewAPIScaffolder(p.config, string(bp), res, p.doResource, p.doController, plugins), nil
}

func (p *createAPIPlugin) PostScaffold() error {
// Load the requested plugins
switch strings.ToLower(p.pattern) {
case "":
// Default pattern
case "addon":
// Ensure that we are pinning sigs.k8s.io/kubebuilder-declarative-pattern version
err := util.RunCmd("Get controller runtime", "go", "get",
"sigs.k8s.io/kubebuilder-declarative-pattern@"+scaffoldsv3.KbDeclarativePattern)
if err != nil {
return err
}
default:
return fmt.Errorf("unknown pattern %q", p.pattern)
}

if p.runMake {
return util.RunCmd("Running make", "make")
}
return nil
}
Loading

0 comments on commit 99ed73b

Please sign in to comment.