mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Merge pull request #10933 from andronat/swagger_auto_type_docs
Automatic Generation of Swagger Documentation in Types
This commit is contained in:
		
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										73
									
								
								cmd/genswaggertypedocs/swagger_type_docs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								cmd/genswaggertypedocs/swagger_type_docs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes Authors 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"
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	pkg_runtime "k8s.io/kubernetes/pkg/runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/glog"
 | 
			
		||||
	flag "github.com/spf13/pflag"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	functionDest = flag.StringP("func-dest", "f", "-", "Output for swagger functions; '-' means stdout (default)")
 | 
			
		||||
	typeSrc      = flag.StringP("type-src", "s", "", "From where we are going to read the types")
 | 
			
		||||
	verify       = flag.BoolP("verify", "v", false, "Verifies if the given type-src file has documentation for every type")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	runtime.GOMAXPROCS(runtime.NumCPU())
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	if *typeSrc == "" {
 | 
			
		||||
		glog.Fatalf("Please define -s flag as it is the source file")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var funcOut io.Writer
 | 
			
		||||
	if *functionDest == "-" {
 | 
			
		||||
		funcOut = os.Stdout
 | 
			
		||||
	} else {
 | 
			
		||||
		file, err := os.Create(*functionDest)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			glog.Fatalf("Couldn't open %v: %v", *functionDest, err)
 | 
			
		||||
		}
 | 
			
		||||
		defer file.Close()
 | 
			
		||||
		funcOut = file
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	docsForTypes := pkg_runtime.ParseDocumentationFrom(*typeSrc)
 | 
			
		||||
 | 
			
		||||
	if *verify == true {
 | 
			
		||||
		rc, err := pkg_runtime.VerifySwaggerDocsExist(docsForTypes, funcOut)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Error in verification process: %s\n", err)
 | 
			
		||||
		}
 | 
			
		||||
		os.Exit(rc)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if docsForTypes != nil && len(docsForTypes) > 0 {
 | 
			
		||||
		if err := pkg_runtime.WriteSwaggerDocFunc(docsForTypes, funcOut); err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Error when writing swagger documentation functions: %s\n", err)
 | 
			
		||||
			os.Exit(-1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								hack/after-build/verify-description.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										67
									
								
								hack/after-build/verify-description.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# Copyright 2014 The Kubernetes Authors 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.
 | 
			
		||||
 | 
			
		||||
set -o errexit
 | 
			
		||||
set -o nounset
 | 
			
		||||
set -o pipefail
 | 
			
		||||
 | 
			
		||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
 | 
			
		||||
source "${KUBE_ROOT}/hack/lib/init.sh"
 | 
			
		||||
 | 
			
		||||
kube::golang::setup_env
 | 
			
		||||
 | 
			
		||||
# Find binary
 | 
			
		||||
genswaggertypedocs=$(kube::util::find-binary "genswaggertypedocs")
 | 
			
		||||
 | 
			
		||||
result=0
 | 
			
		||||
 | 
			
		||||
find_files() {
 | 
			
		||||
  find . -not \( \
 | 
			
		||||
      \( \
 | 
			
		||||
        -wholename './output' \
 | 
			
		||||
        -o -wholename './_output' \
 | 
			
		||||
        -o -wholename './release' \
 | 
			
		||||
        -o -wholename './target' \
 | 
			
		||||
        -o -wholename '*/third_party/*' \
 | 
			
		||||
        -o -wholename '*/Godeps/*' \
 | 
			
		||||
      \) -prune \
 | 
			
		||||
    \) -wholename '*pkg/api/v*/types.go'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if [[ $# -eq 0 ]]; then
 | 
			
		||||
  versioned_api_files=`find_files | egrep "pkg/api/v.[^/]*/types\.go"`
 | 
			
		||||
else
 | 
			
		||||
  versioned_api_files=("${@}")
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
for file in $versioned_api_files; do
 | 
			
		||||
  $genswaggertypedocs -v -s "${file}" -f - || result=$?
 | 
			
		||||
  if [[ "${result}" -ne "0" ]]; then
 | 
			
		||||
    echo "API file: ${file} is missing: ${result} descriptions"
 | 
			
		||||
  fi
 | 
			
		||||
  if grep json: "${file}" | grep -v // | grep description: ; then
 | 
			
		||||
    echo "API file: ${file} should not contain descriptions in struct tags"
 | 
			
		||||
    result=1
 | 
			
		||||
  fi
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
internal_types_file="${KUBE_ROOT}/pkg/api/types.go"
 | 
			
		||||
if grep json: "${internal_types_file}" | grep -v // | grep description: ; then
 | 
			
		||||
  echo "Internal API types should not contain descriptions"
 | 
			
		||||
  result=1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
exit ${result}
 | 
			
		||||
							
								
								
									
										58
									
								
								hack/after-build/verify-generated-swagger-docs.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										58
									
								
								hack/after-build/verify-generated-swagger-docs.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# Copyright 2014 The Kubernetes Authors 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.
 | 
			
		||||
 | 
			
		||||
set -o errexit
 | 
			
		||||
set -o nounset
 | 
			
		||||
set -o pipefail
 | 
			
		||||
 | 
			
		||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
 | 
			
		||||
source "${KUBE_ROOT}/hack/lib/init.sh"
 | 
			
		||||
 | 
			
		||||
kube::golang::setup_env
 | 
			
		||||
 | 
			
		||||
# Find binary
 | 
			
		||||
genswaggertypedocs=$(kube::util::find-binary "genswaggertypedocs")
 | 
			
		||||
 | 
			
		||||
if [[ ! -x "$genswaggertypedocs" ]]; then
 | 
			
		||||
  {
 | 
			
		||||
    echo "It looks as if you don't have a compiled genswaggertypedocs binary"
 | 
			
		||||
    echo
 | 
			
		||||
    echo "If you are running from a clone of the git repo, please run"
 | 
			
		||||
    echo "'./hack/build-go.sh cmd/genswaggertypedocs'."
 | 
			
		||||
  } >&2
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
APIROOT="${KUBE_ROOT}/pkg/api"
 | 
			
		||||
TMP_APIROOT="${KUBE_ROOT}/_tmp/api"
 | 
			
		||||
_tmp="${KUBE_ROOT}/_tmp"
 | 
			
		||||
 | 
			
		||||
mkdir -p "${_tmp}"
 | 
			
		||||
cp -a "${APIROOT}" "${TMP_APIROOT}"
 | 
			
		||||
 | 
			
		||||
"${KUBE_ROOT}/hack/update-generated-swagger-docs.sh"
 | 
			
		||||
echo "diffing ${APIROOT} against freshly generated swagger type documentation"
 | 
			
		||||
ret=0
 | 
			
		||||
diff -Naupr -I 'Auto generated by' "${APIROOT}" "${TMP_APIROOT}" || ret=$?
 | 
			
		||||
cp -a ${TMP_APIROOT} "${KUBE_ROOT}/pkg"
 | 
			
		||||
rm -rf "${_tmp}"
 | 
			
		||||
if [[ $ret -eq 0 ]]
 | 
			
		||||
then
 | 
			
		||||
  echo "${APIROOT} up to date."
 | 
			
		||||
else
 | 
			
		||||
  echo "${APIROOT} is out of date. Please run hack/update-generated-swagger-docs.sh"
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
@@ -68,6 +68,7 @@ kube::golang::test_targets() {
 | 
			
		||||
    cmd/genbashcomp
 | 
			
		||||
    cmd/genconversion
 | 
			
		||||
    cmd/gendeepcopy
 | 
			
		||||
    cmd/genswaggertypedocs
 | 
			
		||||
    examples/k8petstore/web-server
 | 
			
		||||
    github.com/onsi/ginkgo/ginkgo
 | 
			
		||||
    test/e2e/e2e.test
 | 
			
		||||
 
 | 
			
		||||
@@ -807,8 +807,8 @@ __EOF__
 | 
			
		||||
    file="${KUBE_TEMP}/schema-${version}.json"
 | 
			
		||||
    curl -s "http://127.0.0.1:${API_PORT}/swaggerapi/api/${version}" > "${file}"
 | 
			
		||||
    [[ "$(grep "list of returned" "${file}")" ]]
 | 
			
		||||
    [[ "$(grep "list of pods" "${file}")" ]]
 | 
			
		||||
    [[ "$(grep "watch for changes to the described resources" "${file}")" ]]
 | 
			
		||||
    [[ "$(grep "List of pods" "${file}")" ]]
 | 
			
		||||
    [[ "$(grep "Watch for changes to the described resources" "${file}")" ]]
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  kube::test::clear_all
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										66
									
								
								hack/update-generated-swagger-docs.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										66
									
								
								hack/update-generated-swagger-docs.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# Copyright 2015 The Kubernetes Authors 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.
 | 
			
		||||
 | 
			
		||||
set -o errexit
 | 
			
		||||
set -o nounset
 | 
			
		||||
set -o pipefail
 | 
			
		||||
 | 
			
		||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
 | 
			
		||||
source "${KUBE_ROOT}/hack/lib/init.sh"
 | 
			
		||||
 | 
			
		||||
kube::golang::setup_env
 | 
			
		||||
 | 
			
		||||
function generate_version() {
 | 
			
		||||
	local version=$1
 | 
			
		||||
	local TMPFILE="/tmp/types_swagger_doc_generated.$(date +%s).go"
 | 
			
		||||
 | 
			
		||||
	echo "Generating swagger type docs for version ${version}"
 | 
			
		||||
 | 
			
		||||
	sed 's/YEAR/2015/' hack/boilerplate/boilerplate.go.txt > $TMPFILE
 | 
			
		||||
  echo "package ${version}" >> $TMPFILE
 | 
			
		||||
	cat >> $TMPFILE <<EOF
 | 
			
		||||
 | 
			
		||||
// This file contains a collection of methods that can be used from go-resful to
 | 
			
		||||
// generate Swagger API documentation for its models. Please read this PR for more
 | 
			
		||||
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
 | 
			
		||||
//
 | 
			
		||||
// TODOs are ignored from the parser. (e.g. TODO(andronat):... || TODO:...) iff
 | 
			
		||||
// are on one line! For multiple line or blocks that you want to ignore use ---.
 | 
			
		||||
// Any context after a --- is ignored.
 | 
			
		||||
//
 | 
			
		||||
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
 | 
			
		||||
 | 
			
		||||
// AUTO-GENERATED FUNCTIONS START HERE
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
	GOPATH=$(godep path):$GOPATH go run cmd/genswaggertypedocs/swagger_type_docs.go -s "pkg/api/${version}/types.go" -f - >>  $TMPFILE
 | 
			
		||||
 | 
			
		||||
	echo "// AUTO-GENERATED FUNCTIONS END HERE" >> $TMPFILE
 | 
			
		||||
 | 
			
		||||
	gofmt -w -s $TMPFILE
 | 
			
		||||
	mv $TMPFILE "pkg/api/${version}/types_swagger_doc_generated.go"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VERSIONS="v1"
 | 
			
		||||
# To avoid compile errors, remove the currently existing files.
 | 
			
		||||
for ver in $VERSIONS; do
 | 
			
		||||
	rm -f "pkg/api/${ver}/types_swagger_doc_generated.go"
 | 
			
		||||
done
 | 
			
		||||
for ver in $VERSIONS; do
 | 
			
		||||
	generate_version "${ver}"
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
"${KUBE_ROOT}/hack/update-swagger-spec.sh"
 | 
			
		||||
@@ -19,43 +19,11 @@ set -o nounset
 | 
			
		||||
set -o pipefail
 | 
			
		||||
 | 
			
		||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
 | 
			
		||||
source "${KUBE_ROOT}/hack/lib/init.sh"
 | 
			
		||||
 | 
			
		||||
cd "${KUBE_ROOT}"
 | 
			
		||||
kube::golang::setup_env
 | 
			
		||||
"${KUBE_ROOT}/hack/build-go.sh" cmd/genswaggertypedocs
 | 
			
		||||
 | 
			
		||||
result=0
 | 
			
		||||
 | 
			
		||||
find_files() {
 | 
			
		||||
  find . -not \( \
 | 
			
		||||
      \( \
 | 
			
		||||
        -wholename './output' \
 | 
			
		||||
        -o -wholename './_output' \
 | 
			
		||||
        -o -wholename './release' \
 | 
			
		||||
        -o -wholename './target' \
 | 
			
		||||
        -o -wholename '*/third_party/*' \
 | 
			
		||||
        -o -wholename '*/Godeps/*' \
 | 
			
		||||
      \) -prune \
 | 
			
		||||
    \) -wholename '*pkg/api/v*/types.go'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if [[ $# -eq 0 ]]; then
 | 
			
		||||
  versioned_api_files=`find_files | egrep "pkg/api/v.[^/]*/types\.go"`
 | 
			
		||||
else
 | 
			
		||||
  versioned_api_files=("${@}")
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
for file in $versioned_api_files; do
 | 
			
		||||
  if grep json: "${file}" | grep -v // | grep -v ,inline | grep -v -q description: ; then
 | 
			
		||||
    echo "API file is missing the required field descriptions: ${file}"
 | 
			
		||||
    result=1
 | 
			
		||||
  fi
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
internal_types_file="${KUBE_ROOT}/pkg/api/types.go"
 | 
			
		||||
if grep json: "${internal_types_file}" | grep -v // | grep description: ; then
 | 
			
		||||
  echo "Internal API types should not contain descriptions"
 | 
			
		||||
  result=1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
exit ${result}
 | 
			
		||||
"${KUBE_ROOT}/hack/after-build/verify-description.sh" "$@"
 | 
			
		||||
 | 
			
		||||
# ex: ts=2 sw=2 et filetype=sh
 | 
			
		||||
 
 | 
			
		||||
@@ -242,6 +242,7 @@ tls-cert-file
 | 
			
		||||
tls-private-key-file
 | 
			
		||||
token-auth-file
 | 
			
		||||
ttl-secs
 | 
			
		||||
type-src
 | 
			
		||||
unix-socket
 | 
			
		||||
update-period
 | 
			
		||||
upgrade-target
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								hack/verify-generated-swagger-docs.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										28
									
								
								hack/verify-generated-swagger-docs.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# Copyright 2015 The Kubernetes Authors 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.
 | 
			
		||||
 | 
			
		||||
set -o errexit
 | 
			
		||||
set -o nounset
 | 
			
		||||
set -o pipefail
 | 
			
		||||
 | 
			
		||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
 | 
			
		||||
source "${KUBE_ROOT}/hack/lib/init.sh"
 | 
			
		||||
 | 
			
		||||
kube::golang::setup_env
 | 
			
		||||
 | 
			
		||||
"${KUBE_ROOT}/hack/build-go.sh" cmd/genswaggertypedocs
 | 
			
		||||
 | 
			
		||||
"${KUBE_ROOT}/hack/after-build/verify-generated-swagger-docs.sh" "$@"
 | 
			
		||||
@@ -93,8 +93,8 @@ files_need_description=()
 | 
			
		||||
# Check API schema definitions for field descriptions
 | 
			
		||||
for file in $(git diff --cached --name-only --diff-filter ACM | egrep "pkg/api/v.[^/]*/types\.go" | grep -v "third_party"); do
 | 
			
		||||
  # Check for files with fields without description tags
 | 
			
		||||
  descriptionless=$(hack/after-build/verify-description.sh "${file}")
 | 
			
		||||
  if [[ "$descriptionless" != "" ]]; then
 | 
			
		||||
  descriptionless=$(hack/after-build/verify-description.sh "${file}" > /dev/null; echo $?)
 | 
			
		||||
  if [[ "$descriptionless" -ne "0" ]]; then
 | 
			
		||||
    files_need_description+=("${file}")
 | 
			
		||||
  fi
 | 
			
		||||
done
 | 
			
		||||
@@ -158,6 +158,18 @@ else
 | 
			
		||||
fi
 | 
			
		||||
echo "${reset}"
 | 
			
		||||
 | 
			
		||||
echo -ne "Checking for swagger type documentation that need updating... "
 | 
			
		||||
if ! hack/after-build/verify-generated-swagger-docs.sh > /dev/null; then
 | 
			
		||||
  echo "${red}ERROR!"
 | 
			
		||||
  echo "Swagger type documentation needs to be updated."
 | 
			
		||||
  echo "To regenerate the spec, run:"
 | 
			
		||||
  echo "  hack/update-generated-swagger-docs.sh"
 | 
			
		||||
  exit_code=1
 | 
			
		||||
else
 | 
			
		||||
  echo "${green}OK"
 | 
			
		||||
fi
 | 
			
		||||
echo "${reset}"
 | 
			
		||||
 | 
			
		||||
echo -ne "Checking for swagger spec that need updating... "
 | 
			
		||||
if ! hack/after-build/verify-swagger-spec.sh > /dev/null; then
 | 
			
		||||
  echo "${red}ERROR!"
 | 
			
		||||
 
 | 
			
		||||
@@ -197,6 +197,7 @@ type VolumeSource struct {
 | 
			
		||||
	// directly exposed to the container. This is generally used for system
 | 
			
		||||
	// agents or other privileged things that are allowed to see the host
 | 
			
		||||
	// machine. Most containers will NOT need this.
 | 
			
		||||
	// ---
 | 
			
		||||
	// TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not
 | 
			
		||||
	// mount host directories as read/write.
 | 
			
		||||
	HostPath *HostPathVolumeSource `json:"hostPath,omitempty"`
 | 
			
		||||
@@ -290,10 +291,12 @@ const (
 | 
			
		||||
	// PersistentVolumeReclaimRecycle means the volume will be recycled back into the pool of unbound persistent volumes on release from its claim.
 | 
			
		||||
	// The volume plugin must support Recycling.
 | 
			
		||||
	PersistentVolumeReclaimRecycle PersistentVolumeReclaimPolicy = "Recycle"
 | 
			
		||||
 | 
			
		||||
	// PersistentVolumeReclaimDelete means the volume will be deleted from Kubernetes on release from its claim.
 | 
			
		||||
	// The volume plugin must support Deletion.
 | 
			
		||||
	// TODO: implement w/ DeletableVolumePlugin
 | 
			
		||||
	// PersistentVolumeReclaimDelete PersistentVolumeReclaimPolicy = "Delete"
 | 
			
		||||
 | 
			
		||||
	// PersistentVolumeReclaimRetain means the volume will left in its current phase (Released) for manual reclamation by the administrator.
 | 
			
		||||
	// The default policy is Retain.
 | 
			
		||||
	PersistentVolumeReclaimRetain PersistentVolumeReclaimPolicy = "Retain"
 | 
			
		||||
@@ -806,8 +809,6 @@ type ContainerState struct {
 | 
			
		||||
type ContainerStatus struct {
 | 
			
		||||
	// Each container in a pod must have a unique name.
 | 
			
		||||
	Name                 string         `json:"name"`
 | 
			
		||||
	// TODO(dchen1107): Should we rename PodStatus to a more generic name or have a separate states
 | 
			
		||||
	// defined for container?
 | 
			
		||||
	State                ContainerState `json:"state,omitempty"`
 | 
			
		||||
	LastTerminationState ContainerState `json:"lastState,omitempty"`
 | 
			
		||||
	// Ready specifies whether the conatiner has passed its readiness check.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1977
									
								
								pkg/api/v1/types.go
									
									
									
									
									
								
							
							
						
						
									
										1977
									
								
								pkg/api/v1/types.go
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1411
									
								
								pkg/api/v1/types_swagger_doc_generated.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1411
									
								
								pkg/api/v1/types_swagger_doc_generated.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -52,6 +52,11 @@ type action struct {
 | 
			
		||||
	Namer  ScopeNamer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An interface to see if an object supports swagger documentation as a method
 | 
			
		||||
type documentable interface {
 | 
			
		||||
	SwaggerDoc() map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// errEmptyName is returned when API requests do not fill the name section of the path.
 | 
			
		||||
var errEmptyName = errors.NewBadRequest("name must be provided")
 | 
			
		||||
 | 
			
		||||
@@ -796,7 +801,11 @@ func addObjectParams(ws *restful.WebService, route *restful.RouteBuilder, obj in
 | 
			
		||||
				if len(jsonName) == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				desc := sf.Tag.Get("description")
 | 
			
		||||
 | 
			
		||||
				var desc string
 | 
			
		||||
				if docable, ok := obj.(documentable); ok {
 | 
			
		||||
					desc = docable.SwaggerDoc()[jsonName]
 | 
			
		||||
				}
 | 
			
		||||
				route.Param(ws.QueryParameter(jsonName, desc).DataType(typeToJSON(sf.Type.Name())))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -247,11 +247,18 @@ func (*SimpleRoot) IsAnAPIObject() {}
 | 
			
		||||
 | 
			
		||||
type SimpleGetOptions struct {
 | 
			
		||||
	api.TypeMeta `json:",inline"`
 | 
			
		||||
	Param1       string `json:"param1" description:"description for param1"`
 | 
			
		||||
	Param2       string `json:"param2" description:"description for param2"`
 | 
			
		||||
	Param1       string `json:"param1"`
 | 
			
		||||
	Param2       string `json:"param2"`
 | 
			
		||||
	Path         string `json:"atAPath"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (SimpleGetOptions) SwaggerDoc() map[string]string {
 | 
			
		||||
	return map[string]string{
 | 
			
		||||
		"param1": "description for param1",
 | 
			
		||||
		"param2": "description for param2",
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*SimpleGetOptions) IsAnAPIObject() {}
 | 
			
		||||
 | 
			
		||||
type SimpleList struct {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										228
									
								
								pkg/runtime/swagger_doc_generator.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								pkg/runtime/swagger_doc_generator.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,228 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes Authors 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 runtime
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/doc"
 | 
			
		||||
	"go/parser"
 | 
			
		||||
	"go/token"
 | 
			
		||||
	"io"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Pair of strings. We keed the name of fields and the doc
 | 
			
		||||
type Pair struct {
 | 
			
		||||
	Name, Doc string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KubeTypes is an array to represent all available types in a parsed file. [0] is for the type itself
 | 
			
		||||
type KubeTypes []Pair
 | 
			
		||||
 | 
			
		||||
func astFrom(filePath string) *doc.Package {
 | 
			
		||||
	fset := token.NewFileSet()
 | 
			
		||||
	m := make(map[string]*ast.File)
 | 
			
		||||
 | 
			
		||||
	f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m[filePath] = f
 | 
			
		||||
	apkg, _ := ast.NewPackage(fset, m, nil, nil)
 | 
			
		||||
 | 
			
		||||
	return doc.New(apkg, "", 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fmtRawDoc(rawDoc string) string {
 | 
			
		||||
	var buffer bytes.Buffer
 | 
			
		||||
	delPrevChar := func() {
 | 
			
		||||
		if buffer.Len() > 0 {
 | 
			
		||||
			buffer.Truncate(buffer.Len() - 1) // Delete the last " " or "\n"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Ignore all lines after ---
 | 
			
		||||
	rawDoc = strings.Split(rawDoc, "---")[0]
 | 
			
		||||
 | 
			
		||||
	for _, line := range strings.Split(rawDoc, "\n") {
 | 
			
		||||
		line = strings.TrimRight(line, " ")
 | 
			
		||||
 | 
			
		||||
		if line == "" { // Keep paragraphs
 | 
			
		||||
			delPrevChar()
 | 
			
		||||
			buffer.WriteString("\n\n")
 | 
			
		||||
		} else if !strings.HasPrefix(strings.TrimLeft(line, " "), "TODO") { // Ignore one line TODOs
 | 
			
		||||
			if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
 | 
			
		||||
				delPrevChar()
 | 
			
		||||
				line = "\n" + line + "\n" // Replace it with newline. This is useful when we have a line with: "Example:\n\tJSON-someting..."
 | 
			
		||||
			} else {
 | 
			
		||||
				line += " "
 | 
			
		||||
			}
 | 
			
		||||
			buffer.WriteString(line)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	postDoc := strings.TrimRight(buffer.String(), "\n")
 | 
			
		||||
	postDoc = strings.Replace(postDoc, "\\\"", "\"", -1) // replace user's \" to "
 | 
			
		||||
	postDoc = strings.Replace(postDoc, "\"", "\\\"", -1) // Escape "
 | 
			
		||||
	postDoc = strings.Replace(postDoc, "\n", "\\n", -1)
 | 
			
		||||
	postDoc = strings.Replace(postDoc, "\t", "\\t", -1)
 | 
			
		||||
 | 
			
		||||
	return postDoc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fieldName returns the name of the field as it should appear in JSON format
 | 
			
		||||
// "-" indicates that this field is not part of the JSON representation
 | 
			
		||||
func fieldName(field *ast.Field) string {
 | 
			
		||||
	jsonTag := ""
 | 
			
		||||
	if field.Tag != nil {
 | 
			
		||||
		jsonTag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("json") // Delete first and last quotation
 | 
			
		||||
		if strings.Contains(jsonTag, "inline") {
 | 
			
		||||
			return "-"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	jsonTag = strings.Split(jsonTag, ",")[0] // This can return "-"
 | 
			
		||||
	if jsonTag == "" {
 | 
			
		||||
		if field.Names != nil {
 | 
			
		||||
			return field.Names[0].Name
 | 
			
		||||
		}
 | 
			
		||||
		return field.Type.(*ast.Ident).Name
 | 
			
		||||
	}
 | 
			
		||||
	return jsonTag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeFuncHeader(b *buffer, structName string, indent int) {
 | 
			
		||||
	s := fmt.Sprintf("var map_%s = map[string]string {\n", structName)
 | 
			
		||||
	b.addLine(s, indent)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeFuncFooter(b *buffer, structName string, indent int) {
 | 
			
		||||
	b.addLine("}\n", indent) // Closes the map definition
 | 
			
		||||
 | 
			
		||||
	s := fmt.Sprintf("func (%s) SwaggerDoc() map[string]string {\n", structName)
 | 
			
		||||
	b.addLine(s, indent)
 | 
			
		||||
	s = fmt.Sprintf("return map_%s\n", structName)
 | 
			
		||||
	b.addLine(s, indent+1)
 | 
			
		||||
	b.addLine("}\n", indent) // Closes the function definition
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeMapBody(b *buffer, kubeType []Pair, indent int) {
 | 
			
		||||
	format := "\"%s\": \"%s\",\n"
 | 
			
		||||
	for _, pair := range kubeType {
 | 
			
		||||
		s := fmt.Sprintf(format, pair.Name, pair.Doc)
 | 
			
		||||
		b.addLine(s, indent+2)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseDocumentationFrom gets all types' documentation and returns them as an
 | 
			
		||||
// array. Each type is again represented as an array (we have to use arrays as we
 | 
			
		||||
// need to be sure for the order of the fields). This function returns fields and
 | 
			
		||||
// struct definitions that have no documentation as {name, ""}.
 | 
			
		||||
func ParseDocumentationFrom(src string) []KubeTypes {
 | 
			
		||||
	var docForTypes []KubeTypes
 | 
			
		||||
 | 
			
		||||
	pkg := astFrom(src)
 | 
			
		||||
 | 
			
		||||
	for _, kubType := range pkg.Types {
 | 
			
		||||
		if structType, ok := kubType.Decl.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType); ok {
 | 
			
		||||
			var ks KubeTypes
 | 
			
		||||
			ks = append(ks, Pair{kubType.Name, fmtRawDoc(kubType.Doc)})
 | 
			
		||||
 | 
			
		||||
			for _, field := range structType.Fields.List {
 | 
			
		||||
				if n := fieldName(field); n != "-" {
 | 
			
		||||
					fieldDoc := fmtRawDoc(field.Doc.Text())
 | 
			
		||||
					ks = append(ks, Pair{n, fieldDoc})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			docForTypes = append(docForTypes, ks)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return docForTypes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteSwaggerDocFunc writes a declaration of a function as a string. This function is used in
 | 
			
		||||
// Swagger as a documentation source for structs and theirs fields
 | 
			
		||||
func WriteSwaggerDocFunc(kubeTypes []KubeTypes, w io.Writer) error {
 | 
			
		||||
	for _, kubeType := range kubeTypes {
 | 
			
		||||
		structName := kubeType[0].Name
 | 
			
		||||
		kubeType[0].Name = ""
 | 
			
		||||
 | 
			
		||||
		// Ignore empty documentation
 | 
			
		||||
		docfulTypes := make(KubeTypes, 0, len(kubeType))
 | 
			
		||||
		for _, pair := range kubeType {
 | 
			
		||||
			if pair.Doc != "" {
 | 
			
		||||
				docfulTypes = append(docfulTypes, pair)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(docfulTypes) == 0 {
 | 
			
		||||
			continue // If no fields and the struct have documentation, skip the function definition
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		indent := 0
 | 
			
		||||
		buffer := newBuffer()
 | 
			
		||||
 | 
			
		||||
		writeFuncHeader(buffer, structName, indent)
 | 
			
		||||
		writeMapBody(buffer, docfulTypes, indent)
 | 
			
		||||
		writeFuncFooter(buffer, structName, indent)
 | 
			
		||||
		buffer.addLine("\n", 0)
 | 
			
		||||
 | 
			
		||||
		if err := buffer.flushLines(w); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VerifySwaggerDocsExist writes in a io.Writer a list of structs and fields that
 | 
			
		||||
// are missing of documentation.
 | 
			
		||||
func VerifySwaggerDocsExist(kubeTypes []KubeTypes, w io.Writer) (int, error) {
 | 
			
		||||
	missingDocs := 0
 | 
			
		||||
	buffer := newBuffer()
 | 
			
		||||
 | 
			
		||||
	for _, kubeType := range kubeTypes {
 | 
			
		||||
		structName := kubeType[0].Name
 | 
			
		||||
		if kubeType[0].Doc == "" {
 | 
			
		||||
			format := "Missing documentation for the struct itself: %s\n"
 | 
			
		||||
			s := fmt.Sprintf(format, structName)
 | 
			
		||||
			buffer.addLine(s, 0)
 | 
			
		||||
			missingDocs++
 | 
			
		||||
		}
 | 
			
		||||
		kubeType = kubeType[1:] // Skip struct definition
 | 
			
		||||
 | 
			
		||||
		for _, pair := range kubeType { // Iterate only the fields
 | 
			
		||||
			if pair.Doc == "" {
 | 
			
		||||
				format := "In struct: %s, field documentation is missing: %s\n"
 | 
			
		||||
				s := fmt.Sprintf(format, structName, pair.Name)
 | 
			
		||||
				buffer.addLine(s, 0)
 | 
			
		||||
				missingDocs++
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := buffer.flushLines(w); err != nil {
 | 
			
		||||
		return -1, err
 | 
			
		||||
	}
 | 
			
		||||
	return missingDocs, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								pkg/runtime/swagger_doc_generator_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								pkg/runtime/swagger_doc_generator_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes Authors 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 runtime
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestFmtRawDoc(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		t, expected string
 | 
			
		||||
	}{
 | 
			
		||||
		{"aaa\n  --- asd\n TODO: tooooodo\n toooodoooooo\n", "aaa"},
 | 
			
		||||
		{"aaa\nasd\n TODO: tooooodo\nbbbb\n --- toooodoooooo\n", "aaa asd bbbb"},
 | 
			
		||||
		{" TODO: tooooodo\n", ""},
 | 
			
		||||
		{"Par1\n\nPar2\n\n", "Par1\\n\\nPar2"},
 | 
			
		||||
		{"", ""},
 | 
			
		||||
		{" ", ""},
 | 
			
		||||
		{" \n", ""},
 | 
			
		||||
		{" \n\n ", ""},
 | 
			
		||||
		{"Example:\n\tl1\n\t\tl2\n", "Example:\\n\\tl1\\n\\t\\tl2"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		if o := fmtRawDoc(test.t); o != test.expected {
 | 
			
		||||
			t.Fatalf("Expected: %q, got %q", test.expected, o)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user