Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🏃 structure and expose cobra cmds #1148

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions cmd/alpha.go → cmd/alpha/alpha.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package main
package alpha

import (
"github.com/spf13/cobra"
"sigs.k8s.io/kubebuilder/cmd/alpha/webhookv1"
)

// newAlphaCommand returns alpha subcommand which will be mounted
// NewAlphaCommand returns alpha subcommand which will be mounted
// at the root command by the caller.
func newAlphaCommand() *cobra.Command {
func NewAlphaCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "alpha",
Short: "Expose commands which are in experimental or early stages of development",
Expand All @@ -34,7 +35,7 @@ kubebuilder alpha webhook <params>
}

cmd.AddCommand(
newWebhookCmd(),
webhookv1.NewWebhookCmd(),
)
return cmd
}
23 changes: 7 additions & 16 deletions cmd/webhook_v1.go → cmd/alpha/webhookv1/webhook_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package main
package webhookv1

import (
"fmt"
Expand All @@ -25,18 +25,19 @@ import (

"github.com/gobuffalo/flect"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"

"sigs.k8s.io/kubebuilder/cmd/util"
"sigs.k8s.io/kubebuilder/pkg/model"
"sigs.k8s.io/kubebuilder/pkg/scaffold"
"sigs.k8s.io/kubebuilder/pkg/scaffold/input"
"sigs.k8s.io/kubebuilder/pkg/scaffold/project"
sutil "sigs.k8s.io/kubebuilder/pkg/scaffold/util"
"sigs.k8s.io/kubebuilder/pkg/scaffold/v1/manager"
"sigs.k8s.io/kubebuilder/pkg/scaffold/v1/resource"
"sigs.k8s.io/kubebuilder/pkg/scaffold/v1/webhook"
)

func newWebhookCmd() *cobra.Command {
func NewWebhookCmd() *cobra.Command {
o := webhookOptions{}

cmd := &cobra.Command{
Expand All @@ -51,9 +52,9 @@ This command is only available for v1 scaffolding project.
kubebuilder alpha webhook --group crew --version v1 --kind FirstMate --type=mutating --operations=create,update
`,
Run: func(cmd *cobra.Command, args []string) {
dieIfNoProject()
util.DieIfNoProject()

projectInfo, err := scaffold.LoadProjectFile("PROJECT")
projectInfo, err := sutil.LoadProjectFile("PROJECT")
if err != nil {
log.Fatalf("failed to read the PROJECT file: %v", err)
}
Expand Down Expand Up @@ -103,7 +104,7 @@ This command is only available for v1 scaffolding project.
"the operations that the webhook will intercept, e.g. create, update, delete and connect")
cmd.Flags().BoolVar(&o.doMake, "make", true,
"if true, run make after generating files")
o.res = gvkForFlags(cmd.Flags())
o.res = util.GVKForFlags(cmd.Flags())
return cmd
}

Expand All @@ -115,13 +116,3 @@ type webhookOptions struct {
webhookType string
doMake bool
}

// gvkForFlags registers flags for Resource fields and returns the Resource
func gvkForFlags(f *flag.FlagSet) *resource.Resource {
r := &resource.Resource{}
f.StringVar(&r.Group, "group", "", "resource Group")
f.StringVar(&r.Version, "version", "", "resource Version")
f.StringVar(&r.Kind, "kind", "", "resource Kind")
f.StringVar(&r.Resource, "resource", "", "resource Resource")
return r
}
13 changes: 3 additions & 10 deletions cmd/api.go → cmd/create/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package main
package api

import (
"bufio"
Expand Down Expand Up @@ -74,7 +74,7 @@ func resourceForFlags(f *flag.FlagSet) *resource.Resource {

// APICmd represents the resource command
func (o *apiOptions) runAddAPI() {
dieIfNoProject()
util.DieIfNoProject()

switch strings.ToLower(o.pattern) {
case "":
Expand Down Expand Up @@ -126,7 +126,7 @@ func (o *apiOptions) postScaffold() error {
return nil
}

func newAPICommand() *cobra.Command {
func NewAPICommand() *cobra.Command {
options := apiOptions{
apiScaffolder: scaffold.API{},
}
Expand Down Expand Up @@ -169,10 +169,3 @@ After the scaffold is written, api will run make on the project.

return apiCmd
}

// dieIfNoProject checks to make sure the command is run from a directory containing a project file.
func dieIfNoProject() {
if _, err := os.Stat("PROJECT"); os.IsNotExist(err) {
log.Fatalf("Command must be run from a directory containing %s", "PROJECT")
}
}
13 changes: 8 additions & 5 deletions cmd/create.go → cmd/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package main
package create

import (
"fmt"

"github.com/spf13/cobra"
"sigs.k8s.io/kubebuilder/cmd/create/api"
"sigs.k8s.io/kubebuilder/cmd/create/webhookv2"
"sigs.k8s.io/kubebuilder/cmd/util"
)

func newCreateCmd() *cobra.Command {
func NewCreateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "Scaffold a Kubernetes API or webhook.",
Expand All @@ -32,16 +35,16 @@ func newCreateCmd() *cobra.Command {
},
}
cmd.AddCommand(
newAPICommand(),
api.NewAPICommand(),
)

foundProject, version := getProjectVersion()
foundProject, version := util.GetProjectVersion()
// It add webhook v2 command in the following 2 cases:
// - There are no PROJECT file found.
// - version == 2 is found in the PROJECT file.
if !foundProject || version == "2" {
cmd.AddCommand(
newWebhookV2Cmd(),
webhookv2.NewWebhookV2Cmd(),
)
}

Expand Down
12 changes: 7 additions & 5 deletions cmd/webhook_v2.go → cmd/create/webhookv2/webhook_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package main
package webhookv2

import (
"fmt"
Expand All @@ -26,16 +26,18 @@ import (
"github.com/gobuffalo/flect"
"github.com/spf13/cobra"

"sigs.k8s.io/kubebuilder/cmd/util"
"sigs.k8s.io/kubebuilder/pkg/model"
"sigs.k8s.io/kubebuilder/pkg/scaffold"
"sigs.k8s.io/kubebuilder/pkg/scaffold/input"
"sigs.k8s.io/kubebuilder/pkg/scaffold/project"
sutil "sigs.k8s.io/kubebuilder/pkg/scaffold/util"
"sigs.k8s.io/kubebuilder/pkg/scaffold/v1/resource"
resourcev2 "sigs.k8s.io/kubebuilder/pkg/scaffold/v2"
"sigs.k8s.io/kubebuilder/pkg/scaffold/v2/webhook"
)

func newWebhookV2Cmd() *cobra.Command {
func NewWebhookV2Cmd() *cobra.Command {
o := webhookV2Options{}

cmd := &cobra.Command{
Expand All @@ -49,9 +51,9 @@ func newWebhookV2Cmd() *cobra.Command {
kubebuilder create webhook --group crew --version v1 --kind FirstMate --conversion
`,
Run: func(cmd *cobra.Command, args []string) {
dieIfNoProject()
util.DieIfNoProject()

projectInfo, err := scaffold.LoadProjectFile("PROJECT")
projectInfo, err := sutil.LoadProjectFile("PROJECT")
if err != nil {
log.Fatalf("failed to read the PROJECT file: %v", err)
}
Expand Down Expand Up @@ -107,7 +109,7 @@ You need to implement the conversion.Hub and conversion.Convertible interfaces f

},
}
o.res = gvkForFlags(cmd.Flags())
o.res = util.GVKForFlags(cmd.Flags())
cmd.Flags().BoolVar(&o.defaulting, "defaulting", false,
"if set, scaffold the defaulting webhook")
cmd.Flags().BoolVar(&o.validation, "programmatic-validation", false,
Expand Down
2 changes: 1 addition & 1 deletion cmd/go_version_test.go → cmd/init/go_version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package main
package init

import "testing"

Expand Down
79 changes: 77 additions & 2 deletions cmd/init_project.go → cmd/init/init_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package main
package init

import (
"encoding/json"
"fmt"
"log"
"os"
Expand All @@ -27,13 +28,15 @@ import (

"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"golang.org/x/tools/go/packages"

"sigs.k8s.io/kubebuilder/cmd/util"
"sigs.k8s.io/kubebuilder/pkg/scaffold"
"sigs.k8s.io/kubebuilder/pkg/scaffold/project"
sutil "sigs.k8s.io/kubebuilder/pkg/scaffold/util"
)

func newInitProjectCmd() *cobra.Command {
func NewInitProjectCmd() *cobra.Command {
o := projectOptions{}

initCmd := &cobra.Command{
Expand Down Expand Up @@ -66,6 +69,14 @@ kubebuilder init --domain example.org --license apache2 --owner "The Kubernetes
return initCmd
}

// module and goMod arg just enough of the output of `go mod edit -json` for our purposes
type goMod struct {
Module module
}
type module struct {
Path string
}

type projectOptions struct {

// flags
Expand Down Expand Up @@ -224,6 +235,70 @@ func checkGoVersion(verStr string) error {
return nil
}

// findCurrentRepo attempts to determine the current repository
// though a combination of go/packages and `go mod` commands/tricks.
func findCurrentRepo() (string, error) {
// easiest case: project file already exists
projFile, err := sutil.LoadProjectFile("PROJECT")
if err == nil {
return projFile.Repo, nil
}

// next easy case: existing go module
path, err := findGoModulePath(false)
if err == nil {
return path, nil
}

// next, check if we've got a package in the current directory
pkgCfg := &packages.Config{
Mode: packages.NeedName, // name gives us path as well
}
pkgs, err := packages.Load(pkgCfg, ".")
// NB(directxman12): when go modules are off and we're outside GOPATH and
// we don't otherwise have a good guess packages.Load will fabricate a path
// that consists of `_/absolute/path/to/current/directory`. We shouldn't
// use that when it happens.
if err == nil && len(pkgs) > 0 && len(pkgs[0].PkgPath) > 0 && pkgs[0].PkgPath[0] != '_' {
return pkgs[0].PkgPath, nil
}

// otherwise, try to get `go mod init` to guess for us -- it's pretty good
cmd := exec.Command("go", "mod", "init")
cmd.Env = append(cmd.Env, os.Environ()...)
cmd.Env = append(cmd.Env, "GO111MODULE=on" /* turn on modules just for these commands */)
if _, err := cmd.Output(); err != nil {
if exitErr, isExitErr := err.(*exec.ExitError); isExitErr {
err = fmt.Errorf("%s", string(exitErr.Stderr))
}
// give up, let the user figure it out
return "", fmt.Errorf("could not determine repository path from module data, package data, or by initializing a module: %v", err)
}
defer os.Remove("go.mod") // clean up after ourselves
return findGoModulePath(true)
}

// findGoModulePath finds the path of the current module, if present.
func findGoModulePath(forceModules bool) (string, error) {
cmd := exec.Command("go", "mod", "edit", "-json")
cmd.Env = append(cmd.Env, os.Environ()...)
if forceModules {
cmd.Env = append(cmd.Env, "GO111MODULE=on" /* turn on modules just for these commands */)
}
out, err := cmd.Output()
if err != nil {
if exitErr, isExitErr := err.(*exec.ExitError); isExitErr {
err = fmt.Errorf("%s", string(exitErr.Stderr))
}
return "", err
}
mod := goMod{}
if err := json.Unmarshal(out, &mod); err != nil {
return "", err
}
return mod.Module.Path, nil
}

func (o *projectOptions) postScaffold() error {
// preserve old "ask if not explicitly set" behavior for the `--dep` flag
// (asking is handled by the v1 scaffolder)
Expand Down
Loading