From e9e35b2b4ed37a4ba7961913799edff7fc4f3ddd Mon Sep 17 00:00:00 2001 From: fanmin shi Date: Sun, 18 Feb 2018 17:45:15 -0800 Subject: [PATCH 1/5] commands: add ExitError and ExitSuccess error codes in error.go --- commands/operator-sdk/cmd/error.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/commands/operator-sdk/cmd/error.go b/commands/operator-sdk/cmd/error.go index d648ccacd7..c79c430bdf 100644 --- a/commands/operator-sdk/cmd/error.go +++ b/commands/operator-sdk/cmd/error.go @@ -6,6 +6,9 @@ import ( ) const ( + // http://tldp.org/LDP/abs/html/exitcodes.html + ExitSuccess = iota + ExitError // ExitBadArgs is the exit error code for bad arguments. ExitBadArgs = 128 ) From 6d2a016038a8296e48fd4aece8d7094a3b6cf79f Mon Sep 17 00:00:00 2001 From: fanmin shi Date: Sun, 18 Feb 2018 17:46:34 -0800 Subject: [PATCH 2/5] commands/operator-sdk: support dir stucture generation in cmd/new.go --- commands/operator-sdk/cmd/new.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/commands/operator-sdk/cmd/new.go b/commands/operator-sdk/cmd/new.go index aaf28b3812..7966d73037 100644 --- a/commands/operator-sdk/cmd/new.go +++ b/commands/operator-sdk/cmd/new.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" + "github.com/coreos/operator-sdk/pkg/generator" "github.com/spf13/cobra" ) @@ -27,8 +28,9 @@ var newCmd = &cobra.Command{ } var ( - apiGroup string - kind string + apiGroup string + kind string + projectName string ) func init() { @@ -44,8 +46,16 @@ func newFunc(cmd *cobra.Command, args []string) { ExitWithError(ExitBadArgs, fmt.Errorf("new command needs 1 argument.")) } parse(args) - // TODO: add generation logic. + g := generator.NewGenerator(apiGroup, kind, projectName) + err := g.Render() + if err != nil { + ExitWithError(ExitError, fmt.Errorf("failed to create project %v: %v", projectName, err)) + } } func parse(args []string) { + projectName = args[0] + if len(projectName) == 0 { + ExitWithError(ExitBadArgs, fmt.Errorf("project-name must not be empty")) + } } From ceefdc1974a8f1cc4fb788fa94e8548e703263bf Mon Sep 17 00:00:00 2001 From: fanmin shi Date: Sun, 18 Feb 2018 17:47:48 -0800 Subject: [PATCH 3/5] pkg/generator: add default directories generation --- pkg/generator/generator.go | 105 ++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/pkg/generator/generator.go b/pkg/generator/generator.go index 58ebcec46f..7e0da9ad7b 100644 --- a/pkg/generator/generator.go +++ b/pkg/generator/generator.go @@ -1,61 +1,106 @@ package generator import ( - "errors" "os" "path/filepath" "strings" ) -const defaultFileMode = 0750 +const ( + defaultFileMode = 0750 + cmdDir = "cmd" + deployDir = "deploy" + configDir = "config" + tmpDir = "tmp" + buildDir = tmpDir + "/build" + codegenDir = tmpDir + "/codegen" + pkgDir = "pkg" + apisDir = pkgDir + "/apis" + clientDir = pkgDir + "/client" + stubDir = pkgDir + "/stub" +) type Generator struct { + apiGroup string + kind string + projectName string +} + +// NewGenerator creates a new scaffold Generator. +func NewGenerator(apiGroup, kind, projectName string) *Generator { + return &Generator{apiGroup: apiGroup, kind: kind, projectName: projectName} } +// Render generates the default project structure. func (g *Generator) Render() error { - gopath, ok := os.LookupEnv("GOPATH") - if !ok { - return errors.New("GOPATH must be set") + if err := g.renderCmd(); err != nil { + return err } - projDir, ok := os.LookupEnv("PROJECT") // github.com/coreos/play - if !ok { - return errors.New("PROJECT must be set") + if err := g.renderConfig(); err != nil { + return err + } + if err := g.renderDeploy(); err != nil { + return err } - apiGroup, ok := os.LookupEnv("APIGROUP") // play.coreos.com/v1alpha1 - if !ok { - return errors.New("PROJECT must be set") + if err := g.renderPkg(); err != nil { + return err } + if err := g.renderTmp(); err != nil { + return err + } + return nil +} - projDir = filepath.Join(gopath, "src", projDir) - err := os.MkdirAll(projDir, defaultFileMode) - if err != nil { +func (g *Generator) renderCmd() error { + if err := os.MkdirAll(filepath.Join(cmdDir, g.projectName), defaultFileMode); err != nil { return err } + // TODO render files. + return nil +} - programName := func() string { - splits := strings.Split(projDir, "/") - return splits[len(splits)-1] - }() - err = os.MkdirAll(filepath.Join(projDir, "cmd", programName), defaultFileMode) - if err != nil { +func (g *Generator) renderConfig() error { + if err := os.MkdirAll(configDir, defaultFileMode); err != nil { return err } + // TODO render files. + return nil +} - // pkg/apis/ - groupName, apiVersion := func() (string, string) { - splits := strings.Split(apiGroup, "/") - return strings.Split(splits[0], ".")[0], splits[1] - }() - err = os.MkdirAll(filepath.Join(projDir, "pkg/apis", groupName, apiVersion), defaultFileMode) - if err != nil { +func (g *Generator) renderDeploy() error { + if err := os.MkdirAll(filepath.Join(deployDir, g.projectName), defaultFileMode); err != nil { return err } + // TODO render files. + return nil +} - // pkg/controller/ - err = os.MkdirAll(filepath.Join(projDir, "pkg/controller"), defaultFileMode) - if err != nil { +func (g *Generator) renderTmp() error { + if err := os.MkdirAll(buildDir, defaultFileMode); err != nil { + return err + } + if err := os.MkdirAll(codegenDir, defaultFileMode); err != nil { return err } + // TODO render files. + return nil +} +func (g *Generator) renderPkg() error { + if err := os.MkdirAll(filepath.Join(apisDir, g.projectName, apiVersion(g.apiGroup)), defaultFileMode); err != nil { + return err + } + if err := os.MkdirAll(clientDir, defaultFileMode); err != nil { + return err + } + if err := os.MkdirAll(stubDir, defaultFileMode); err != nil { + return err + } + // TODO render files. return nil } + +// apiVersion extracts api version from the given apiGroup. +func apiVersion(apiGroup string) string { + return strings.Split(apiGroup, "/")[1] +} From c1373f762599ceb4ff3b05b60d5b2f427810bdc2 Mon Sep 17 00:00:00 2001 From: fanmin shi Date: Tue, 20 Feb 2018 14:21:06 -0800 Subject: [PATCH 4/5] *: change gen output --- commands/operator-sdk/cmd/new.go | 5 ++++ pkg/generator/generator.go | 42 +++++++++++++++++++------------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/commands/operator-sdk/cmd/new.go b/commands/operator-sdk/cmd/new.go index 7966d73037..39825a7a83 100644 --- a/commands/operator-sdk/cmd/new.go +++ b/commands/operator-sdk/cmd/new.go @@ -46,6 +46,7 @@ func newFunc(cmd *cobra.Command, args []string) { ExitWithError(ExitBadArgs, fmt.Errorf("new command needs 1 argument.")) } parse(args) + verifyFlags() g := generator.NewGenerator(apiGroup, kind, projectName) err := g.Render() if err != nil { @@ -59,3 +60,7 @@ func parse(args []string) { ExitWithError(ExitBadArgs, fmt.Errorf("project-name must not be empty")) } } + +func verifyFlags() { + // TODO: verify format of input flags. +} diff --git a/pkg/generator/generator.go b/pkg/generator/generator.go index 7e0da9ad7b..5f8ff40392 100644 --- a/pkg/generator/generator.go +++ b/pkg/generator/generator.go @@ -16,13 +16,14 @@ const ( codegenDir = tmpDir + "/codegen" pkgDir = "pkg" apisDir = pkgDir + "/apis" - clientDir = pkgDir + "/client" stubDir = pkgDir + "/stub" ) type Generator struct { - apiGroup string - kind string + apiGroup string + kind string + // projectName is name of the new operator application + // and is also the name of the base directory. projectName string } @@ -45,14 +46,11 @@ func (g *Generator) Render() error { if err := g.renderPkg(); err != nil { return err } - if err := g.renderTmp(); err != nil { - return err - } - return nil + return g.renderTmp() } func (g *Generator) renderCmd() error { - if err := os.MkdirAll(filepath.Join(cmdDir, g.projectName), defaultFileMode); err != nil { + if err := os.MkdirAll(filepath.Join(g.projectName, cmdDir, g.projectName), defaultFileMode); err != nil { return err } // TODO render files. @@ -60,7 +58,7 @@ func (g *Generator) renderCmd() error { } func (g *Generator) renderConfig() error { - if err := os.MkdirAll(configDir, defaultFileMode); err != nil { + if err := os.MkdirAll(filepath.Join(g.projectName, configDir), defaultFileMode); err != nil { return err } // TODO render files. @@ -68,7 +66,7 @@ func (g *Generator) renderConfig() error { } func (g *Generator) renderDeploy() error { - if err := os.MkdirAll(filepath.Join(deployDir, g.projectName), defaultFileMode); err != nil { + if err := os.MkdirAll(filepath.Join(g.projectName, deployDir), defaultFileMode); err != nil { return err } // TODO render files. @@ -76,10 +74,10 @@ func (g *Generator) renderDeploy() error { } func (g *Generator) renderTmp() error { - if err := os.MkdirAll(buildDir, defaultFileMode); err != nil { + if err := os.MkdirAll(filepath.Join(g.projectName, buildDir), defaultFileMode); err != nil { return err } - if err := os.MkdirAll(codegenDir, defaultFileMode); err != nil { + if err := os.MkdirAll(filepath.Join(g.projectName, codegenDir), defaultFileMode); err != nil { return err } // TODO render files. @@ -87,13 +85,10 @@ func (g *Generator) renderTmp() error { } func (g *Generator) renderPkg() error { - if err := os.MkdirAll(filepath.Join(apisDir, g.projectName, apiVersion(g.apiGroup)), defaultFileMode); err != nil { - return err - } - if err := os.MkdirAll(clientDir, defaultFileMode); err != nil { + if err := os.MkdirAll(filepath.Join(g.projectName, apisDir, apiDirName(g.apiGroup), apiVersion(g.apiGroup)), defaultFileMode); err != nil { return err } - if err := os.MkdirAll(stubDir, defaultFileMode); err != nil { + if err := os.MkdirAll(filepath.Join(g.projectName, stubDir), defaultFileMode); err != nil { return err } // TODO render files. @@ -104,3 +99,16 @@ func (g *Generator) renderPkg() error { func apiVersion(apiGroup string) string { return strings.Split(apiGroup, "/")[1] } + +// groupName extracts the group name from the givem apiGroup. +func groupName(apiGroup string) string { + return strings.Split(apiGroup, "/")[0] +} + +// apiDirName extracts the name of api directory under ../apis/ from the apiGroup. +// it uses the first word separated with "." of the groupName as the api directory name. +// for example, +// apiDirName("app.example.com/v1alpha1") => "app". +func apiDirName(apiGroup string) string { + return strings.Split(groupName(apiGroup), ".")[0] +} From 31c32cf86afd0f6e2c14455179c07455e33b78b3 Mon Sep 17 00:00:00 2001 From: fanmin shi Date: Tue, 20 Feb 2018 14:32:49 -0800 Subject: [PATCH 5/5] gen: add ascii graph for render output --- commands/operator-sdk/cmd/new.go | 1 + pkg/generator/generator.go | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/commands/operator-sdk/cmd/new.go b/commands/operator-sdk/cmd/new.go index 39825a7a83..34e2fed530 100644 --- a/commands/operator-sdk/cmd/new.go +++ b/commands/operator-sdk/cmd/new.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/coreos/operator-sdk/pkg/generator" + "github.com/spf13/cobra" ) diff --git a/pkg/generator/generator.go b/pkg/generator/generator.go index 5f8ff40392..60022f8ef9 100644 --- a/pkg/generator/generator.go +++ b/pkg/generator/generator.go @@ -32,7 +32,21 @@ func NewGenerator(apiGroup, kind, projectName string) *Generator { return &Generator{apiGroup: apiGroup, kind: kind, projectName: projectName} } -// Render generates the default project structure. +// Render generates the default project structure: +// +// ├── +// │ ├── cmd +// │ │ └── +// │ ├── config +// │ ├── deploy +// │ ├── pkg +// │ │ ├── apis +// │ │ │ └── // computed from apiDirName(apiGroup). +// │ │ │ └── // computed from apiVersion(apiGroup). +// │ │ └── stub +// │ └── tmp +// │ ├── build +// │ └── codegen func (g *Generator) Render() error { if err := g.renderCmd(); err != nil { return err