Merge pull request #6744 from eparis/auto-gen-bash

Auto gen bash completions
This commit is contained in:
Jeff Lowdermilk 2015-04-13 13:49:26 -07:00
commit 86d3072492
9 changed files with 191 additions and 270 deletions

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}
}

41
cmd/genutils/genutils.go Normal file
View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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=()
}

View File

@ -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() {

View File

@ -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/"

View File

@ -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