#!/usr/bin/env bash # Copyright 2014 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. # shellcheck disable=2046 # printf word-splitting is intentional set -o errexit set -o nounset set -o pipefail # This tool wants a different default than usual. KUBE_VERBOSE="${KUBE_VERBOSE:-1}" KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${KUBE_ROOT}/hack/lib/init.sh" source "${KUBE_ROOT}/hack/lib/protoc.sh" cd "${KUBE_ROOT}" kube::golang::setup_env DBG_CODEGEN="${DBG_CODEGEN:-0}" GENERATED_FILE_PREFIX="${GENERATED_FILE_PREFIX:-zz_generated.}" UPDATE_API_KNOWN_VIOLATIONS="${UPDATE_API_KNOWN_VIOLATIONS:-}" OUT_DIR="_output" PRJ_SRC_PATH="k8s.io/kubernetes" BOILERPLATE_FILENAME="vendor/k8s.io/code-generator/hack/boilerplate.go.txt" APPLYCONFIG_PKG="k8s.io/client-go/applyconfigurations" # Any time we call sort, we want it in the same locale. export LC_ALL="C" # Work around for older grep tools which might have options we don't want. unset GREP_OPTIONS if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: starting generated_files" fi function git_find() { # Similar to find but faster and easier to understand. We want to include # modified and untracked files because this might be running against code # which is not tracked by git yet. git ls-files -cmo --exclude-standard ':!:vendor/*' "$@" } function git_grep() { # We want to include modified and untracked files because this might be # running against code which is not tracked by git yet. # We need vendor exclusion added at the end since it has to be part of # the pathspecs which are specified last. git grep --untracked "$@" ':!:vendor/*' } # Generate a list of all files that have a `+k8s:` comment-tag. This will be # used to derive lists of files/dirs for generation tools. # # We want to include the "special" vendor directories which are actually part # of the Kubernetes source tree (staging/*) but we need them to be named as # their vendor/* equivalents. We do not want all of vendor nor # hack/tools/vendor nor even all of vendor/k8s.io - just the subset that lives # in staging. if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: finding all +k8s: tags" fi ALL_K8S_TAG_FILES=() kube::util::read-array ALL_K8S_TAG_FILES < <( git_grep -l \ -e '^// *+k8s:' `# match +k8s: tags` \ -- \ ':!:*/testdata/*' `# not under any testdata` \ ':(glob)**/*.go' `# in any *.go file` \ | sed 's|^staging/src|vendor|' `# see comments above` \ ) if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: found ${#ALL_K8S_TAG_FILES[@]} +k8s: tagged files" fi # # Code generation logic. # # protobuf generation # # Some of the later codegens depend on the results of this, so it needs to come # first in the case of regenerating everything. function codegen::protobuf() { # NOTE: All output from this script needs to be copied back to the calling # source tree. This is managed in kube::build::copy_output in build/common.sh. # If the output set is changed update that function. local apis=() kube::util::read-array apis < <( git grep --untracked --null -l \ -e '// +k8s:protobuf-gen=package' \ -- \ cmd pkg staging \ | xargs -0 -n1 dirname \ | sed 's|^|k8s.io/kubernetes/|;s|k8s.io/kubernetes/staging/src/||' \ | sort -u) kube::log::status "Generating protobufs for ${#apis[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: generating protobufs for:" for dir in "${apis[@]}"; do kube::log::status "DBG: $dir" done fi git_find -z \ ':(glob)**/generated.proto' \ ':(glob)**/generated.pb.go' \ | xargs -0 rm -f if kube::protoc::check_protoc >/dev/null; then hack/update-generated-protobuf-dockerized.sh "${apis[@]}" else kube::log::status "protoc ${PROTOC_VERSION} not found (can install with hack/install-protoc.sh); generating containerized..." build/run.sh hack/update-generated-protobuf-dockerized.sh "${apis[@]}" fi } # Generates types_swagger_doc_generated file for the given group version. # $1: Name of the group version # $2: Path to the directory where types.go for that group version exists. This # is the directory where the file will be generated. function gen_types_swagger_doc() { # The tool used to generate swagger code. local swagger_bin swagger_bin="$(kube::util::find-binary "genswaggertypedocs")" local group_version="$1" local gv_dir="$2" local tmpfile tmpfile="${TMPDIR:-/tmp}/types_swagger_doc_generated.$(date +%s).go" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running ${swagger_bin} for ${group_version} at ${gv_dir}" fi { cat "${BOILERPLATE_FILENAME}" echo echo "package ${group_version##*/}" # Indenting here prevents the boilerplate checker from thinking this file # is generated - gofmt will fix the indents anyway. cat < "${tmpfile}" "${swagger_bin}" \ -s \ "${gv_dir}/types.go" \ -f - \ >> "${tmpfile}" echo "// AUTO-GENERATED FUNCTIONS END HERE" >> "${tmpfile}" gofmt -w -s "${tmpfile}" mv "${tmpfile}" "${gv_dir}/types_swagger_doc_generated.go" } # swagger generation # # Some of the later codegens depend on the results of this, so it needs to come # first in the case of regenerating everything. function codegen::swagger() { # Build the tool GO111MODULE=on GOPROXY=off go install \ ./cmd/genswaggertypedocs local group_versions=() IFS=" " read -r -a group_versions <<< "meta/v1 meta/v1beta1 ${KUBE_AVAILABLE_GROUP_VERSIONS}" kube::log::status "Generating swagger for ${#group_versions[@]} targets" git_find -z ':(glob)**/types_swagger_doc_generated.go' | xargs -0 rm -f # Regenerate files. for group_version in "${group_versions[@]}"; do gen_types_swagger_doc "${group_version}" "$(kube::util::group-version-to-pkg-path "${group_version}")" done } # prerelease-lifecycle generation # # Any package that wants prerelease-lifecycle functions generated must include a # comment-tag in column 0 of one file of the form: # // +k8s:prerelease-lifecycle-gen=true function codegen::prerelease() { # Build the tool. GO111MODULE=on GOPROXY=off go install \ k8s.io/code-generator/cmd/prerelease-lifecycle-gen # The result file, in each pkg, of prerelease-lifecycle generation. local output_base="${GENERATED_FILE_PREFIX}prerelease-lifecycle" # The tool used to generate prerelease-lifecycle code. local gen_prerelease_bin gen_prerelease_bin="$(kube::util::find-binary "prerelease-lifecycle-gen")" # Find all the directories that request prerelease-lifecycle generation. if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: finding all +k8s:prerelease-lifecycle-gen tags" fi local tag_dirs=() kube::util::read-array tag_dirs < <( \ grep -l --null '+k8s:prerelease-lifecycle-gen=true' "${ALL_K8S_TAG_FILES[@]}" \ | xargs -0 -n1 dirname \ | sort -u) if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: found ${#tag_dirs[@]} +k8s:prerelease-lifecycle-gen tagged dirs" fi local tag_pkgs=() for dir in "${tag_dirs[@]}"; do tag_pkgs+=("${PRJ_SRC_PATH}/$dir") done kube::log::status "Generating prerelease-lifecycle code for ${#tag_pkgs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running ${gen_prerelease_bin} for:" for dir in "${tag_dirs[@]}"; do kube::log::status "DBG: $dir" done fi git_find -z ':(glob)**'/"${output_base}.go" | xargs -0 rm -f ./hack/run-in-gopath.sh "${gen_prerelease_bin}" \ --v "${KUBE_VERBOSE}" \ --logtostderr \ -h "${BOILERPLATE_FILENAME}" \ -O "${output_base}" \ $(printf -- " -i %s" "${tag_pkgs[@]}") \ "$@" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "Generated prerelease-lifecycle code" fi } # Deep-copy generation # # Any package that wants deep-copy functions generated must include a # comment-tag in column 0 of one file of the form: # // +k8s:deepcopy-gen= # # The may be one of: # generate: generate deep-copy functions into the package # register: generate deep-copy functions and register them with a # scheme function codegen::deepcopy() { # Build the tool. GO111MODULE=on GOPROXY=off go install \ k8s.io/code-generator/cmd/deepcopy-gen # The result file, in each pkg, of deep-copy generation. local output_base="${GENERATED_FILE_PREFIX}deepcopy" # The tool used to generate deep copies. local gen_deepcopy_bin gen_deepcopy_bin="$(kube::util::find-binary "deepcopy-gen")" # Find all the directories that request deep-copy generation. if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: finding all +k8s:deepcopy-gen tags" fi local tag_dirs=() kube::util::read-array tag_dirs < <( \ grep -l --null '+k8s:deepcopy-gen=' "${ALL_K8S_TAG_FILES[@]}" \ | xargs -0 -n1 dirname \ | sort -u) if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: found ${#tag_dirs[@]} +k8s:deepcopy-gen tagged dirs" fi local tag_pkgs=() for dir in "${tag_dirs[@]}"; do tag_pkgs+=("${PRJ_SRC_PATH}/$dir") done kube::log::status "Generating deepcopy code for ${#tag_pkgs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running ${gen_deepcopy_bin} for:" for dir in "${tag_dirs[@]}"; do kube::log::status "DBG: $dir" done fi git_find -z ':(glob)**'/"${output_base}.go" | xargs -0 rm -f ./hack/run-in-gopath.sh "${gen_deepcopy_bin}" \ --v "${KUBE_VERBOSE}" \ --logtostderr \ -h "${BOILERPLATE_FILENAME}" \ -O "${output_base}" \ --bounding-dirs "${PRJ_SRC_PATH},k8s.io/api" \ $(printf -- " -i %s" "${tag_pkgs[@]}") \ "$@" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "Generated deepcopy code" fi } # Defaulter generation # # Any package that wants defaulter functions generated must include a # comment-tag in column 0 of one file of the form: # // +k8s:defaulter-gen= # # The depends on context: # on types: # true: always generate a defaulter for this type # false: never generate a defaulter for this type # on functions: # covers: if the function name matches SetDefault_NAME, instructs # the generator not to recurse # on packages: # FIELDNAME: any object with a field of this name is a candidate # for having a defaulter generated function codegen::defaults() { # Build the tool. GO111MODULE=on GOPROXY=off go install \ k8s.io/code-generator/cmd/defaulter-gen # The result file, in each pkg, of defaulter generation. local output_base="${GENERATED_FILE_PREFIX}defaults" # The tool used to generate defaulters. local gen_defaulter_bin gen_defaulter_bin="$(kube::util::find-binary "defaulter-gen")" # All directories that request any form of defaulter generation. if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: finding all +k8s:defaulter-gen tags" fi local tag_dirs=() kube::util::read-array tag_dirs < <( \ grep -l --null '+k8s:defaulter-gen=' "${ALL_K8S_TAG_FILES[@]}" \ | xargs -0 -n1 dirname \ | sort -u) if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: found ${#tag_dirs[@]} +k8s:defaulter-gen tagged dirs" fi local tag_pkgs=() for dir in "${tag_dirs[@]}"; do tag_pkgs+=("${PRJ_SRC_PATH}/$dir") done kube::log::status "Generating defaulter code for ${#tag_pkgs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running ${gen_defaulter_bin} for:" for dir in "${tag_dirs[@]}"; do kube::log::status "DBG: $dir" done fi git_find -z ':(glob)**'/"${output_base}.go" | xargs -0 rm -f ./hack/run-in-gopath.sh "${gen_defaulter_bin}" \ --v "${KUBE_VERBOSE}" \ --logtostderr \ -h "${BOILERPLATE_FILENAME}" \ -O "${output_base}" \ $(printf -- " --extra-peer-dirs %s" "${tag_pkgs[@]}") \ $(printf -- " -i %s" "${tag_pkgs[@]}") \ "$@" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "Generated defaulter code" fi } # Conversion generation # Any package that wants conversion functions generated into it must # include one or more comment-tags in its `doc.go` file, of the form: # // +k8s:conversion-gen= # # The INTERNAL_TYPES_DIR is a project-local path to another directory # which should be considered when evaluating peer types for # conversions. An optional additional comment of the form # // +k8s:conversion-gen-external-types= # # identifies where to find the external types; if there is no such # comment then the external types are sought in the package where the # `k8s:conversion` tag is found. # # Conversions, in both directions, are generated for every type name # that is defined in both an internal types package and the external # types package. # # TODO: it might be better in the long term to make peer-types explicit in the # IDL. function codegen::conversions() { # Build the tool. GO111MODULE=on GOPROXY=off go install \ k8s.io/code-generator/cmd/conversion-gen # The result file, in each pkg, of conversion generation. local output_base="${GENERATED_FILE_PREFIX}conversion" # The tool used to generate conversions. local gen_conversion_bin gen_conversion_bin="$(kube::util::find-binary "conversion-gen")" # All directories that request any form of conversion generation. if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: finding all +k8s:conversion-gen tags" fi local tag_dirs=() kube::util::read-array tag_dirs < <(\ grep -l --null '^// *+k8s:conversion-gen=' "${ALL_K8S_TAG_FILES[@]}" \ | xargs -0 -n1 dirname \ | sort -u) if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: found ${#tag_dirs[@]} +k8s:conversion-gen tagged dirs" fi local tag_pkgs=() for dir in "${tag_dirs[@]}"; do tag_pkgs+=("${PRJ_SRC_PATH}/$dir") done local extra_peer_pkgs=( k8s.io/kubernetes/pkg/apis/core k8s.io/kubernetes/pkg/apis/core/v1 k8s.io/api/core/v1 ) kube::log::status "Generating conversion code for ${#tag_pkgs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running ${gen_conversion_bin} for:" for dir in "${tag_dirs[@]}"; do kube::log::status "DBG: $dir" done fi git_find -z ':(glob)**'/"${output_base}.go" | xargs -0 rm -f ./hack/run-in-gopath.sh "${gen_conversion_bin}" \ --v "${KUBE_VERBOSE}" \ --logtostderr \ -h "${BOILERPLATE_FILENAME}" \ -O "${output_base}" \ $(printf -- " --extra-peer-dirs %s" "${extra_peer_pkgs[@]}") \ $(printf -- " --extra-dirs %s" "${tag_pkgs[@]}") \ $(printf -- " -i %s" "${tag_pkgs[@]}") \ "$@" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "Generated conversion code" fi } # $@: directories to exclude # example: # k8s_tag_files_except foo bat/qux function k8s_tag_files_except() { for f in "${ALL_K8S_TAG_FILES[@]}"; do local excl="" for x in "$@"; do if [[ "$f" =~ "$x"/.* ]]; then excl="true" break fi done if [[ "${excl}" != true ]]; then echo "$f" fi done } # $@: directories to exclude # example: # k8s_tag_files_matching foo bat/qux function k8s_tag_files_matching() { for f in "${ALL_K8S_TAG_FILES[@]}"; do for x in "$@"; do if [[ "$f" =~ "${x}"/.* ]]; then echo "$f" break fi done done } # $1: the name of a scalar variable to read # example: # FOO_VAR="foo value" # BAR_VAR="bar value" # x=FOO # indirect "${x}_VAR" # -> "foo value\n" function indirect() { # This is a trick to get bash to indirectly read a variable. # Thanks StackOverflow! local var="$1" echo "${!var}" } # $1: the name of an array variable to read # FOO_ARR=(a b c) # BAR_ARR=(1 2 3) # x=FOO # indirect_array "${x}_ARR" # -> "a\nb\nc\n" function indirect_array() { # This is a trick to get bash to indirectly read an array. # Thanks StackOverflow! local arrayname="$1" # shellcheck disable=SC1087 # intentional local tmp="$arrayname[@]" printf -- "%s\n" "${!tmp}" } # OpenAPI generation # # Any package that wants open-api functions generated must include a # comment-tag in column 0 of one file of the form: # // +k8s:openapi-gen=true function codegen::openapi() { # Build the tool. GO111MODULE=on GOPROXY=off go install \ k8s.io/kube-openapi/cmd/openapi-gen # The result file, in each pkg, of open-api generation. local output_base="${GENERATED_FILE_PREFIX}openapi" # The tool used to generate open apis. local gen_openapi_bin gen_openapi_bin="$(kube::util::find-binary "openapi-gen")" # Standard dirs which all targets need. local apimachinery_dirs=( vendor/k8s.io/apimachinery/pkg/apis/meta/v1 vendor/k8s.io/apimachinery/pkg/runtime vendor/k8s.io/apimachinery/pkg/version ) # These should probably be configured by tags in code-files somewhere. local targets=( KUBE AGGREGATOR APIEXTENSIONS CODEGEN SAMPLEAPISERVER ) # shellcheck disable=SC2034 # used indirectly local KUBE_output_dir="pkg/generated/openapi" # shellcheck disable=SC2034 # used indirectly local KUBE_known_violations_file="api/api-rules/violation_exceptions.list" # shellcheck disable=SC2034 # used indirectly local KUBE_tag_files=() kube::util::read-array KUBE_tag_files < <( k8s_tag_files_except \ vendor/k8s.io/code-generator \ vendor/k8s.io/sample-apiserver ) # shellcheck disable=SC2034 # used indirectly local AGGREGATOR_output_dir="staging/src/k8s.io/kube-aggregator/pkg/generated/openapi" # shellcheck disable=SC2034 # used indirectly local AGGREGATOR_known_violations_file="api/api-rules/aggregator_violation_exceptions.list" # shellcheck disable=SC2034 # used indirectly local AGGREGATOR_tag_files=() kube::util::read-array AGGREGATOR_tag_files < <( k8s_tag_files_matching \ vendor/k8s.io/kube-aggregator \ "${apimachinery_dirs[@]}" ) # shellcheck disable=SC2034 # used indirectly local APIEXTENSIONS_output_dir="staging/src/k8s.io/apiextensions-apiserver/pkg/generated/openapi" # shellcheck disable=SC2034 # used indirectly local APIEXTENSIONS_known_violations_file="api/api-rules/apiextensions_violation_exceptions.list" # shellcheck disable=SC2034 # used indirectly local APIEXTENSIONS_tag_files=() kube::util::read-array APIEXTENSIONS_tag_files < <( k8s_tag_files_matching \ vendor/k8s.io/apiextensions \ vendor/k8s.io/api/autoscaling/v1 \ "${apimachinery_dirs[@]}" ) # shellcheck disable=SC2034 # used indirectly local CODEGEN_output_dir="staging/src/k8s.io/code-generator/examples/apiserver/openapi" # shellcheck disable=SC2034 # used indirectly local CODEGEN_known_violations_file="api/api-rules/codegen_violation_exceptions.list" # shellcheck disable=SC2034 # used indirectly local CODEGEN_tag_files=() kube::util::read-array CODEGEN_tag_files < <( k8s_tag_files_matching \ vendor/k8s.io/code-generator \ "${apimachinery_dirs[@]}" ) # shellcheck disable=SC2034 # used indirectly local SAMPLEAPISERVER_output_dir="staging/src/k8s.io/sample-apiserver/pkg/generated/openapi" # shellcheck disable=SC2034 # used indirectly local SAMPLEAPISERVER_known_violations_file="api/api-rules/sample_apiserver_violation_exceptions.list" # shellcheck disable=SC2034 # used indirectly local SAMPLEAPISERVER_tag_files=() kube::util::read-array SAMPLEAPISERVER_tag_files < <( k8s_tag_files_matching \ vendor/k8s.io/sample-apiserver \ "${apimachinery_dirs[@]}" ) git_find -z ':(glob)**'/"${output_base}.go" | xargs -0 rm -f for prefix in "${targets[@]}"; do local report_file="${OUT_DIR}/${prefix}_violations.report" # When UPDATE_API_KNOWN_VIOLATIONS is set to be true, let the generator to write # updated API violations to the known API violation exceptions list. if [[ "${UPDATE_API_KNOWN_VIOLATIONS}" == true ]]; then report_file=$(indirect "${prefix}_known_violations_file") fi # 2 lines because shellcheck local output_dir output_dir=$(indirect "${prefix}_output_dir") if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: finding all +k8s:openapi-gen tags for ${prefix}" fi local tag_dirs=() kube::util::read-array tag_dirs < <( grep -l --null '+k8s:openapi-gen=' $(indirect_array "${prefix}_tag_files") \ | xargs -0 -n1 dirname \ | sort -u) if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: found ${#tag_dirs[@]} +k8s:openapi-gen tagged dirs for ${prefix}" fi local tag_pkgs=() for dir in "${tag_dirs[@]}"; do tag_pkgs+=("${PRJ_SRC_PATH}/$dir") done kube::log::status "Generating openapi code for ${prefix}" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running ${gen_openapi_bin} for:" for dir in "${tag_dirs[@]}"; do kube::log::status "DBG: $dir" done fi ./hack/run-in-gopath.sh "${gen_openapi_bin}" \ --v "${KUBE_VERBOSE}" \ --logtostderr \ -h "${BOILERPLATE_FILENAME}" \ -O "${output_base}" \ -p "${PRJ_SRC_PATH}/${output_dir}" \ -r "${report_file}" \ $(printf -- " -i %s" "${tag_pkgs[@]}") \ "$@" touch "${report_file}" # 2 lines because shellcheck local known_filename known_filename=$(indirect "${prefix}_known_violations_file") if ! diff -u "${known_filename}" "${report_file}"; then echo -e "ERROR:" echo -e "\t'${prefix}' API rule check failed - reported violations differ from known violations" echo -e "\tPlease read api/api-rules/README.md to resolve the failure in ${known_filename}" fi if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "Generated openapi code" fi done # for each prefix } function codegen::applyconfigs() { GO111MODULE=on GOPROXY=off go install \ k8s.io/kubernetes/pkg/generated/openapi/cmd/models-schema \ k8s.io/code-generator/cmd/applyconfiguration-gen local modelsschema modelsschema=$(kube::util::find-binary "models-schema") local applyconfigurationgen applyconfigurationgen=$(kube::util::find-binary "applyconfiguration-gen") local ext_apis=() kube::util::read-array ext_apis < <( cd "${KUBE_ROOT}/staging/src" git_find -z ':(glob)k8s.io/api/**/types.go' \ | xargs -0 -n1 dirname \ | sort -u) ext_apis+=("k8s.io/apimachinery/pkg/apis/meta/v1") kube::log::status "Generating apply-config code for ${#ext_apis[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running ${applyconfigurationgen} for:" for api in "${ext_apis[@]}"; do kube::log::status "DBG: $api" done fi git_grep -l --null \ -e '^// Code generated by applyconfiguration-gen. DO NOT EDIT.$' \ -- \ ':(glob)staging/src/k8s.io/client-go/**/*.go' \ | xargs -0 rm -f "${applyconfigurationgen}" \ --openapi-schema <("${modelsschema}") \ --go-header-file "${BOILERPLATE_FILENAME}" \ --output-base "${KUBE_ROOT}/vendor" \ --output-package "${APPLYCONFIG_PKG}" \ $(printf -- " --input-dirs %s" "${ext_apis[@]}") \ "$@" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "Generated apply-config code" fi } function codegen::clients() { GO111MODULE=on GOPROXY=off go install \ k8s.io/code-generator/cmd/client-gen local clientgen clientgen=$(kube::util::find-binary "client-gen") IFS=" " read -r -a group_versions <<< "${KUBE_AVAILABLE_GROUP_VERSIONS}" local gv_dirs=() for gv in "${group_versions[@]}"; do # add items, but strip off any leading apis/ you find to match command expectations local api_dir api_dir=$(kube::util::group-version-to-pkg-path "${gv}") local nopkg_dir=${api_dir#pkg/} nopkg_dir=${nopkg_dir#vendor/k8s.io/api/} local pkg_dir=${nopkg_dir#apis/} # skip groups that aren't being served, clients for these don't matter if [[ " ${KUBE_NONSERVER_GROUP_VERSIONS} " == *" ${gv} "* ]]; then continue fi gv_dirs+=("${pkg_dir}") done kube::log::status "Generating client code for ${#gv_dirs[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running ${clientgen} for:" for dir in "${gv_dirs[@]}"; do kube::log::status "DBG: $dir" done fi git_grep -l --null \ -e '^// Code generated by client-gen. DO NOT EDIT.$' \ -- \ ':(glob)staging/src/k8s.io/client-go/**/*.go' \ | xargs -0 rm -f "${clientgen}" \ --go-header-file "${BOILERPLATE_FILENAME}" \ --output-base "${KUBE_ROOT}/vendor" \ --output-package="k8s.io/client-go" \ --clientset-name="kubernetes" \ --input-base="k8s.io/api" \ --apply-configuration-package "${APPLYCONFIG_PKG}" \ $(printf -- " --input %s" "${gv_dirs[@]}") \ "$@" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "Generated client code" fi } function codegen::listers() { GO111MODULE=on GOPROXY=off go install \ k8s.io/code-generator/cmd/lister-gen local listergen listergen=$(kube::util::find-binary "lister-gen") local ext_apis=() kube::util::read-array ext_apis < <( cd "${KUBE_ROOT}/staging/src" git_find -z ':(glob)k8s.io/api/**/types.go' \ | xargs -0 -n1 dirname \ | sort -u) kube::log::status "Generating lister code for ${#ext_apis[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running ${listergen} for:" for api in "${ext_apis[@]}"; do kube::log::status "DBG: $api" done fi git_grep -l --null \ -e '^// Code generated by lister-gen. DO NOT EDIT.$' \ -- \ ':(glob)staging/src/k8s.io/client-go/**/*.go' \ | xargs -0 rm -f "${listergen}" \ --go-header-file "${BOILERPLATE_FILENAME}" \ --output-base "${KUBE_ROOT}/vendor" \ --output-package "k8s.io/client-go/listers" \ $(printf -- " --input-dirs %s" "${ext_apis[@]}") \ "$@" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "Generated lister code" fi } function codegen::informers() { GO111MODULE=on GOPROXY=off go install \ k8s.io/code-generator/cmd/informer-gen local informergen informergen=$(kube::util::find-binary "informer-gen") local ext_apis=() kube::util::read-array ext_apis < <( cd "${KUBE_ROOT}/staging/src" git_find -z ':(glob)k8s.io/api/**/types.go' \ | xargs -0 -n1 dirname \ | sort -u) kube::log::status "Generating informer code for ${#ext_apis[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: running ${informergen} for:" for api in "${ext_apis[@]}"; do kube::log::status "DBG: $api" done fi git_grep -l --null \ -e '^// Code generated by informer-gen. DO NOT EDIT.$' \ -- \ ':(glob)staging/src/k8s.io/client-go/**/*.go' \ | xargs -0 rm -f "${informergen}" \ --go-header-file "${BOILERPLATE_FILENAME}" \ --output-base "${KUBE_ROOT}/vendor" \ --output-package "k8s.io/client-go/informers" \ --single-directory \ --versioned-clientset-package k8s.io/client-go/kubernetes \ --listers-package k8s.io/client-go/listers \ $(printf -- " --input-dirs %s" "${ext_apis[@]}") \ "$@" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "Generated informer code" fi } function codegen::subprojects() { # Call generation on sub-projects. # TODO(thockin): make these take a list of codegens and flags local subs=( vendor/k8s.io/code-generator/hack/update-codegen.sh vendor/k8s.io/kube-aggregator/hack/update-codegen.sh vendor/k8s.io/sample-apiserver/hack/update-codegen.sh vendor/k8s.io/sample-controller/hack/update-codegen.sh vendor/k8s.io/apiextensions-apiserver/hack/update-codegen.sh vendor/k8s.io/metrics/hack/update-codegen.sh vendor/k8s.io/apiextensions-apiserver/examples/client-go/hack/update-codegen.sh ) for s in "${subs[@]}"; do CODEGEN_PKG=./vendor/k8s.io/code-generator "$s" done } function codegen::protobindings() { # Each element of this array is a directory containing subdirectories which # eventually contain a file named "api.proto". local apis=( "staging/src/k8s.io/cri-api/pkg/apis/runtime" "staging/src/k8s.io/kubelet/pkg/apis/podresources" "staging/src/k8s.io/kubelet/pkg/apis/deviceplugin" "staging/src/k8s.io/kms/apis" "staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2" "staging/src/k8s.io/kubelet/pkg/apis/dra" "staging/src/k8s.io/kubelet/pkg/apis/pluginregistration" "pkg/kubelet/pluginmanager/pluginwatcher/example_plugin_apis" ) kube::log::status "Generating protobuf bindings for ${#apis[@]} targets" if [[ "${DBG_CODEGEN}" == 1 ]]; then kube::log::status "DBG: generating protobuf bindings for:" for dir in "${apis[@]}"; do kube::log::status "DBG: $dir" done fi for api in "${apis[@]}"; do git ls-files -z -cmo --exclude-standard ":(glob)${api}"/'**/api.pb.go' \ | xargs -0 rm -f done if kube::protoc::check_protoc >/dev/null; then hack/update-generated-proto-bindings-dockerized.sh "${apis[@]}" else kube::log::status "protoc ${PROTOC_VERSION} not found (can install with hack/install-protoc.sh); generating containerized..." # NOTE: All output from this script needs to be copied back to the calling # source tree. This is managed in kube::build::copy_output in build/common.sh. # If the output set is changed update that function. build/run.sh hack/update-generated-proto-bindings-dockerized.sh "${apis[@]}" fi } # # main # function list_codegens() { ( shopt -s extdebug declare -F \ | cut -f3 -d' ' \ | grep "^codegen::" \ | while read -r fn; do declare -F "$fn"; done \ | sort -n -k2 \ | cut -f1 -d' ' \ | sed 's/^codegen:://' ) } # shellcheck disable=SC2207 # safe, no functions have spaces all_codegens=($(list_codegens)) function print_codegens() { echo "available codegens:" for g in "${all_codegens[@]}"; do echo " $g" done } # Validate and accumulate flags to pass thru and codegens to run if args are # specified. flags_to_pass=() codegens_to_run=() for arg; do # Use -? to list known codegens. if [[ "${arg}" == "-?" ]]; then print_codegens exit 0 fi if [[ "${arg}" =~ ^- ]]; then flags_to_pass+=("${arg}") continue fi # Make sure each non-flag arg matches at least one codegen. nmatches=0 for t in "${all_codegens[@]}"; do if [[ "$t" =~ ${arg} ]]; then nmatches=$((nmatches+1)) # Don't run codegens twice, just keep the first match. # shellcheck disable=SC2076 # we want literal matching if [[ " ${codegens_to_run[*]} " =~ " $t " ]]; then continue fi codegens_to_run+=("$t") continue fi done if [[ ${nmatches} == 0 ]]; then echo "ERROR: no codegens match pattern '${arg}'" echo print_codegens exit 1 fi # The array-syntax abomination is to accommodate older bash. codegens_to_run+=("${matches[@]:+"${matches[@]}"}") done # If no codegens were specified, run them all. if [[ "${#codegens_to_run[@]}" == 0 ]]; then codegens_to_run=("${all_codegens[@]}") fi for g in "${codegens_to_run[@]}"; do # The array-syntax abomination is to accommodate older bash. "codegen::${g}" "${flags_to_pass[@]:+"${flags_to_pass[@]}"}" done