Skip to content

Commit

Permalink
Merge pull request #375 from yue9944882/feat/interactive-create-resource
Browse files Browse the repository at this point in the history
Feat: Make create resource commands interactive
  • Loading branch information
k8s-ci-robot authored Jul 12, 2019
2 parents c8758f7 + 50669ee commit 916cc4e
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 99 deletions.
217 changes: 122 additions & 95 deletions cmd/apiserver-boot/boot/create/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package create

import (
"bufio"
"fmt"
"log"
"os"
Expand All @@ -37,7 +38,9 @@ import (
var kindName string
var resourceName string
var nonNamespacedKind bool
var generateAdmissionController bool
var skipGenerateAdmissionController bool
var skipGenerateResource bool
var skipGenerateController bool

var createResourceCmd = &cobra.Command{
Use: "resource",
Expand All @@ -53,7 +56,10 @@ func AddCreateResource(cmd *cobra.Command) {
RegisterResourceFlags(createResourceCmd)

createResourceCmd.Flags().BoolVar(&nonNamespacedKind, "non-namespaced", false, "if set, the API kind will be non namespaced")
createResourceCmd.Flags().BoolVar(&generateAdmissionController, "admission-controller", false, "if set, an admission controller for the resources will be generated")

createResourceCmd.Flags().BoolVar(&skipGenerateResource, "skip-resource", false, "if set, the resources will not be generated")
createResourceCmd.Flags().BoolVar(&skipGenerateController, "skip-controller", false, "if set, the controller will not be generated")
createResourceCmd.Flags().BoolVar(&skipGenerateAdmissionController, "skip-admission-controller", false, "if set, the admission controller will not be generated")

cmd.AddCommand(createResourceCmd)
}
Expand All @@ -66,6 +72,23 @@ func RunCreateResource(cmd *cobra.Command, args []string) {
util.GetDomain()
ValidateResourceFlags()

reader := bufio.NewReader(os.Stdin)

if !cmd.Flag("skip-resource").Changed {
fmt.Println("Create Resource [y/n]")
skipGenerateResource = !Yesno(reader)
}

if !cmd.Flag("skip-controller").Changed {
fmt.Println("Create Controller [y/n]")
skipGenerateController = !Yesno(reader)
}

if !cmd.Flag("skip-admission-controller").Changed {
fmt.Println("Create Admission Controller [y/n]")
skipGenerateAdmissionController = !Yesno(reader)
}

cr := util.GetCopyright(copyright)

ignoreGroupExists = true
Expand Down Expand Up @@ -97,74 +120,76 @@ func createResource(boilerplate string) {

found := false

strategyFileName := fmt.Sprintf("%s_strategy.go", strings.ToLower(kindName))
unversionedPath := filepath.Join(dir, "pkg", "apis", groupName, strategyFileName)
created := util.WriteIfNotFound(unversionedPath, "unversioned-strategy-template", unversionedStrategyTemplate, a)
if !created {
if !found {
log.Printf("API group version kind %s/%s/%s already exists.",
groupName, versionName, kindName)
found = true
if !skipGenerateResource {
strategyFileName := fmt.Sprintf("%s_strategy.go", strings.ToLower(kindName))
unversionedPath := filepath.Join(dir, "pkg", "apis", groupName, strategyFileName)
created := util.WriteIfNotFound(unversionedPath, "unversioned-strategy-template", unversionedStrategyTemplate, a)
if !created {
if !found {
log.Printf("API group version kind %s/%s/%s already exists.",
groupName, versionName, kindName)
found = true
}
}
}

typesFileName := fmt.Sprintf("%s_types.go", strings.ToLower(kindName))
path := filepath.Join(dir, "pkg", "apis", groupName, versionName, typesFileName)
created = util.WriteIfNotFound(path, "versioned-resource-template", versionedResourceTemplate, a)
if !created {
if !found {
log.Printf("API group version kind %s/%s/%s already exists.",
groupName, versionName, kindName)
found = true
typesFileName := fmt.Sprintf("%s_types.go", strings.ToLower(kindName))
path := filepath.Join(dir, "pkg", "apis", groupName, versionName, typesFileName)
created = util.WriteIfNotFound(path, "versioned-resource-template", versionedResourceTemplate, a)
if !created {
if !found {
log.Printf("API group version kind %s/%s/%s already exists.",
groupName, versionName, kindName)
found = true
}
}
}

os.MkdirAll(filepath.Join("docs", "examples"), 0700)
docpath := filepath.Join("docs", "examples", strings.ToLower(kindName), fmt.Sprintf("%s.yaml", strings.ToLower(kindName)))
created = util.WriteIfNotFound(docpath, "example-template", exampleTemplate, a)
if !created {
if !found {
log.Printf("Example %s already exists.", docpath)
found = true
os.MkdirAll(filepath.Join("docs", "examples"), 0700)
docpath := filepath.Join("docs", "examples", strings.ToLower(kindName), fmt.Sprintf("%s.yaml", strings.ToLower(kindName)))
created = util.WriteIfNotFound(docpath, "example-template", exampleTemplate, a)
if !created {
if !found {
log.Printf("Example %s already exists.", docpath)
found = true
}
}
}

os.MkdirAll("sample", 0700)
samplepath := filepath.Join("sample", fmt.Sprintf("%s.yaml", strings.ToLower(kindName)))
created = util.WriteIfNotFound(samplepath, "sample-template", sampleTemplate, a)
if !created {
if !found {
log.Printf("Sample %s already exists.", docpath)
found = true
os.MkdirAll("sample", 0700)
samplepath := filepath.Join("sample", fmt.Sprintf("%s.yaml", strings.ToLower(kindName)))
created = util.WriteIfNotFound(samplepath, "sample-template", sampleTemplate, a)
if !created {
if !found {
log.Printf("Sample %s already exists.", docpath)
found = true
}
}
}

// write the suite if it is missing
typesFileName = fmt.Sprintf("%s_suite_test.go", strings.ToLower(versionName))
path = filepath.Join(dir, "pkg", "apis", groupName, versionName, typesFileName)
util.WriteIfNotFound(path, "version-suite-test-template", resourceSuiteTestTemplate, a)

typesFileName = fmt.Sprintf("%s_types_test.go", strings.ToLower(kindName))
path = filepath.Join(dir, "pkg", "apis", groupName, versionName, typesFileName)
created = util.WriteIfNotFound(path, "resource-test-template", resourceTestTemplate, a)
if !created {
if !found {
log.Printf("API group version kind %s/%s/%s test already exists.",
groupName, versionName, kindName)
found = true
// write the suite if it is missing
typesFileName = fmt.Sprintf("%s_suite_test.go", strings.ToLower(versionName))
path = filepath.Join(dir, "pkg", "apis", groupName, versionName, typesFileName)
util.WriteIfNotFound(path, "version-suite-test-template", resourceSuiteTestTemplate, a)

typesFileName = fmt.Sprintf("%s_types_test.go", strings.ToLower(kindName))
path = filepath.Join(dir, "pkg", "apis", groupName, versionName, typesFileName)
created = util.WriteIfNotFound(path, "resource-test-template", resourceTestTemplate, a)
if !created {
if !found {
log.Printf("API group version kind %s/%s/%s test already exists.",
groupName, versionName, kindName)
found = true
}
}
}

if generateAdmissionController {
if !skipGenerateAdmissionController {
// write the admission-controller initializer if it is missing
os.MkdirAll(filepath.Join("plugin", "admission"), 0700)
admissionInitializerFileName := "initializer.go"
path = filepath.Join(dir, "plugin", "admission", admissionInitializerFileName)
created = util.WriteIfNotFound(path, "admission-initializer-template", admissionControllerInitializerTemplate, a)
path := filepath.Join(dir, "plugin", "admission", admissionInitializerFileName)
created := util.WriteIfNotFound(path, "admission-initializer-template", admissionControllerInitializerTemplate, a)
if !created {
if !found {
log.Printf("admission initializer already exists.")
found = true
// found = true
}
}

Expand All @@ -181,56 +206,58 @@ func createResource(boilerplate string) {
}
}

// write controller-runtime scaffolding templates
r := &resource.Resource{
Namespaced: !nonNamespacedKind,
Group: groupName,
Version: versionName,
Kind: kindName,
Resource: resourceName,
}

err = (&scaffold.Scaffold{}).Execute(input.Options{
BoilerplatePath: "boilerplate.go.txt",
}, &Controller{
Resource: r,
Input: input.Input{
IfExistsAction: input.Skip,
},
})
if err != nil {
klog.Warningf("failed generating %v controller: %v", kindName, err)
}
if !skipGenerateController {
// write controller-runtime scaffolding templates
r := &resource.Resource{
Namespaced: !nonNamespacedKind,
Group: groupName,
Version: versionName,
Kind: kindName,
Resource: resourceName,
}

err = (&scaffold.Scaffold{}).Execute(input.Options{
BoilerplatePath: "boilerplate.go.txt",
},
&manager.Controller{
Input: input.Input{
IfExistsAction: input.Skip,
},
},
&controller.AddController{
err = (&scaffold.Scaffold{}).Execute(input.Options{
BoilerplatePath: "boilerplate.go.txt",
}, &Controller{
Resource: r,
Input: input.Input{
IfExistsAction: input.Skip,
},
})
if err != nil {
klog.Warningf("failed generating %v controller: %v", kindName, err)
}

err = (&scaffold.Scaffold{}).Execute(input.Options{
BoilerplatePath: "boilerplate.go.txt",
},
&SuiteTest{
Resource: r,
Input: input.Input{
IfExistsAction: input.Skip,
&manager.Controller{
Input: input.Input{
IfExistsAction: input.Skip,
},
},
},
&controller.Test{
Resource: r,
Input: input.Input{
IfExistsAction: input.Skip,
&controller.AddController{
Resource: r,
Input: input.Input{
IfExistsAction: input.Skip,
},
},
},
)
if err != nil {
klog.Warningf("failed generating controller basic packages: %v", err)
&SuiteTest{
Resource: r,
Input: input.Input{
IfExistsAction: input.Skip,
},
},
&controller.Test{
Resource: r,
Input: input.Input{
IfExistsAction: input.Skip,
},
},
)
if err != nil {
klog.Warningf("failed generating controller basic packages: %v", err)
}
}

if found {
Expand Down
28 changes: 28 additions & 0 deletions cmd/apiserver-boot/boot/create/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package create

import (
"bufio"
"fmt"
"log"
"regexp"
"strings"
Expand Down Expand Up @@ -66,3 +68,29 @@ func RegisterResourceFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&kindName, "kind", "", "name of the API kind. **Must be CamelCased (match ^[A-Z]+[A-Za-z0-9]*$)**")
cmd.Flags().StringVar(&resourceName, "resource", "", "optional name of the API resource, defaults to the plural name of the lowercase kind")
}

// Yesno reads from stdin looking for one of "y", "yes", "n", "no" and returns
// true for "y" and false for "n"
func Yesno(reader *bufio.Reader) bool {
for {
text := readstdin(reader)
switch text {
case "y", "yes":
return true
case "n", "no":
return false
default:
fmt.Printf("invalid input %q, should be [y/n]", text)
}
}
}

// Readstdin reads a line from stdin trimming spaces, and returns the value.
// log.Fatal's if there is an error.
func readstdin(reader *bufio.Reader) string {
text, err := reader.ReadString('\n')
if err != nil {
log.Fatalf("Error when reading input: %v", err)
}
return strings.TrimSpace(text)
}
10 changes: 6 additions & 4 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

all: test

NON_INTERACTIVE_FLAG=--skip-resource=false --skip-controller=false --skip-admission-controller=false

test: build
go test ./pkg/apis/...
go test ./pkg/controller/volumeclaim
Expand All @@ -26,10 +28,10 @@ test: build

skeleton: cmds
apiserver-boot init repo --domain sample.kubernetes.io
apiserver-boot create group version resource --group storage --version v1 --kind VolumeClaim
apiserver-boot create group version resource --group storage --version v1 --kind SnapshotClaim
apiserver-boot create group version resource --group storage --version v1 --kind Volume --non-namespaced
apiserver-boot create group version resource --group storage --version v1 --kind Snapshot --non-namespaced
apiserver-boot create group version resource --group storage --version v1 --kind VolumeClaim $(NON_INTERACTIVE_FLAG)
apiserver-boot create group version resource --group storage --version v1 --kind SnapshotClaim $(NON_INTERACTIVE_FLAG)
apiserver-boot create group version resource --group storage --version v1 --kind Volume --non-namespaced $(NON_INTERACTIVE_FLAG)
apiserver-boot create group version resource --group storage --version v1 --kind Snapshot --non-namespaced $(NON_INTERACTIVE_FLAG)

build: cmds skeleton
apiserver-boot build executables
Expand Down

0 comments on commit 916cc4e

Please sign in to comment.