Skip to content

Commit 3eba593

Browse files
committed
pkg/plugin/v3: add plugin 3-alpha
testdata: modify project-v3.* testdata to use go/v3-alpha plugin, since project-v2.* already tests plugin go/v2 generate_testdata.sh: add --plugin option
1 parent 448b384 commit 3eba593

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+4147
-9
lines changed

cmd/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ import (
2222
"sigs.k8s.io/kubebuilder/cmd/version"
2323
"sigs.k8s.io/kubebuilder/pkg/cli"
2424
pluginv2 "sigs.k8s.io/kubebuilder/pkg/plugin/v2"
25+
pluginv3 "sigs.k8s.io/kubebuilder/pkg/plugin/v3"
2526
)
2627

2728
func main() {
2829
c, err := cli.New(
2930
cli.WithPlugins(
3031
&pluginv2.Plugin{},
32+
&pluginv3.Plugin{},
3133
),
3234
cli.WithDefaultPlugins(
3335
&pluginv2.Plugin{},

generate_testdata.sh

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,26 @@ build_kb() {
2828
# project-version.
2929
#
3030
scaffold_test_project() {
31-
project=$1
32-
version=$2
31+
local project=$1
32+
local version=$2
33+
local plugin=${3:-""}
34+
3335
testdata_dir=$(pwd)/testdata
3436
mkdir -p ./testdata/$project
3537
rm -rf ./testdata/$project/*
3638
pushd .
3739
cd testdata/$project
3840
kb=$testdata_dir/../bin/kubebuilder
3941
oldgopath=$GOPATH
42+
# Set "--plugins $plugin" if $plugin is not null.
43+
local plugin_flag="${plugin:+--plugins $plugin}"
44+
4045
if [ $version == "2" ] || [ $version == "3-alpha" ]; then
46+
if [ $version == "2" ] && [ -n "$plugin_flag" ]; then
47+
echo "--plugins flag may not be set for project versions less than 3"
48+
exit 1
49+
fi
50+
4151
header_text "Starting to generate projects with version $version"
4252
header_text "Generating $project"
4353

@@ -46,7 +56,7 @@ scaffold_test_project() {
4656
go mod init sigs.k8s.io/kubebuilder/testdata/$project # our repo autodetection will traverse up to the kb module if we don't do this
4757

4858
header_text "initializing $project ..."
49-
$kb init --project-version $version --domain testproject.org --license apache2 --owner "The Kubernetes authors"
59+
$kb init $plugin_flag --project-version $version --domain testproject.org --license apache2 --owner "The Kubernetes authors"
5060

5161
if [ $project == "project-v2" ] || [ $project == "project-v3" ]; then
5262
header_text 'Creating APIs ...'
@@ -91,6 +101,6 @@ build_kb
91101
scaffold_test_project project-v2 2
92102
scaffold_test_project project-v2-multigroup 2
93103
scaffold_test_project project-v2-addon 2
94-
scaffold_test_project project-v3 3-alpha
95-
scaffold_test_project project-v3-multigroup 3-alpha
96-
scaffold_test_project project-v3-addon 3-alpha
104+
scaffold_test_project project-v3 3-alpha go/v3-alpha
105+
scaffold_test_project project-v3-multigroup 3-alpha go/v3-alpha
106+
scaffold_test_project project-v3-addon 3-alpha go/v3-alpha

pkg/plugin/v3/api.go

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v3
18+
19+
import (
20+
"bufio"
21+
"errors"
22+
"fmt"
23+
"io/ioutil"
24+
"os"
25+
"path/filepath"
26+
"strings"
27+
28+
"github.com/spf13/pflag"
29+
30+
"sigs.k8s.io/kubebuilder/internal/cmdutil"
31+
"sigs.k8s.io/kubebuilder/pkg/model"
32+
"sigs.k8s.io/kubebuilder/pkg/model/config"
33+
"sigs.k8s.io/kubebuilder/pkg/model/resource"
34+
"sigs.k8s.io/kubebuilder/pkg/plugin"
35+
"sigs.k8s.io/kubebuilder/pkg/plugin/internal/util"
36+
"sigs.k8s.io/kubebuilder/pkg/plugin/scaffold"
37+
"sigs.k8s.io/kubebuilder/pkg/plugin/v3/scaffolds"
38+
"sigs.k8s.io/kubebuilder/plugins/addon"
39+
)
40+
41+
// (used only to gen api with --pattern=addon)
42+
// KbDeclarativePatternVersion is the sigs.k8s.io/kubebuilder-declarative-pattern version
43+
// TODO: remove this when a better solution for using addons is implemented.
44+
const KbDeclarativePatternVersion = "v0.0.0-20200522144838-848d48e5b073"
45+
46+
type createAPIPlugin struct {
47+
config *config.Config
48+
49+
// pattern indicates that we should use a plugin to build according to a pattern
50+
pattern string
51+
52+
resource *resource.Options
53+
54+
// Check if we have to scaffold resource and/or controller
55+
resourceFlag *pflag.Flag
56+
controllerFlag *pflag.Flag
57+
doResource bool
58+
doController bool
59+
60+
// force indicates that the resource should be created even if it already exists
61+
force bool
62+
63+
// runMake indicates whether to run make or not after scaffolding APIs
64+
runMake bool
65+
}
66+
67+
var (
68+
_ plugin.CreateAPI = &createAPIPlugin{}
69+
_ cmdutil.RunOptions = &createAPIPlugin{}
70+
)
71+
72+
func (p createAPIPlugin) UpdateContext(ctx *plugin.Context) {
73+
ctx.Description = `Scaffold a Kubernetes API by creating a Resource definition and / or a Controller.
74+
75+
create resource will prompt the user for if it should scaffold the Resource and / or Controller. To only
76+
scaffold a Controller for an existing Resource, select "n" for Resource. To only define
77+
the schema for a Resource without writing a Controller, select "n" for Controller.
78+
79+
After the scaffold is written, api will run make on the project.
80+
`
81+
ctx.Examples = fmt.Sprintf(` # Create a frigates API with Group: ship, Version: v1beta1 and Kind: Frigate
82+
%s create api --group ship --version v1beta1 --kind Frigate
83+
84+
# Edit the API Scheme
85+
nano api/v1beta1/frigate_types.go
86+
87+
# Edit the Controller
88+
nano controllers/frigate/frigate_controller.go
89+
90+
# Edit the Controller Test
91+
nano controllers/frigate/frigate_controller_test.go
92+
93+
# Install CRDs into the Kubernetes cluster using kubectl apply
94+
make install
95+
96+
# Regenerate code and run against the Kubernetes cluster configured by ~/.kube/config
97+
make run
98+
`,
99+
ctx.CommandName)
100+
}
101+
102+
func (p *createAPIPlugin) BindFlags(fs *pflag.FlagSet) {
103+
fs.BoolVar(&p.runMake, "make", true, "if true, run make after generating files")
104+
105+
fs.BoolVar(&p.doResource, "resource", true,
106+
"if set, generate the resource without prompting the user")
107+
p.resourceFlag = fs.Lookup("resource")
108+
fs.BoolVar(&p.doController, "controller", true,
109+
"if set, generate the controller without prompting the user")
110+
p.controllerFlag = fs.Lookup("controller")
111+
112+
// TODO: remove this when a better solution for using addons is implemented.
113+
if os.Getenv("KUBEBUILDER_ENABLE_PLUGINS") != "" {
114+
fs.StringVar(&p.pattern, "pattern", "",
115+
"generates an API following an extension pattern (addon)")
116+
}
117+
118+
fs.BoolVar(&p.force, "force", false,
119+
"attempt to create resource even if it already exists")
120+
p.resource = &resource.Options{}
121+
fs.StringVar(&p.resource.Kind, "kind", "", "resource Kind")
122+
fs.StringVar(&p.resource.Group, "group", "", "resource Group")
123+
fs.StringVar(&p.resource.Version, "version", "", "resource Version")
124+
fs.BoolVar(&p.resource.Namespaced, "namespaced", true, "resource is namespaced")
125+
}
126+
127+
func (p *createAPIPlugin) InjectConfig(c *config.Config) {
128+
p.config = c
129+
}
130+
131+
func (p *createAPIPlugin) Run() error {
132+
return cmdutil.Run(p)
133+
}
134+
135+
func (p *createAPIPlugin) Validate() error {
136+
if err := p.resource.Validate(); err != nil {
137+
return err
138+
}
139+
140+
// TODO: re-evaluate whether y/n input still makes sense. We should probably always
141+
// scaffold the resource and controller.
142+
reader := bufio.NewReader(os.Stdin)
143+
if !p.resourceFlag.Changed {
144+
fmt.Println("Create Resource [y/n]")
145+
p.doResource = util.YesNo(reader)
146+
}
147+
if !p.controllerFlag.Changed {
148+
fmt.Println("Create Controller [y/n]")
149+
p.doController = util.YesNo(reader)
150+
}
151+
152+
// In case we want to scaffold a resource API we need to do some checks
153+
if p.doResource {
154+
// Check that resource doesn't exist or flag force was set
155+
if !p.force && p.config.HasResource(p.resource.GVK()) {
156+
return errors.New("API resource already exists")
157+
}
158+
159+
// Check that the provided group can be added to the project
160+
if !p.config.MultiGroup && len(p.config.Resources) != 0 && !p.config.HasGroup(p.resource.Group) {
161+
return fmt.Errorf("multiple groups are not allowed by default, " +
162+
"to enable multi-group visit kubebuilder.io/migration/multi-group.html")
163+
}
164+
}
165+
166+
return nil
167+
}
168+
169+
func (p *createAPIPlugin) GetScaffolder() (scaffold.Scaffolder, error) {
170+
// Load the boilerplate
171+
bp, err := ioutil.ReadFile(filepath.Join("hack", "boilerplate.go.txt")) // nolint:gosec
172+
if err != nil {
173+
return nil, fmt.Errorf("unable to load boilerplate: %v", err)
174+
}
175+
176+
// Load the requested plugins
177+
plugins := make([]model.Plugin, 0)
178+
switch strings.ToLower(p.pattern) {
179+
case "":
180+
// Default pattern
181+
case "addon":
182+
plugins = append(plugins, &addon.Plugin{})
183+
default:
184+
return nil, fmt.Errorf("unknown pattern %q", p.pattern)
185+
}
186+
187+
// Create the actual resource from the resource options
188+
res := p.resource.NewResource(p.config, p.doResource)
189+
return scaffolds.NewAPIScaffolder(p.config, string(bp), res, p.doResource, p.doController, plugins), nil
190+
}
191+
192+
func (p *createAPIPlugin) PostScaffold() error {
193+
// Load the requested plugins
194+
switch strings.ToLower(p.pattern) {
195+
case "":
196+
// Default pattern
197+
case "addon":
198+
// Ensure that we are pinning sigs.k8s.io/kubebuilder-declarative-pattern version
199+
// TODO: either find a better way to inject this version (ex. tools.go).
200+
err := util.RunCmd("Get kubebuilder-declarative-pattern dependency", "go", "get",
201+
"sigs.k8s.io/kubebuilder-declarative-pattern@"+KbDeclarativePatternVersion)
202+
if err != nil {
203+
return err
204+
}
205+
default:
206+
return fmt.Errorf("unknown pattern %q", p.pattern)
207+
}
208+
209+
if p.runMake {
210+
return util.RunCmd("Running make", "make")
211+
}
212+
return nil
213+
}

0 commit comments

Comments
 (0)