Skip to content

Commit 0530fd1

Browse files
committed
Switch v2 Scaffolding to Use Go Modules
This switches the v2 scaffolding to use Go modules. Currently, a custom fork of controller-runtime is being used. We'll need to merge that to controller-runtime master before merging this.
1 parent 8e34625 commit 0530fd1

15 files changed

+404
-392
lines changed

.travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ os:
55
- osx
66

77
go:
8-
- "1.11"
8+
- "1.12"
99

1010
git:
1111
depth: 3
@@ -28,7 +28,7 @@ install:
2828
-
2929

3030
script:
31-
- TRACE=1 ./test.sh
31+
- GO111MODULE=on TRACE=1 ./test.sh
3232
- ./scripts/install_test.sh
3333

3434
# TBD. Suppressing for now.

cmd/init_project.go

+74-63
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package main
1818

1919
import (
20-
"bufio"
2120
"fmt"
2221
"log"
2322
"os"
@@ -35,9 +34,7 @@ import (
3534
)
3635

3736
func newInitProjectCmd() *cobra.Command {
38-
o := projectOptions{
39-
projectScaffolder: &scaffold.Project{},
40-
}
37+
o := projectOptions{}
4138

4239
initCmd := &cobra.Command{
4340
Use: "init",
@@ -70,50 +67,54 @@ kubebuilder init --domain example.org --license apache2 --owner "The Kubernetes
7067
}
7168

7269
type projectOptions struct {
73-
projectScaffolder *scaffold.Project
7470

71+
// flags
72+
fetchDeps bool
73+
skipGoVersionCheck bool
74+
75+
boilerplate project.Boilerplate
76+
project project.Project
77+
78+
// deprecated flags
7579
dep bool
7680
depFlag *flag.Flag
7781
depArgs []string
78-
skipGoVersionCheck bool
82+
83+
// final result
84+
scaffolder scaffold.ProjectScaffolder
7985
}
8086

8187
func (o *projectOptions) bindCmdlineFlags(cmd *cobra.Command) {
82-
cmd.Flags().BoolVar(
83-
&o.skipGoVersionCheck, "skip-go-version-check", false, "if specified, skip checking the Go version")
84-
cmd.Flags().BoolVar(
85-
&o.dep, "dep", true, "if specified, determines whether dep will be used.")
88+
cmd.Flags().BoolVar(&o.skipGoVersionCheck, "skip-go-version-check", false, "if specified, skip checking the Go version")
89+
90+
// dependency args
91+
cmd.Flags().BoolVar(&o.fetchDeps, "fetch-deps", true, "ensure dependencies are downloaded")
92+
93+
// deprecated dependency args
94+
cmd.Flags().BoolVar(&o.dep, "dep", true, "if specified, determines whether dep will be used.")
8695
o.depFlag = cmd.Flag("dep")
8796
cmd.Flags().StringArrayVar(&o.depArgs, "depArgs", nil, "Additional arguments for dep")
97+
cmd.Flags().MarkDeprecated("dep", "use the fetch-deps flag instead")
98+
cmd.Flags().MarkDeprecated("depArgs", "will be removed with version 1 scaffolding")
8899

89-
o.bindBoilerplateFlags(cmd)
90-
o.bindProjectFlags(cmd)
91-
}
100+
// boilerplate args
101+
cmd.Flags().StringVar(&o.boilerplate.Path, "path", "", "path for boilerplate")
102+
cmd.Flags().StringVar(&o.boilerplate.License, "license", "apache2", "license to use to boilerplate. May be one of apache2,none")
103+
cmd.Flags().StringVar(&o.boilerplate.Owner, "owner", "", "Owner to add to the copyright")
92104

93-
// projectForFlags registers flags for Project fields and returns the Project
94-
func (o *projectOptions) bindProjectFlags(cmd *cobra.Command) {
95-
p := &o.projectScaffolder.Info
96-
cmd.Flags().StringVar(&p.Repo, "repo", "", "name of the github repo. "+
105+
// project args
106+
cmd.Flags().StringVar(&o.project.Repo, "repo", util.Repo, "name of the github repo. "+
97107
"defaults to the go package of the current working directory.")
98-
cmd.Flags().StringVar(&p.Domain, "domain", "k8s.io", "domain for groups")
99-
cmd.Flags().StringVar(&p.Version, "project-version", project.Version1, "project version")
100-
}
101-
102-
// bindBoilerplateFlags registers flags for Boilerplate fields and returns the Boilerplate
103-
func (o *projectOptions) bindBoilerplateFlags(cmd *cobra.Command) {
104-
bp := &o.projectScaffolder.Boilerplate
105-
cmd.Flags().StringVar(&bp.Path, "path", "", "path for boilerplate")
106-
cmd.Flags().StringVar(&bp.License, "license", "apache2", "license to use to boilerplate. Maybe one of apache2,none")
107-
cmd.Flags().StringVar(&bp.Owner, "owner", "", "Owner to add to the copyright")
108+
cmd.Flags().StringVar(&o.project.Domain, "domain", "k8s.io", "domain for groups")
109+
cmd.Flags().StringVar(&o.project.Version, "project-version", project.Version1, "project version")
108110
}
109111

110112
func (o *projectOptions) initializeProject() {
111-
112113
if err := o.validate(); err != nil {
113114
log.Fatal(err)
114115
}
115116

116-
if err := o.projectScaffolder.Scaffold(); err != nil {
117+
if err := o.scaffolder.Scaffold(); err != nil {
117118
log.Fatalf("error scaffolding project: %v", err)
118119
}
119120

@@ -132,8 +133,30 @@ func (o *projectOptions) validate() error {
132133
}
133134
}
134135

135-
if !depExists() {
136-
return fmt.Errorf("Dep is not installed. Follow steps at: https://golang.github.io/dep/docs/installation.html")
136+
switch o.project.Version {
137+
case project.Version1:
138+
var defEnsure *bool
139+
if o.depFlag.Changed {
140+
defEnsure = &o.dep
141+
}
142+
o.scaffolder = &scaffold.V1Project{
143+
Project: o.project,
144+
Boilerplate: o.boilerplate,
145+
146+
DepArgs: o.depArgs,
147+
DefinitelyEnsure: defEnsure,
148+
}
149+
case project.Version2:
150+
o.scaffolder = &scaffold.V2Project{
151+
Project: o.project,
152+
Boilerplate: o.boilerplate,
153+
}
154+
default:
155+
return fmt.Errorf("unknown project version %v", o.project.Version)
156+
}
157+
158+
if err := o.scaffolder.Validate(); err != nil {
159+
return err
137160
}
138161

139162
if util.ProjectExist() {
@@ -193,39 +216,27 @@ func checkGoVersion(verStr string) error {
193216
return nil
194217
}
195218

196-
func depExists() bool {
197-
_, err := exec.LookPath("dep")
198-
return err == nil
199-
}
200-
201219
func (o *projectOptions) postScaffold() error {
202-
if !o.depFlag.Changed {
203-
reader := bufio.NewReader(os.Stdin)
204-
fmt.Println("Run `dep ensure` to fetch dependencies (Recommended) [y/n]?")
205-
o.dep = util.Yesno(reader)
206-
}
207-
if o.dep {
208-
c := exec.Command("dep", "ensure") // #nosec
209-
if len(o.depArgs) > 0 {
210-
c.Args = append(c.Args, o.depArgs...)
211-
}
212-
c.Stderr = os.Stderr
213-
c.Stdout = os.Stdout
214-
fmt.Println(strings.Join(c.Args, " "))
215-
if err := c.Run(); err != nil {
216-
return err
217-
}
220+
// preserve old "ask if not explicitly set" behavior for the `--dep` flag
221+
// (asking is handled by the v1 scaffolder)
222+
if (o.depFlag.Changed && !o.dep) || !o.fetchDeps {
223+
fmt.Println("Skipping fetching dependencies.")
224+
return nil
225+
}
218226

219-
fmt.Println("Running make...")
220-
c = exec.Command("make") // #nosec
221-
c.Stderr = os.Stderr
222-
c.Stdout = os.Stdout
223-
fmt.Println(strings.Join(c.Args, " "))
224-
if err := c.Run(); err != nil {
225-
return err
226-
}
227-
} else {
228-
fmt.Println("Skipping `dep ensure`. Dependencies will not be fetched.")
227+
ensured, err := o.scaffolder.EnsureDependencies()
228+
if err != nil {
229+
return err
229230
}
230-
return nil
231+
232+
if !ensured {
233+
return nil
234+
}
235+
236+
fmt.Println("Running make...")
237+
c := exec.Command("make") // #nosec
238+
c.Stderr = os.Stderr
239+
c.Stdout = os.Stdout
240+
fmt.Println(strings.Join(c.Args, " "))
241+
return c.Run()
231242
}

cmd/main.go

+73-15
Original file line numberDiff line numberDiff line change
@@ -17,37 +17,95 @@ limitations under the License.
1717
package main
1818

1919
import (
20-
gobuild "go/build"
21-
"log"
20+
"fmt"
2221
"os"
23-
"regexp"
22+
"os/exec"
23+
"log"
24+
"encoding/json"
2425

2526
"github.com/spf13/cobra"
27+
"golang.org/x/tools/go/packages"
2628

27-
toolsutil "sigs.k8s.io/controller-tools/pkg/crd/util"
2829
"sigs.k8s.io/kubebuilder/cmd/util"
2930
"sigs.k8s.io/kubebuilder/cmd/version"
31+
"sigs.k8s.io/kubebuilder/pkg/scaffold"
3032
)
3133

32-
func main() {
33-
gopath := gobuild.Default.GOPATH
34+
// module and goMod arg just enough of the output of `go mod edit -json` for our purposes
35+
type goMod struct {
36+
Module module
37+
}
38+
type module struct {
39+
Path string
40+
}
3441

35-
wd, err := os.Getwd()
42+
// findGoModulePath finds the path of the current module, if present.
43+
func findGoModulePath(forceModules bool) (string, error) {
44+
cmd := exec.Command("go", "mod", "edit", "-json")
45+
cmd.Env = append(cmd.Env, os.Environ()...)
46+
if forceModules {
47+
cmd.Env = append(cmd.Env, "GO111MODULE=on" /* turn on modules just for these commands */)
48+
}
49+
out, err := cmd.Output()
3650
if err != nil {
37-
log.Fatal(err)
51+
if exitErr, isExitErr := err.(*exec.ExitError); isExitErr {
52+
err = fmt.Errorf("%s", string(exitErr.Stderr))
53+
}
54+
return "", err
55+
}
56+
mod := goMod{}
57+
if err := json.Unmarshal(out, &mod); err != nil {
58+
return "", err
59+
}
60+
return mod.Module.Path, nil
61+
}
62+
63+
// findCurrentRepo attempts to determine the current repository
64+
// though a combination of go/packages and `go mod` commands/tricks.
65+
func findCurrentRepo() (string, error) {
66+
// easiest case: project file already exists
67+
projFile, err := scaffold.LoadProjectFile("PROJECT")
68+
if err == nil {
69+
return projFile.Repo, nil
70+
}
71+
72+
// next easy case: existing go module
73+
path, err := findGoModulePath(false)
74+
if err == nil {
75+
return path, nil
76+
}
77+
78+
// next, check if we've got a package in the current directory
79+
pkgCfg := &packages.Config{
80+
Mode: packages.NeedName, // name gives us path as well
81+
}
82+
pkgs, err := packages.Load(pkgCfg, ".")
83+
if err == nil && len(pkgs) > 0 {
84+
return pkgs[0].PkgPath, nil
3885
}
3986

40-
if !toolsutil.IsUnderGoSrcPath(wd) {
41-
log.Fatalf("kubebuilder must be run from the project root under $GOPATH/src/<package>. "+
42-
"\nCurrent GOPATH=%s. \nCurrent directory=%s", gopath, wd)
87+
// otherwise, try to get `go mod init` to guess for us -- it's pretty good
88+
cmd := exec.Command("go", "mod", "init")
89+
cmd.Env = append(cmd.Env, os.Environ()...)
90+
cmd.Env = append(cmd.Env, "GO111MODULE=on" /* turn on modules just for these commands */)
91+
if _, err := cmd.Output(); err != nil {
92+
if exitErr, isExitErr := err.(*exec.ExitError); isExitErr {
93+
err = fmt.Errorf("%s", string(exitErr.Stderr))
94+
}
95+
// give up, let the user figure it out
96+
return "", fmt.Errorf("could not determine repository path from module data, package data, or by initializing a module: %v", err)
4397
}
44-
util.Repo, err = toolsutil.DirToGoPkg(wd)
98+
defer os.Remove("go.mod") // clean up after ourselves
99+
return findGoModulePath(true)
100+
}
101+
102+
func main() {
103+
repoPath, err := findCurrentRepo()
45104
if err != nil {
46-
log.Fatal(err)
105+
log.Fatal(fmt.Errorf("error finding current repository: %v", err))
47106
}
48107

49-
re := regexp.MustCompile(`(^.*\/src)(\/.*$)`)
50-
util.GoSrc = re.ReplaceAllString(wd, "$1")
108+
util.Repo = repoPath
51109

52110
rootCmd := defaultCommand()
53111

cmd/util/util.go

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import (
3434

3535
var Domain string
3636
var Repo string
37-
var GoSrc string
3837

3938
// writeIfNotFound returns true if the file was created and false if it already exists
4039
func WriteIfNotFound(path, templateName, templateValue string, data interface{}) bool {

common.sh

+3
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ function fetch_tools {
126126
curl -sL ${kb_tools_download_url} -o "$kb_tools_archive_path"
127127
fi
128128
tar -zvxf "$kb_tools_archive_path" -C "$tmp_root/"
129+
130+
header_text "fetching controller-gen from source (till it's packaged)"
131+
GO111MODULE=on go get sigs.k8s.io/controller-tools/cmd/controller-gen@crdgeneratortmp
129132
}
130133

131134
function build_kb {

generate_vendor.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ fi
3434

3535
build_kb() {
3636
rm -f /tmp/kb && \
37-
go build -o /tmp/kb sigs.k8s.io/kubebuilder/cmd
37+
GO111MODULE=on go build -o /tmp/kb sigs.k8s.io/kubebuilder/cmd
3838
}
3939

4040

generated_golden.sh

+11-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
set -e
1818

19+
source common.sh
1920

2021
build_kb() {
2122
go build -o ./bin/kubebuilder sigs.k8s.io/kubebuilder/cmd
@@ -34,14 +35,16 @@ scaffold_test_project() {
3435
rm -rf ./testdata/$project/*
3536
pushd .
3637
cd testdata/$project
37-
# untar Gopkg.lock and vendor directory for appropriate project version
38-
tar -zxf ../vendor.v$version.tgz
3938

4039
kb=$testdata_dir/../bin/kubebuilder
4140

4241
oldgopath=$GOPATH
4342
if [ $version == "1" ]; then
43+
export GO111MODULE=auto
4444
export GOPATH=$(pwd)/../.. # go ignores vendor under testdata, so fake out a gopath
45+
# untar Gopkg.lock and vendor directory for appropriate project version
46+
tar -zxf $testdata_dir/vendor.v$version.tgz
47+
4548
$kb init --project-version $version --domain testproject.org --license apache2 --owner "The Kubernetes authors" --dep=false
4649
$kb create api --group crew --version v1 --kind FirstMate --controller=true --resource=true --make=false
4750
$kb alpha webhook --group crew --version v1 --kind FirstMate --type=mutating --operations=create,update --make=false
@@ -54,6 +57,11 @@ scaffold_test_project() {
5457
$kb alpha webhook --group core --version v1 --kind Namespace --type=mutating --operations=update --make=false
5558
$kb create api --group policy --version v1beta1 --kind HealthCheckPolicy --example=false --controller=true --resource=true --namespaced=false --make=false
5659
elif [ $version == "2" ]; then
60+
export GO111MODULE=on
61+
go get sigs.k8s.io/controller-tools/cmd/controller-gen@crdgeneratortmp
62+
export PATH=$PATH:$(go env GOPATH)/bin
63+
go mod init sigs.k8s.io/kubebuilder/testdata/project_v2 # our repo autodetection will traverse up to the kb module if we don't do this
64+
5765
$kb init --project-version $version --domain testproject.org --license apache2 --owner "The Kubernetes authors"
5866
$kb create api --group crew --version v1 --kind Captain --controller=true --resource=true --make=false
5967
$kb create api --group crew --version v1 --kind FirstMate --controller=true --resource=true --make=false
@@ -66,7 +74,7 @@ scaffold_test_project() {
6674
$kb alpha webhook --group core --version v1 --kind Namespace --type=mutating --operations=update --make=false
6775
# $kb create api --group policy --version v1beta1 --kind HealthCheckPolicy --example=false --controller=true --resource=true --namespaced=false --make=false
6876
fi
69-
make
77+
make all test # v2 doesn't test by default
7078
rm -f Gopkg.lock
7179
rm -rf ./vendor
7280
rm -rf ./bin

0 commit comments

Comments
 (0)