diff --git a/cmd/genbashcomp/gen_kubectl_bash_comp.go b/cmd/genbashcomp/gen_kubectl_bash_comp.go new file mode 100644 index 00000000000..3db7688e552 --- /dev/null +++ b/cmd/genbashcomp/gen_kubectl_bash_comp.go @@ -0,0 +1,49 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 main + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/GoogleCloudPlatform/kubernetes/cmd/genutils" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd" + cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" +) + +func main() { + // use os.Args instead of "flags" because "flags" will mess up the man pages! + path := "contrib/completions/bash/" + if len(os.Args) == 2 { + path = os.Args[1] + } else if len(os.Args) > 2 { + fmt.Fprintf(os.Stderr, "usage: %s [output directory]\n", os.Args[0]) + os.Exit(1) + } + + outDir, err := genutils.OutDir(path) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err) + os.Exit(1) + } + outFile := outDir + "kubectl" + + //TODO os.Stdin should really be something like ioutil.Discard, but a Reader + kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) + kubectl.GenBashCompletionFile(outFile) +} diff --git a/cmd/gendocs/gen_kubectl_docs.go b/cmd/gendocs/gen_kubectl_docs.go index d345d550c56..1ab18689458 100644 --- a/cmd/gendocs/gen_kubectl_docs.go +++ b/cmd/gendocs/gen_kubectl_docs.go @@ -20,8 +20,8 @@ import ( "fmt" "io/ioutil" "os" - "path/filepath" + "github.com/GoogleCloudPlatform/kubernetes/cmd/genutils" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/spf13/cobra" @@ -29,36 +29,24 @@ import ( func main() { // use os.Args instead of "flags" because "flags" will mess up the man pages! - docsDir := "docs/man/man1/" + path := "docs/" if len(os.Args) == 2 { - docsDir = os.Args[1] + path = os.Args[1] } else if len(os.Args) > 2 { fmt.Fprintf(os.Stderr, "usage: %s [output directory]\n", os.Args[0]) - return + os.Exit(1) } - docsDir, err := filepath.Abs(docsDir) + outDir, err := genutils.OutDir(path) if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - return + fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err) + os.Exit(1) } - stat, err := os.Stat(docsDir) - if err != nil { - fmt.Fprintf(os.Stderr, "output directory %s does not exist\n", docsDir) - return - } - - if !stat.IsDir() { - fmt.Fprintf(os.Stderr, "output directory %s is not a directory\n", docsDir) - return - } - docsDir = docsDir + "/" - // Set environment variables used by kubectl so the output is consistent, // regardless of where we run. os.Setenv("HOME", "/home/username") //TODO os.Stdin should really be something like ioutil.Discard, but a Reader kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) - cobra.GenMarkdownTree(kubectl, docsDir) + cobra.GenMarkdownTree(kubectl, outDir) } diff --git a/cmd/genman/gen_kubectl_man.go b/cmd/genman/gen_kubectl_man.go index 80a844100b9..84bac5e6a83 100644 --- a/cmd/genman/gen_kubectl_man.go +++ b/cmd/genman/gen_kubectl_man.go @@ -21,9 +21,9 @@ import ( "fmt" "io/ioutil" "os" - "path/filepath" "strings" + "github.com/GoogleCloudPlatform/kubernetes/cmd/genutils" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" "github.com/cpuguy83/go-md2man/mangen" @@ -34,40 +34,28 @@ import ( func main() { // use os.Args instead of "flags" because "flags" will mess up the man pages! - docsDir := "docs/man/man1/" + path := "docs/man/man1" if len(os.Args) == 2 { - docsDir = os.Args[1] + path = os.Args[1] } else if len(os.Args) > 2 { fmt.Fprintf(os.Stderr, "usage: %s [output directory]\n", os.Args[0]) os.Exit(1) } - docsDir, err := filepath.Abs(docsDir) + outDir, err := genutils.OutDir(path) if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) + fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err) os.Exit(1) } - stat, err := os.Stat(docsDir) - if err != nil { - fmt.Fprintf(os.Stderr, "output directory %s does not exist\n", docsDir) - os.Exit(1) - } - - if !stat.IsDir() { - fmt.Fprintf(os.Stderr, "output directory %s is not a directory\n", docsDir) - os.Exit(1) - } - docsDir = docsDir + "/" - // Set environment variables used by kubectl so the output is consistent, // regardless of where we run. os.Setenv("HOME", "/home/username") //TODO os.Stdin should really be something like ioutil.Discard, but a Reader kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) - genMarkdown(kubectl, "", docsDir) + genMarkdown(kubectl, "", outDir) for _, c := range kubectl.Commands() { - genMarkdown(c, "kubectl", docsDir) + genMarkdown(c, "kubectl", outDir) } } diff --git a/cmd/genutils/genutils.go b/cmd/genutils/genutils.go new file mode 100644 index 00000000000..b772819a37b --- /dev/null +++ b/cmd/genutils/genutils.go @@ -0,0 +1,41 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 genutils + +import ( + "fmt" + "os" + "path/filepath" +) + +func OutDir(path string) (string, error) { + outDir, err := filepath.Abs(path) + if err != nil { + return "", err + } + + stat, err := os.Stat(outDir) + if err != nil { + return "", err + } + + if !stat.IsDir() { + return "", fmt.Errorf("output directory %s is not a directory\n", outDir) + } + outDir = outDir + "/" + return outDir, nil +} diff --git a/cmd/genutils/genutils_test.go b/cmd/genutils/genutils_test.go new file mode 100644 index 00000000000..56f8de67eb0 --- /dev/null +++ b/cmd/genutils/genutils_test.go @@ -0,0 +1,42 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 genutils + +import ( + "testing" +) + +func TestValidDir(t *testing.T) { + _, err := OutDir("./") + if err != nil { + t.Fatal(err) + } +} + +func TestInvalidDir(t *testing.T) { + _, err := OutDir("./nondir") + if err == nil { + t.Fatal(err) + } +} + +func TestNotDir(t *testing.T) { + _, err := OutDir("./genutils_test.go") + if err == nil { + t.Fatal(err) + } +} diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 4cde434f47e..201d9acea2d 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -149,73 +149,6 @@ __handle_word() __handle_word } -# call kubectl get $1, -# use the first column in compgen -# we could use templates, but then would need a template per resource -__kubectl_parse_get() -{ - local kubectl_output out - if kubectl_output=$(kubectl get --no-headers "$1" 2>/dev/null); then - out=($(echo "${kubectl_output}" | awk '{print $1}')) - COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) - fi -} - -__kubectl_get_resource() -{ - if [[ ${#nouns[@]} -eq 0 ]]; then - return 1 - fi - __kubectl_parse_get ${nouns[${#nouns[@]} -1]} - if [[ $? -eq 0 ]]; then - return 0 - fi -} - -# $1 is the name of the pod we want to get the list of containers inside -__kubectl_get_containers() -{ - local template - template="{{ range .desiredState.manifest.containers }}{{ .name }} {{ end }}" - __debug ${FUNCNAME} "nouns are ${nouns[@]}" - - local len="${#nouns[@]}" - if [[ ${len} -ne 1 ]]; then - return - fi - local last=${nouns[${len} -1]} - local kubectl_out - if kubectl_out=$(kubectl get -o template --template="${template}" pods "${last}" 2>/dev/null); then - COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) ) - fi -} - -# Require both a pod and a container to be specified -__kubectl_require_pod_and_container() -{ - if [[ ${#nouns[@]} -eq 0 ]]; then - __kubectl_parse_get pods - return 0 - fi; - __kubectl_get_containers - return 0 -} - -__custom_func() { - case ${last_command} in - kubectl_get | kubectl_describe | kubectl_delete | kubectl_stop) - __kubectl_get_resource - return - ;; - kubectl_log) - __kubectl_require_pod_and_container - return - ;; - *) - ;; - esac -} - _kubectl_get() { last_command="kubectl_get" @@ -242,19 +175,6 @@ _kubectl_get() must_have_one_flag=() must_have_one_noun=() - must_have_one_noun+=("limitrange") - must_have_one_noun+=("resourcequota") - must_have_one_noun+=("persistentvolume") - must_have_one_noun+=("service") - must_have_one_noun+=("event") - must_have_one_noun+=("namespace") - must_have_one_noun+=("pod") - must_have_one_noun+=("secret") - must_have_one_noun+=("replicationcontroller") - must_have_one_noun+=("node") - must_have_one_noun+=("status") - must_have_one_noun+=("persistentvolumeclaim") - must_have_one_noun+=("endpoints") } _kubectl_describe() @@ -272,15 +192,6 @@ _kubectl_describe() must_have_one_flag=() must_have_one_noun=() - must_have_one_noun+=("persistentvolume") - must_have_one_noun+=("persistentvolumeclaim") - must_have_one_noun+=("pod") - must_have_one_noun+=("service") - must_have_one_noun+=("node") - must_have_one_noun+=("limitrange") - must_have_one_noun+=("resourcequota") - must_have_one_noun+=("replicationcontroller") - must_have_one_noun+=("minion") } _kubectl_create() @@ -294,17 +205,11 @@ _kubectl_create() flags_completion=() flags+=("--filename=") - flags_with_completion+=("--filename") - flags_completion+=("_filedir '@(json|yaml|yml)'") two_word_flags+=("-f") - flags_with_completion+=("-f") - flags_completion+=("_filedir '@(json|yaml|yml)'") flags+=("--help") flags+=("-h") must_have_one_flag=() - must_have_one_flag+=("--filename=") - must_have_one_flag+=("-f") must_have_one_noun=() } @@ -319,19 +224,12 @@ _kubectl_update() flags_completion=() flags+=("--filename=") - flags_with_completion+=("--filename") - flags_completion+=("_filedir '@(json|yaml|yml)'") two_word_flags+=("-f") - flags_with_completion+=("-f") - flags_completion+=("_filedir '@(json|yaml|yml)'") flags+=("--help") flags+=("-h") flags+=("--patch=") must_have_one_flag=() - must_have_one_flag+=("--filename=") - must_have_one_flag+=("-f") - must_have_one_flag+=("--patch=") must_have_one_noun=() } @@ -347,11 +245,7 @@ _kubectl_delete() flags+=("--all") flags+=("--filename=") - flags_with_completion+=("--filename") - flags_completion+=("_filedir '@(json|yaml|yml)'") two_word_flags+=("-f") - flags_with_completion+=("-f") - flags_completion+=("_filedir '@(json|yaml|yml)'") flags+=("--help") flags+=("-h") flags+=("--selector=") @@ -409,11 +303,7 @@ _kubectl_rolling-update() flags_completion=() flags+=("--filename=") - flags_with_completion+=("--filename") - flags_completion+=("_filedir '@(json|yaml|yml)'") two_word_flags+=("-f") - flags_with_completion+=("-f") - flags_completion+=("_filedir '@(json|yaml|yml)'") flags+=("--help") flags+=("-h") flags+=("--poll-interval=") @@ -421,8 +311,6 @@ _kubectl_rolling-update() flags+=("--update-period=") must_have_one_flag=() - must_have_one_flag+=("--filename=") - must_have_one_flag+=("-f") must_have_one_noun=() } @@ -443,7 +331,6 @@ _kubectl_resize() flags+=("--resource-version=") must_have_one_flag=() - must_have_one_flag+=("--replicas=") must_have_one_noun=() } @@ -469,10 +356,6 @@ _kubectl_exec() flags+=("-t") must_have_one_flag=() - must_have_one_flag+=("--container=") - must_have_one_flag+=("-c") - must_have_one_flag+=("--pod=") - must_have_one_flag+=("-p") must_have_one_noun=() } @@ -492,8 +375,6 @@ _kubectl_port-forward() two_word_flags+=("-p") must_have_one_flag=() - must_have_one_flag+=("--pod=") - must_have_one_flag+=("-p") must_have_one_noun=() } @@ -550,7 +431,6 @@ _kubectl_run-container() two_word_flags+=("-t") must_have_one_flag=() - must_have_one_flag+=("--image=") must_have_one_noun=() } @@ -566,11 +446,7 @@ _kubectl_stop() flags+=("--all") flags+=("--filename=") - flags_with_completion+=("--filename") - flags_completion+=("_filedir '@(json|yaml|yml)'") two_word_flags+=("-f") - flags_with_completion+=("-f") - flags_completion+=("_filedir '@(json|yaml|yml)'") flags+=("--help") flags+=("-h") flags+=("--selector=") @@ -613,7 +489,6 @@ _kubectl_expose() two_word_flags+=("-t") must_have_one_flag=() - must_have_one_flag+=("--port=") must_have_one_noun=() } diff --git a/hack/lib/util.sh b/hack/lib/util.sh index 980f77dda5f..60a9fac0471 100644 --- a/hack/lib/util.sh +++ b/hack/lib/util.sh @@ -96,6 +96,18 @@ kube::util::host_platform() { echo "${host_os}/${host_arch}" } +kube::util::find-binary() { + local lookfor="${1}" + local host_platform="$(kube::util::host_platform)" + local locations=( + "${KUBE_ROOT}/_output/dockerized/bin/${host_platform}/${lookfor}" + "${KUBE_ROOT}/_output/local/bin/${host_platform}/${lookfor}" + "${KUBE_ROOT}/platforms/${host_platform}/${lookfor}" + ) + local bin=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 ) + echo -n "${bin}" +} + # Wait for background jobs to finish. Return with # an error status if any of the jobs failed. kube::util::wait-for-jobs() { diff --git a/hack/run-gendocs.sh b/hack/run-gendocs.sh index 912ce64a9c3..3c922788a0e 100755 --- a/hack/run-gendocs.sh +++ b/hack/run-gendocs.sh @@ -22,7 +22,7 @@ KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. source "${KUBE_ROOT}/hack/lib/init.sh" kube::golang::setup_env -"${KUBE_ROOT}/hack/build-go.sh" cmd/gendocs cmd/genman +"${KUBE_ROOT}/hack/build-go.sh" cmd/gendocs cmd/genman cmd/genbashcomp # Get the absolute path of the directory component of a file, i.e. the # absolute path of the dirname of $1. @@ -30,65 +30,21 @@ get_absolute_dirname() { echo "$(cd "$(dirname "$1")" && pwd)" } -# Detect the OS name/arch so that we can find our binary -case "$(uname -s)" in - Darwin) - host_os=darwin - ;; - Linux) - host_os=linux - ;; - *) - echo "Unsupported host OS. Must be Linux or Mac OS X." >&2 - exit 1 - ;; -esac - -case "$(uname -m)" in - x86_64*) - host_arch=amd64 - ;; - i?86_64*) - host_arch=amd64 - ;; - amd64*) - host_arch=amd64 - ;; - arm*) - host_arch=arm - ;; - i?86*) - host_arch=x86 - ;; - *) - echo "Unsupported host arch. Must be x86_64, 386 or arm." >&2 - exit 1 - ;; -esac - # Find binary -doc_locations=( - "${KUBE_ROOT}/_output/dockerized/bin/${host_os}/${host_arch}/gendocs" - "${KUBE_ROOT}/_output/local/bin/${host_os}/${host_arch}/gendocs" - "${KUBE_ROOT}/platforms/${host_os}/${host_arch}/gendocs" -) -gendocs=$( (ls -t "${doc_locations[@]}" 2>/dev/null || true) | head -1 ) -man_locations=( - "${KUBE_ROOT}/_output/dockerized/bin/${host_os}/${host_arch}/genman" - "${KUBE_ROOT}/_output/local/bin/${host_os}/${host_arch}/genman" - "${KUBE_ROOT}/platforms/${host_os}/${host_arch}/genman" -) -genman=$( (ls -t "${man_locations[@]}" 2>/dev/null || true) | head -1 ) +gendocs=$(kube::util::find-binary "gendocs") +genman=$(kube::util::find-binary "genman") +genbashcomp=$(kube::util::find-binary "genbashcomp") -if [[ ! -x "$gendocs" || ! -x "$genman" ]]; then +if [[ ! -x "$gendocs" || ! -x "$genman" || ! -x "$genbashcomp" ]]; then { - echo "It looks as if you don't have a compiled gendocs or genman binary" + echo "It looks as if you don't have a compiled gendocs, genman, or genbashcomp binary" echo echo "If you are running from a clone of the git repo, please run" - echo "'./hack/build-go.sh cmd/gendocs cmd/genman'." + echo "'./hack/build-go.sh cmd/gendocs cmd/genman cmd/genbashcomp'." } >&2 exit 1 fi ${gendocs} "${KUBE_ROOT}/docs/" ${genman} "${KUBE_ROOT}/docs/man/man1/" +${genbashcomp} "${KUBE_ROOT}/contrib/completions/bash/" diff --git a/hack/verify-gendocs.sh b/hack/verify-gendocs.sh index 33ba6babf60..77c1a79c471 100755 --- a/hack/verify-gendocs.sh +++ b/hack/verify-gendocs.sh @@ -22,7 +22,7 @@ KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. source "${KUBE_ROOT}/hack/lib/init.sh" kube::golang::setup_env -"${KUBE_ROOT}/hack/build-go.sh" cmd/gendocs cmd/genman +"${KUBE_ROOT}/hack/build-go.sh" cmd/gendocs cmd/genman cmd/genbashcomp # Get the absolute path of the directory component of a file, i.e. the # absolute path of the dirname of $1. @@ -30,63 +30,16 @@ get_absolute_dirname() { echo "$(cd "$(dirname "$1")" && pwd)" } -# Detect the OS name/arch so that we can find our binary -case "$(uname -s)" in - Darwin) - host_os=darwin - ;; - Linux) - host_os=linux - ;; - *) - echo "Unsupported host OS. Must be Linux or Mac OS X." >&2 - exit 1 - ;; -esac +gendocs=$(kube::util::find-binary "gendocs") +genman=$(kube::util::find-binary "genman") +genbashcomp=$(kube::util::find-binary "genbashcomp") -case "$(uname -m)" in - x86_64*) - host_arch=amd64 - ;; - i?86_64*) - host_arch=amd64 - ;; - amd64*) - host_arch=amd64 - ;; - arm*) - host_arch=arm - ;; - i?86*) - host_arch=x86 - ;; - *) - echo "Unsupported host arch. Must be x86_64, 386 or arm." >&2 - exit 1 - ;; -esac - -# Find binary -locations=( - "${KUBE_ROOT}/_output/dockerized/bin/${host_os}/${host_arch}/gendocs" - "${KUBE_ROOT}/_output/local/bin/${host_os}/${host_arch}/gendocs" - "${KUBE_ROOT}/platforms/${host_os}/${host_arch}/gendocs" -) -gendocs=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 ) - -locations=( - "${KUBE_ROOT}/_output/dockerized/bin/${host_os}/${host_arch}/genman" - "${KUBE_ROOT}/_output/local/bin/${host_os}/${host_arch}/genman" - "${KUBE_ROOT}/platforms/${host_os}/${host_arch}/genman" -) -genman=$( (ls -t "${locations[@]}" 2>/dev/null || true) | head -1 ) - -if [[ ! -x "$gendocs" || ! -x "$genman" ]]; then +if [[ ! -x "$gendocs" || ! -x "$genman" || ! -x "$genbashcomp" ]]; then { - echo "It looks as if you don't have a compiled gendocs or genman binary" + echo "It looks as if you don't have a compiled gendocs, genman, or genbashcomp binary" echo echo "If you are running from a clone of the git repo, please run" - echo "'./hack/build-go.sh cmd/gendocs cmd/genman'." + echo "'./hack/build-go.sh cmd/gendocs cmd/genman cmd/genbashcomp'." } >&2 exit 1 fi @@ -109,3 +62,20 @@ else echo "${DOCROOT} is out of date. Please run hack/run-gendocs.sh" exit 1 fi + +COMPROOT="${KUBE_ROOT}/contrib/completions" +TMP_COMPROOT="${KUBE_ROOT}/contrib/completions_tmp" +cp -a "${COMPROOT}" "${TMP_COMPROOT}" +${genbashcomp} "${TMP_COMPROOT}/bash/" +set +e +diff -Naupr "${COMPROOT}" "${TMP_COMPROOT}" +ret=$? +set -e +rm -rf ${TMP_COMPROOT} +if [ $ret -eq 0 ] +then + echo "${COMPROOT} up to date." +else + echo "${COMPROOT} is out of date. Please run hack/run-gendocs.sh" + exit 1 +fi