From 6d0f20bc48213edceb3e57d40364aa79a9f7da6d Mon Sep 17 00:00:00 2001 From: Phillip Wittrock Date: Tue, 20 Mar 2018 22:23:36 -0700 Subject: [PATCH] Support generating docs in kubebuilder --- build/apiserverbuilder/Dockerfile | 34 +++ build/apiserverbuilder/docs.sh | 53 +++++ build/test.sh | 14 ++ build/thirdparty/darwin/Dockerfile | 14 ++ build/thirdparty/linux/Dockerfile | 14 ++ cmd/kubebuilder/build/build.go | 11 +- cmd/kubebuilder/build/build_executables.go | 99 -------- cmd/kubebuilder/build/docs.go | 215 ------------------ cmd/kubebuilder/create/create.go | 2 + cmd/kubebuilder/create/example/example.go | 100 ++++++++ cmd/kubebuilder/create/resource/run.go | 4 +- cmd/kubebuilder/docs/docs.go | 146 ++++++++++++ cmd/kubebuilder/docs/gen.go | 101 ++++++++ .../{build => generate}/generate.go | 17 +- cmd/kubebuilder/main.go | 14 +- 15 files changed, 507 insertions(+), 331 deletions(-) create mode 100644 build/apiserverbuilder/Dockerfile create mode 100755 build/apiserverbuilder/docs.sh delete mode 100644 cmd/kubebuilder/build/build_executables.go delete mode 100644 cmd/kubebuilder/build/docs.go create mode 100644 cmd/kubebuilder/create/example/example.go create mode 100644 cmd/kubebuilder/docs/docs.go create mode 100644 cmd/kubebuilder/docs/gen.go rename cmd/kubebuilder/{build => generate}/generate.go (97%) diff --git a/build/apiserverbuilder/Dockerfile b/build/apiserverbuilder/Dockerfile new file mode 100644 index 0000000000..c872a99fed --- /dev/null +++ b/build/apiserverbuilder/Dockerfile @@ -0,0 +1,34 @@ +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Build an image containing apiserver-builder for generating docs + +FROM golang:1.10-stretch + +ENV URL https://github.com/kubernetes-incubator/apiserver-builder/releases/download/v1.9-alpha.2 +ENV BIN apiserver-builder-v1.9-alpha.2-linux-amd64.tar.gz +ENV DEST /usr/local/apiserver-builder/bin/ +RUN curl -L $URL/$BIN -o /tmp/$BIN +RUN mkdir -p /usr/local/apiserver-builder +RUN tar -xzvf /tmp/$BIN -C /usr/local/apiserver-builder/ + +ENV PATH /usr/local/apiserver-builder/bin/:$PATH + +RUN apt-get update +RUN apt-get install less -y +RUN apt-get install nano -yu + +COPY docs.sh /usr/local/bin/docs.sh + +CMD docs.sh \ No newline at end of file diff --git a/build/apiserverbuilder/docs.sh b/build/apiserverbuilder/docs.sh new file mode 100755 index 0000000000..859581c5b1 --- /dev/null +++ b/build/apiserverbuilder/docs.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e +set -x + +# Copy over the files from the host repo +export D=$DIR/$OUTPUT +mkdir -p $D + +# Copy the api definitions +cp -r /host/repo/pkg $DIR/pkg + +# Copy the docs +if [ -d "/host/repo/$OUTPUT" ]; then + cp -r /host/repo/$OUTPUT/* $D +fi +if [ ! -d "$DIR/boilerplate.go.txt" ]; then + touch $DIR/boilerplate.go.txt +else + cp /host/repo/boilerplate.go.txt $DIR/boilerplate.go.txt +fi + +cd $DIR + +# Generate the artifacts +apiserver-boot init repo --domain $DOMAIN +apiserver-boot build generated clean +apiserver-boot build generated + +# Generate the input .md files for the docs +go build -o bin/apiserver cmd/apiserver/main.go +bin/apiserver --etcd-servers=http://localhost:2379 --secure-port=9443 --print-openapi --delegated-auth=false > $OUTPUT/openapi-spec/swagger.json +gen-apidocs --build-operations=false --use-tags=false --allow-errors --config-dir=$OUTPUT + +# Copy the input files to the host +if [ ! -d "/host/repo/$OUTPUT" ]; then + mkdir -p /host/repo/$OUTPUT +fi +cp -r $OUTPUT/* /host/repo/$OUTPUT diff --git a/build/test.sh b/build/test.sh index a5de5dcc7d..df320ff8b4 100755 --- a/build/test.sh +++ b/build/test.sh @@ -1,5 +1,19 @@ #!/usr/bin/env bash +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + cp -r /workspace/_output/kubebuilder /tmp/kubebuilder/ # Tests won't work on darwin diff --git a/build/thirdparty/darwin/Dockerfile b/build/thirdparty/darwin/Dockerfile index bd48db8d97..33346ac1fa 100644 --- a/build/thirdparty/darwin/Dockerfile +++ b/build/thirdparty/darwin/Dockerfile @@ -1,3 +1,17 @@ +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Build the following into binaries for darwin and then host them in a tar.gz file in an alpine image # - apiserver # - kubectl diff --git a/build/thirdparty/linux/Dockerfile b/build/thirdparty/linux/Dockerfile index 038c1662aa..c1936dcb51 100644 --- a/build/thirdparty/linux/Dockerfile +++ b/build/thirdparty/linux/Dockerfile @@ -1,3 +1,17 @@ +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Build the following into binaries for linux and then host them in a tar.gz file in an alpine image # - apiserver # - kubectl diff --git a/cmd/kubebuilder/build/build.go b/cmd/kubebuilder/build/build.go index 30e49f117b..302e05548f 100644 --- a/cmd/kubebuilder/build/build.go +++ b/cmd/kubebuilder/build/build.go @@ -18,6 +18,8 @@ package build import ( "github.com/spf13/cobra" + "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/docs" + "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/generate" ) var buildCmd = &cobra.Command{ @@ -25,21 +27,20 @@ var buildCmd = &cobra.Command{ Short: "Command group for building source into artifacts.", Long: `Command group for building source into artifacts.`, Example: `# Generate code and build the apiserver and controller-manager binaries into bin/ -kubebuilder build executables +kubebuilder build docs # Rebuild generated code kubebuilder build generated `, Run: RunBuild, + Deprecated: "`build generated` and `build docs` have been moved to `generate` and `docs`", } func AddBuild(cmd *cobra.Command) { cmd.AddCommand(buildCmd) - AddBuildExecutables(buildCmd) - // AddBuildContainer(buildCmd) - AddDocs(buildCmd) - AddGenerate(buildCmd) + buildCmd.AddCommand(docs.GetDocs()) + buildCmd.AddCommand(generate.GetGenerate()) } func RunBuild(cmd *cobra.Command, args []string) { diff --git a/cmd/kubebuilder/build/build_executables.go b/cmd/kubebuilder/build/build_executables.go deleted file mode 100644 index 489e05c3e9..0000000000 --- a/cmd/kubebuilder/build/build_executables.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package build - -import ( - "fmt" - "log" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/golang/glog" - "github.com/spf13/cobra" -) - -var GenerateForBuild = true -var goos = "linux" -var goarch = "amd64" -var outputdir = "bin" - -var createBuildExecutablesCmd = &cobra.Command{ - Use: "executables", - Short: "Builds the source into executables to run on the local machine", - Long: `Builds the source into executables to run on the local machine`, - Example: `# Generate code and build the apiserver and controller -# binaries in the bin directory so they can be run locally. -kubebuilder build executables - -# Build binaries into the linux/ directory using the cross compiler for linux:amd64 -kubebuilder build executables --goos linux --goarch amd64 --output linux/ - -# Regenerate Bazel BUILD files, and then build with bazel -# Must first install bazel and gazelle !!! -kubebuilder build executables --bazel --gazelle - -# RunInformersAndControllers Bazel without generating BUILD files -kubebuilder build executables --bazel - -# RunInformersAndControllers Bazel without generating BUILD files or generated code -kubebuilder build executables --bazel --generated=false -`, - Run: RunBuildExecutables, -} - -func AddBuildExecutables(cmd *cobra.Command) { - cmd.AddCommand(createBuildExecutablesCmd) - - createBuildExecutablesCmd.Flags().StringVar(&vendorDir, "vendor-dir", "", "Location of directory containing vendor files.") - createBuildExecutablesCmd.Flags().BoolVar(&GenerateForBuild, "generate", true, "if true, generate code before building") - createBuildExecutablesCmd.Flags().StringVar(&goos, "goos", "", "if specified, set this GOOS") - createBuildExecutablesCmd.Flags().StringVar(&goarch, "goarch", "", "if specified, set this GOARCH") - createBuildExecutablesCmd.Flags().StringVar(&outputdir, "output", "bin", "if set, write the binaries to this directory") -} - -func RunBuildExecutables(cmd *cobra.Command, args []string) { - GoBuild(cmd, args) -} - -func GoBuild(cmd *cobra.Command, args []string) { - if GenerateForBuild { - RunGenerate(cmd, args) - } - - os.RemoveAll(filepath.Join("bin", "controller-manager")) - - // Build the controller manager - path := filepath.Join("cmd", "controller-manager", "main.go") - c := exec.Command("go", "build", "-o", filepath.Join(outputdir, "controller-manager"), path) - c.Env = append(os.Environ(), "CGO_ENABLED=0") - if len(goos) > 0 { - c.Env = append(c.Env, fmt.Sprintf("GOOS=%s", goos)) - } - if len(goarch) > 0 { - c.Env = append(c.Env, fmt.Sprintf("GOARCH=%s", goarch)) - } - - glog.V(4).Infof("%s\n", strings.Join(c.Args, " ")) - c.Stderr = os.Stderr - c.Stdout = os.Stdout - err := c.Run() - if err != nil { - log.Fatal(err) - } -} diff --git a/cmd/kubebuilder/build/docs.go b/cmd/kubebuilder/build/docs.go deleted file mode 100644 index 4619cbfa28..0000000000 --- a/cmd/kubebuilder/build/docs.go +++ /dev/null @@ -1,215 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package build - -import ( - "bytes" - "fmt" - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/spf13/cobra" -) - -var docsCmd = &cobra.Command{ - Use: "docs", - Short: "Generate API reference docs from the openapi spec.", - Long: `Generate API reference docs from the openapi spec.`, - Example: `# Edit docs examples -nano -w docs/examples//API OVERVIEW - Add your markdown here - -# Add examples in the right-most column -# Edit docs/examples//.yaml -# e.g. docs/examples/pod/pod.yaml - - note: . - sample: | - apiVersion: - kind: - metadata: - name: - spec: - `, - Run: RunDocs, -} - -var operations, buildOpenapi, generateToc bool -var server string -var disableDelegatedAuth bool -var cleanup bool -var outputDir string - -func AddDocs(cmd *cobra.Command) { - docsCmd.Flags().StringVar(&server, "server", "bin/apiserver", "path to apiserver binary to run to get swagger.json") - docsCmd.Flags().BoolVar(&cleanup, "cleanup", true, "If true, cleanup intermediary files") - docsCmd.Flags().BoolVar(&buildOpenapi, "build-openapi", true, "If true, run the server and get the new swagger.json") - docsCmd.Flags().BoolVar(&operations, "operations", false, "if true, include operations in docs.") - docsCmd.Flags().BoolVar(&generateToc, "generate-toc", true, "If true, generate the table of contents from the api groups instead of using a statically configured ToC.") - docsCmd.Flags().StringVar(&outputDir, "output-dir", "docs", "Build docs into this directory") - cmd.AddCommand(docsCmd) - docsCmd.AddCommand(docsCleanCmd) -} - -var docsCleanCmd = &cobra.Command{ - Use: "clean", - Short: "Removes generated docs", - Long: `Removes generated docs`, - Example: ``, - Run: RunCleanDocs, -} - -func RunCleanDocs(cmd *cobra.Command, args []string) { - os.RemoveAll(filepath.Join(outputDir, "build")) - os.RemoveAll(filepath.Join(outputDir, "includes")) - os.Remove(filepath.Join(outputDir, "manifest.json")) -} - -func RunDocs(cmd *cobra.Command, args []string) { - if len(server) == 0 && buildOpenapi { - log.Fatal("Must specifiy --server or --build-openapi=false") - } - - os.RemoveAll(filepath.Join(outputDir, "includes")) - os.MkdirAll(filepath.Join(outputDir, "openapi-spec"), 0700) - os.MkdirAll(filepath.Join(outputDir, "static_includes"), 0700) - os.MkdirAll(filepath.Join(outputDir, "examples"), 0700) - - // Build the swagger.json - if buildOpenapi { - dir, err := os.Getwd() - if err != nil { - log.Fatal(err) - } - - path := filepath.Join(dir, "cmd", "apiserver", "main.go") - c := exec.Command("go", "build", "-o", filepath.Join("bin", "apiserver"), path) - fmt.Println(strings.Join(c.Args, " ")) - c.Stderr = os.Stderr - c.Stdout = os.Stdout - err = c.Run() - if err != nil { - log.Fatal(err) - } - - flags := []string{ - "--etcd-servers=http://localhost:2379", - "--secure-port=9443", - "--print-openapi", - "--delegated-auth=false", - } - - c = exec.Command(server, - flags..., - ) - log.Printf("%s\n", strings.Join(c.Args, " ")) - - var b bytes.Buffer - c.Stdout = &b - c.Stderr = os.Stderr - - err = c.Run() - if err != nil { - log.Fatalf("error: %v\n", err) - } - - err = ioutil.WriteFile(filepath.Join(outputDir, "openapi-spec", "swagger.json"), b.Bytes(), 0644) - if err != nil { - log.Fatalf("error: %v\n", err) - } - } - - // Build the docs - dir, err := os.Executable() - if err != nil { - log.Fatalf("error: %v\n", err) - } - dir = filepath.Dir(dir) - c := exec.Command(filepath.Join(dir, "gen-apidocs"), - fmt.Sprintf("--build-operations=%v", operations), - fmt.Sprintf("--use-tags=%v", generateToc), - "--allow-errors", - "--config-dir="+outputDir) - log.Printf("%s\n", strings.Join(c.Args, " ")) - c.Stderr = os.Stderr - c.Stdout = os.Stdout - err = c.Run() - if err != nil { - log.Fatalf("error: %v\n", err) - } - - wd, err := os.Getwd() - if err != nil { - log.Fatalf("error: %v\n", err) - } - - // RunInformersAndControllers the docker command to build the docs - c = exec.Command("docker", "run", - "-v", fmt.Sprintf("%s:%s", filepath.Join(wd, outputDir, "includes"), "/source"), - "-v", fmt.Sprintf("%s:%s", filepath.Join(wd, outputDir, "build"), "/build"), - "-v", fmt.Sprintf("%s:%s", filepath.Join(wd, outputDir, "build"), "/build"), - "-v", fmt.Sprintf("%s:%s", filepath.Join(wd, outputDir), "/manifest"), - "pwittrock/brodocs", - ) - log.Println(strings.Join(c.Args, " ")) - c.Stderr = os.Stderr - c.Stdout = os.Stdout - err = c.Run() - if err != nil { - log.Fatalf("error: %v\n", err) - } - - // Cleanup intermediate files - if cleanup { - os.RemoveAll(filepath.Join(wd, outputDir, "includes")) - os.RemoveAll(filepath.Join(wd, outputDir, "manifest.json")) - os.RemoveAll(filepath.Join(wd, outputDir, "openapi-spec")) - os.RemoveAll(filepath.Join(wd, outputDir, "build", "documents")) - os.RemoveAll(filepath.Join(wd, outputDir, "build", "documents")) - os.RemoveAll(filepath.Join(wd, outputDir, "build", "runbrodocs.sh")) - os.RemoveAll(filepath.Join(wd, outputDir, "build", "node_modules", "marked", "Makefile")) - } -} diff --git a/cmd/kubebuilder/create/create.go b/cmd/kubebuilder/create/create.go index fd3ecc9bdb..56ad90b1b6 100644 --- a/cmd/kubebuilder/create/create.go +++ b/cmd/kubebuilder/create/create.go @@ -18,6 +18,7 @@ package create import ( "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/create/config" + "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/create/example" "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/create/resource" "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/create/util" "github.com/spf13/cobra" @@ -39,6 +40,7 @@ func AddCreate(cmd *cobra.Command) { util.RegisterCopyrightFlag(cmd) resource.AddCreateResource(createCmd) config.AddCreateConfig(createCmd) + example.AddCreateExample(createCmd) } func RunCreate(cmd *cobra.Command, args []string) { diff --git a/cmd/kubebuilder/create/example/example.go b/cmd/kubebuilder/create/example/example.go new file mode 100644 index 0000000000..d22d0465f1 --- /dev/null +++ b/cmd/kubebuilder/create/example/example.go @@ -0,0 +1,100 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package example + +import ( + "fmt" + "log" + "os" + "path/filepath" + + "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/util" + "github.com/spf13/cobra" + "strings" +) + +// configCmd represents the config command +var configCmd = &cobra.Command{ + Use: "example", + Short: "Create the docs example scaffolding for an API", + Long: `Create the docs example scaffoling for an API. + +Example is written to docs/reference/examples//.yaml +`, + Run: func(cmd *cobra.Command, args []string) { + if kind == "" { + fmt.Printf("Must specify --kind\n") + return + } + if version == "" { + fmt.Printf("Must specify --name\n") + return + } + if group == "" { + fmt.Printf("Must specify --group\n") + return + } + wd, err := os.Getwd() + if err != nil { + log.Fatalf("error: %v\n", err) + } + path := filepath.Join(wd, outputDir, "examples", strings.ToLower(kind), strings.ToLower(kind)+".yaml") + CodeGenerator{}.Execute(path) + fmt.Printf("Edit your controller function...\n") + fmt.Printf("\t%s\n", path) + }, +} + +var kind, version, group, outputDir string + +func AddCreateExample(cmd *cobra.Command) { + cmd.AddCommand(configCmd) + configCmd.Flags().StringVar(&kind, "kind", "", "api Kind.") + configCmd.Flags().StringVar(&group, "group", "", "api group.") + configCmd.Flags().StringVar(&version, "version", "", "api version.") + configCmd.Flags().StringVar(&outputDir, "output-dir", filepath.Join("docs", "reference"), "reference docs location") + +} + +// CodeGenerator generates code for Kubernetes resources and controllers +type CodeGenerator struct{} + +// Execute parses packages and executes the code generators against the resource and controller packages +func (g CodeGenerator) Execute(path string) error { + os.Remove(path) + args := ConfigArgs{ + Group: group, + Version: version, + Kind: strings.ToLower(kind), + } + util.WriteIfNotFound(path, "example-config-template", exampleConfigTemplate, args) + return nil +} + +type ConfigArgs struct { + Group, Version, Kind string +} + +var exampleConfigTemplate = `note: {{ .Kind }} example +sample: | + apiVersion: {{ .Version }} + kind: {{ .Kind }} + metadata: + name: {{ lower .Kind }} + spec: + todo: "write me" +` diff --git a/cmd/kubebuilder/create/resource/run.go b/cmd/kubebuilder/create/resource/run.go index f13e438447..463a576bdc 100644 --- a/cmd/kubebuilder/create/resource/run.go +++ b/cmd/kubebuilder/create/resource/run.go @@ -21,7 +21,7 @@ import ( "log" "os" - "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/build" + generatecmd "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/generate" createutil "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/create/util" "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/util" "github.com/markbates/inflect" @@ -81,7 +81,7 @@ func RunCreateResource(cmd *cobra.Command, args []string) { if generate { fmt.Printf("Generating code for new resource... " + "Regenerate after editing resources files by running `kubebuilder build generated`.\n") - build.RunGenerate(cmd, args) + generatecmd.RunGenerate(cmd, args) } fmt.Printf("Next: Install the API, run the controller and create an instance with:\n" + "$ GOBIN=$(pwd)/bin go install /cmd/controller-manager\n" + diff --git a/cmd/kubebuilder/docs/docs.go b/cmd/kubebuilder/docs/docs.go new file mode 100644 index 0000000000..f9ed9fcbf7 --- /dev/null +++ b/cmd/kubebuilder/docs/docs.go @@ -0,0 +1,146 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package docs + +import ( + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/util" + "github.com/spf13/cobra" +) + +var docsCmd = &cobra.Command{ + Use: "docs", + Short: "Generate API reference docs.", + Long: `Generate API reference docs. + +Documentation will be written to docs/reference/build/index.html + +For creating documentation examples see "kubebuilder create example" +`, + Example: `# Build docs/build/index.html +kubebuilder docs + +# Add examples in the right-most column then rebuild the docs +kubebuilder create example --group group --version version --kind kind +nano -w docs/reference/examples/kind/kind.yaml +kubebuilder docs + + +# Add manual documentation to the generated reference docs by updating the header .md files +# Edit docs/reference/static_includes/*.md +# e.g. docs/reference/static_include/_overview.md + + # API OVERVIEW + Add your markdown here +`, + Run: RunDocs, +} + +var generateConfig bool +var cleanup bool +var outputDir string + +func AddDocs(cmd *cobra.Command) { + docsCmd.Flags().BoolVar(&cleanup, "cleanup", true, "If true, cleanup intermediary files") + docsCmd.Flags().BoolVar(&generateConfig, "generate-config", true, "If true, generate the docs/reference/config.yaml.") + docsCmd.Flags().StringVar(&outputDir, "output-dir", filepath.Join("docs", "reference"), "Build docs into this directory") + cmd.AddCommand(docsCmd) + docsCmd.AddCommand(docsCleanCmd) +} + +func GetDocs() *cobra.Command { + return docsCmd +} + +var docsCleanCmd = &cobra.Command{ + Use: "clean", + Short: "Removes generated docs", + Long: `Removes generated docs`, + Example: ``, + Run: RunCleanDocs, +} + +func RunCleanDocs(cmd *cobra.Command, args []string) { + os.RemoveAll(filepath.Join(outputDir, "build")) + os.RemoveAll(filepath.Join(outputDir, "includes")) + os.Remove(filepath.Join(outputDir, "manifest.json")) +} + +func RunDocs(cmd *cobra.Command, args []string) { + os.RemoveAll(filepath.Join(outputDir, "includes")) + os.MkdirAll(filepath.Join(outputDir, "openapi-spec"), 0700) + os.MkdirAll(filepath.Join(outputDir, "static_includes"), 0700) + os.MkdirAll(filepath.Join(outputDir, "examples"), 0700) + + wd, err := os.Getwd() + if err != nil { + log.Fatalf("error: %v\n", err) + } + + if generateConfig { + CodeGenerator{}.Execute(wd) + } + + // Run the docker command to build the docs + c := exec.Command("docker", "run", + "-v", fmt.Sprintf("%s:%s", filepath.Join(wd), "/host/repo"), + "-e", "DOMAIN="+util.GetDomain(), + "-e", "DIR="+filepath.Join("src", util.Repo), + "-e", "OUTPUT="+outputDir, + "gcr.io/kubebuilder/gendocs", + ) + log.Println(strings.Join(c.Args, " ")) + c.Stderr = os.Stderr + c.Stdout = os.Stdout + err = c.Run() + if err != nil { + log.Fatalf("error: %v\n", err) + } + + // Run the docker command to build the docs + c = exec.Command("docker", "run", + "-v", fmt.Sprintf("%s:%s", filepath.Join(wd, outputDir, "includes"), "/source"), + "-v", fmt.Sprintf("%s:%s", filepath.Join(wd, outputDir, "build"), "/build"), + "-v", fmt.Sprintf("%s:%s", filepath.Join(wd, outputDir, "build"), "/build"), + "-v", fmt.Sprintf("%s:%s", filepath.Join(wd, outputDir), "/manifest"), + "gcr.io/kubebuilder/brodocs", + ) + log.Println(strings.Join(c.Args, " ")) + c.Stderr = os.Stderr + c.Stdout = os.Stdout + err = c.Run() + if err != nil { + log.Fatalf("error: %v\n", err) + } + + // Cleanup intermediate files + if cleanup { + os.RemoveAll(filepath.Join(wd, outputDir, "includes")) + os.RemoveAll(filepath.Join(wd, outputDir, "manifest.json")) + os.RemoveAll(filepath.Join(wd, outputDir, "openapi-spec")) + os.RemoveAll(filepath.Join(wd, outputDir, "build", "documents")) + os.RemoveAll(filepath.Join(wd, outputDir, "build", "documents")) + os.RemoveAll(filepath.Join(wd, outputDir, "build", "runbrodocs.sh")) + os.RemoveAll(filepath.Join(wd, outputDir, "build", "node_modules", "marked", "Makefile")) + } +} diff --git a/cmd/kubebuilder/docs/gen.go b/cmd/kubebuilder/docs/gen.go new file mode 100644 index 0000000000..3aa7c61990 --- /dev/null +++ b/cmd/kubebuilder/docs/gen.go @@ -0,0 +1,101 @@ +package docs + +import ( + "fmt" + "path/filepath" + "sort" + "strings" + + "github.com/kubernetes-sigs/kubebuilder/cmd/internal/codegen/parse" + "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/util" + "k8s.io/gengo/args" + "os" +) + +// CodeGenerator generates code for Kubernetes resources and controllers +type CodeGenerator struct{} + +// Execute parses packages and executes the code generators against the resource and controller packages +func (g CodeGenerator) Execute(dir string) error { + arguments := args.Default() + b, err := arguments.NewBuilder() + if err != nil { + return fmt.Errorf("Failed making a parser: %v", err) + } + for _, d := range []string{"./pkg/apis", "./pkg/controller", "./pkg/inject"} { + if err := b.AddDirRecursive(d); err != nil { + return fmt.Errorf("Failed making a parser: %v", err) + } + } + c, err := parse.NewContext(b) + if err != nil { + return fmt.Errorf("Failed making a context: %v", err) + } + + p := parse.NewAPIs(c, arguments) + path := filepath.Join(dir, outputDir, "config.yaml") + + args := ConfigArgs{} + for group, kindversion := range p.ByGroupKindVersion { + args.Groups = append(args.Groups, strings.Title(group)) + c := Category{ + Name: strings.Title(group), + Include: strings.ToLower(group), + } + m := map[string]Resource{} + s := []string{} + for kind, version := range kindversion { + r := Resource{ + Group: group, + Kind: kind, + } + vs := []string{} + for version := range version { + vs = append(vs, version) + } + sort.Strings(vs) + r.Version = vs[0] + m[r.Kind] = r + s = append(s, r.Kind) + } + // Sort the resources by name + sort.Strings(s) + for _, k := range s { + c.Resources = append(c.Resources, m[k]) + } + + args.Categories = append(args.Categories, c) + } + + os.Remove(path) + util.Write(path, "docs-config-template", docsConfigTemplate, args) + return nil +} + +type ConfigArgs struct { + Groups []string + Categories []Category +} + +type Category struct { + Name string + Include string + Resources []Resource +} + +type Resource struct { + Kind, Version, Group string +} + +var docsConfigTemplate = `example_location: "examples" +api_groups: {{ range $group := .Groups }} + - "{{ $group }}" +{{ end -}} +resource_categories: {{ range $category := .Categories }} +- name: "{{ $category.Name }}" + include: "{{ $category.Include}}" + resources: {{ range $resource := $category.Resources }} + - name: "{{ $resource.Kind }}" + version: "{{ $resource.Version }}" + group: "{{ $resource.Group }}"{{ end }} +{{ end }}` diff --git a/cmd/kubebuilder/build/generate.go b/cmd/kubebuilder/generate/generate.go similarity index 97% rename from cmd/kubebuilder/build/generate.go rename to cmd/kubebuilder/generate/generate.go index de0da390b0..2d70dc6487 100644 --- a/cmd/kubebuilder/build/generate.go +++ b/cmd/kubebuilder/generate/generate.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package build +package generate import ( "io/ioutil" @@ -39,11 +39,12 @@ var generators = sets.String{} var vendorDir string var generateCmd = &cobra.Command{ - Use: "generated", - Short: "RunInformersAndControllers code generators against repo.", - Long: `Automatically run by most build commands. Writes generated source code for a repo.`, - Example: `# RunInformersAndControllers code generators. -kubebuilder build generated`, + Use: "generate", + Aliases: []string{"generated"}, + Short: "Run code generators.", + Long: `Run code generators`, + Example: `# Run code generators. +kubebuilder generate`, Run: RunGenerate, } @@ -61,6 +62,10 @@ func AddGenerate(cmd *cobra.Command) { generateCmd.AddCommand(generateCleanCmd) } +func GetGenerate() *cobra.Command { + return generateCmd +} + var generateCleanCmd = &cobra.Command{ Use: "clean", Short: "Removes generated source code", diff --git a/cmd/kubebuilder/main.go b/cmd/kubebuilder/main.go index c72fb19a78..7c89959ded 100644 --- a/cmd/kubebuilder/main.go +++ b/cmd/kubebuilder/main.go @@ -26,6 +26,8 @@ import ( "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/build" "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/create" + "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/docs" + "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/generate" "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/initproject" "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/update" "github.com/kubernetes-sigs/kubebuilder/cmd/kubebuilder/util" @@ -51,10 +53,11 @@ func main() { } util.Repo = strings.Replace(wd, util.GoSrc+string(filepath.Separator), "", 1) - initproject.AddInit(cmd) - create.AddCreate(cmd) build.AddBuild(cmd) - // run.AddRun(cmd) + create.AddCreate(cmd) + docs.AddDocs(cmd) + generate.AddGenerate(cmd) + initproject.AddInit(cmd) update.AddUpdate(cmd) version.AddVersion(cmd) @@ -103,11 +106,14 @@ Typical project lifecycle: More options: - run tests - kubebuilder build generated + kubebuilder generate export TEST_ASSET_KUBECTL=/usr/local/kubebuilder/bin/kubectl export TEST_ASSET_KUBE_APISERVER=/usr/local/kubebuilder/bin/kube-apiserver export TEST_ASSET_ETCD=/usr/local/kubebuilder/bin/etcd go test ./pkg/... + +- build reference documentation to docs/reference/build/index.html + kubebuilder docs `, Example: `# Initialize your project kubebuilder init --domain example.com