k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/hack/update-codegen.sh (about)

     1  #!/usr/bin/env bash
     2  # Copyright 2014 The Kubernetes Authors.
     3  #
     4  # Licensed under the Apache License, Version 2.0 (the "License");
     5  # you may not use this file except in compliance with the License.
     6  # You may obtain a copy of the License at
     7  #
     8  #     http://www.apache.org/licenses/LICENSE-2.0
     9  #
    10  # Unless required by applicable law or agreed to in writing, software
    11  # distributed under the License is distributed on an "AS IS" BASIS,
    12  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  # See the License for the specific language governing permissions and
    14  # limitations under the License.
    15  
    16  # shellcheck disable=2046 # printf word-splitting is intentional
    17  
    18  set -o errexit
    19  set -o nounset
    20  set -o pipefail
    21  
    22  # This tool wants a different default than usual.
    23  KUBE_VERBOSE="${KUBE_VERBOSE:-1}"
    24  
    25  KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
    26  source "${KUBE_ROOT}/hack/lib/init.sh"
    27  source "${KUBE_ROOT}/hack/lib/protoc.sh"
    28  cd "${KUBE_ROOT}"
    29  
    30  kube::golang::setup_env
    31  
    32  DBG_CODEGEN="${DBG_CODEGEN:-0}"
    33  GENERATED_FILE_PREFIX="${GENERATED_FILE_PREFIX:-zz_generated.}"
    34  UPDATE_API_KNOWN_VIOLATIONS="${UPDATE_API_KNOWN_VIOLATIONS:-}"
    35  API_KNOWN_VIOLATIONS_DIR="${API_KNOWN_VIOLATIONS_DIR:-"${KUBE_ROOT}/api/api-rules"}"
    36  
    37  OUT_DIR="_output"
    38  BOILERPLATE_FILENAME="hack/boilerplate/boilerplate.generatego.txt"
    39  APPLYCONFIG_PKG="k8s.io/client-go/applyconfigurations"
    40  PLURAL_EXCEPTIONS="Endpoints:Endpoints,ResourceClaimParameters:ResourceClaimParameters,ResourceClassParameters:ResourceClassParameters"
    41  
    42  # Any time we call sort, we want it in the same locale.
    43  export LC_ALL="C"
    44  
    45  # Work around for older grep tools which might have options we don't want.
    46  unset GREP_OPTIONS
    47  
    48  if [[ "${DBG_CODEGEN}" == 1 ]]; then
    49      kube::log::status "DBG: starting generated_files"
    50  fi
    51  
    52  # Generate a list of directories we don't want to play in.
    53  DIRS_TO_AVOID=()
    54  kube::util::read-array DIRS_TO_AVOID < <(
    55      git ls-files -cmo --exclude-standard -- ':!:vendor/*' ':(glob)*/**/go.work' \
    56          | while read -r F; do \
    57              echo ':!:'"$(dirname "${F}")"; \
    58          done
    59      )
    60  
    61  function git_find() {
    62      # Similar to find but faster and easier to understand.  We want to include
    63      # modified and untracked files because this might be running against code
    64      # which is not tracked by git yet.
    65      git ls-files -cmo --exclude-standard ':!:vendor/*' "${DIRS_TO_AVOID[@]}" "$@"
    66  }
    67  
    68  function git_grep() {
    69      # We want to include modified and untracked files because this might be
    70      # running against code which is not tracked by git yet.
    71      # We need vendor exclusion added at the end since it has to be part of
    72      # the pathspecs which are specified last.
    73      git grep --untracked "$@" ':!:vendor/*' "${DIRS_TO_AVOID[@]}"
    74  }
    75  
    76  # Generate a list of all files that have a `+k8s:` comment-tag.  This will be
    77  # used to derive lists of files/dirs for generation tools.
    78  if [[ "${DBG_CODEGEN}" == 1 ]]; then
    79      kube::log::status "DBG: finding all +k8s: tags"
    80  fi
    81  ALL_K8S_TAG_FILES=()
    82  kube::util::read-array ALL_K8S_TAG_FILES < <(
    83      git_grep -l \
    84          -e '^// *+k8s:'                `# match +k8s: tags` \
    85          -- \
    86          ':!:*/testdata/*'              `# not under any testdata` \
    87          ':(glob)**/*.go'               `# in any *.go file` \
    88      )
    89  if [[ "${DBG_CODEGEN}" == 1 ]]; then
    90      kube::log::status "DBG: found ${#ALL_K8S_TAG_FILES[@]} +k8s: tagged files"
    91  fi
    92  
    93  #
    94  # Code generation logic.
    95  #
    96  
    97  # protobuf generation
    98  #
    99  # Some of the later codegens depend on the results of this, so it needs to come
   100  # first in the case of regenerating everything.
   101  function codegen::protobuf() {
   102      # NOTE: All output from this script needs to be copied back to the calling
   103      # source tree.  This is managed in kube::build::copy_output in build/common.sh.
   104      # If the output set is changed update that function.
   105  
   106      local apis=()
   107      kube::util::read-array apis < <(
   108          git grep --untracked --null -l \
   109              -e '// +k8s:protobuf-gen=package' \
   110              -- \
   111              cmd pkg staging \
   112              | while read -r -d $'\0' F; do dirname "${F}"; done \
   113              | sed 's|^|k8s.io/kubernetes/|;s|k8s.io/kubernetes/staging/src/||' \
   114              | sort -u)
   115  
   116      kube::log::status "Generating protobufs for ${#apis[@]} targets"
   117      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   118          kube::log::status "DBG: generating protobufs for:"
   119          for dir in "${apis[@]}"; do
   120              kube::log::status "DBG:     $dir"
   121          done
   122      fi
   123  
   124      git_find -z \
   125          ':(glob)**/generated.proto' \
   126          ':(glob)**/generated.pb.go' \
   127          | xargs -0 rm -f
   128  
   129      if kube::protoc::check_protoc >/dev/null; then
   130        hack/_update-generated-protobuf-dockerized.sh "${apis[@]}"
   131      else
   132        kube::log::status "protoc ${PROTOC_VERSION} not found (can install with hack/install-protoc.sh); generating containerized..."
   133        build/run.sh hack/_update-generated-protobuf-dockerized.sh "${apis[@]}"
   134      fi
   135  }
   136  
   137  # Deep-copy generation
   138  #
   139  # Any package that wants deep-copy functions generated must include a
   140  # comment-tag in column 0 of one file of the form:
   141  #     // +k8s:deepcopy-gen=<VALUE>
   142  #
   143  # The <VALUE> may be one of:
   144  #     generate: generate deep-copy functions into the package
   145  #     register: generate deep-copy functions and register them with a
   146  #               scheme
   147  function codegen::deepcopy() {
   148      # Build the tool.
   149      GOPROXY=off go install \
   150          k8s.io/code-generator/cmd/deepcopy-gen
   151  
   152      # The result file, in each pkg, of deep-copy generation.
   153      local output_file="${GENERATED_FILE_PREFIX}deepcopy.go"
   154  
   155      # Find all the directories that request deep-copy generation.
   156      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   157          kube::log::status "DBG: finding all +k8s:deepcopy-gen tags"
   158      fi
   159      local tag_dirs=()
   160      kube::util::read-array tag_dirs < <( \
   161          grep -l --null '+k8s:deepcopy-gen=' "${ALL_K8S_TAG_FILES[@]}" \
   162              | while read -r -d $'\0' F; do dirname "${F}"; done \
   163              | sort -u)
   164      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   165          kube::log::status "DBG: found ${#tag_dirs[@]} +k8s:deepcopy-gen tagged dirs"
   166      fi
   167  
   168      local tag_pkgs=()
   169      for dir in "${tag_dirs[@]}"; do
   170          tag_pkgs+=("./$dir")
   171      done
   172  
   173      kube::log::status "Generating deepcopy code for ${#tag_pkgs[@]} targets"
   174      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   175          kube::log::status "DBG: running deepcopy-gen for:"
   176          for dir in "${tag_dirs[@]}"; do
   177              kube::log::status "DBG:     $dir"
   178          done
   179      fi
   180  
   181      git_find -z ':(glob)**'/"${output_file}" | xargs -0 rm -f
   182  
   183      deepcopy-gen \
   184          -v "${KUBE_VERBOSE}" \
   185          --go-header-file "${BOILERPLATE_FILENAME}" \
   186          --output-file "${output_file}" \
   187          --bounding-dirs "k8s.io/kubernetes,k8s.io/api" \
   188          "${tag_pkgs[@]}" \
   189          "$@"
   190  
   191      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   192          kube::log::status "Generated deepcopy code"
   193      fi
   194  }
   195  
   196  # Generates types_swagger_doc_generated file for the given group version.
   197  # $1: Name of the group version
   198  # $2: Path to the directory where types.go for that group version exists. This
   199  # is the directory where the file will be generated.
   200  function gen_types_swagger_doc() {
   201      local group_version="$1"
   202      local gv_dir="$2"
   203      local tmpfile
   204      tmpfile="${TMPDIR:-/tmp}/types_swagger_doc_generated.$(date +%s).go"
   205  
   206      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   207          kube::log::status "DBG: running genswaggertypedocs for ${group_version} at ${gv_dir}"
   208      fi
   209  
   210      {
   211          cat "${BOILERPLATE_FILENAME}"
   212          echo
   213          echo "package ${group_version##*/}"
   214          # Indenting here prevents the boilerplate checker from thinking this file
   215          # is generated - gofmt will fix the indents anyway.
   216          cat <<EOF
   217  
   218            // This file contains a collection of methods that can be used from go-restful to
   219            // generate Swagger API documentation for its models. Please read this PR for more
   220            // information on the implementation: https://github.com/emicklei/go-restful/pull/215
   221            //
   222            // TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if
   223            // they are on one line! For multiple line or blocks that you want to ignore use ---.
   224            // Any context after a --- is ignored.
   225            //
   226            // Those methods can be generated by using hack/update-codegen.sh
   227  
   228            // AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
   229  EOF
   230      } > "${tmpfile}"
   231  
   232      genswaggertypedocs \
   233          -s \
   234          "${gv_dir}/types.go" \
   235          -f - \
   236          >> "${tmpfile}"
   237  
   238      echo "// AUTO-GENERATED FUNCTIONS END HERE" >> "${tmpfile}"
   239  
   240      gofmt -w -s "${tmpfile}"
   241      mv "${tmpfile}" "${gv_dir}/types_swagger_doc_generated.go"
   242  }
   243  
   244  # swagger generation
   245  #
   246  # Some of the later codegens depend on the results of this, so it needs to come
   247  # first in the case of regenerating everything.
   248  function codegen::swagger() {
   249      # Build the tool
   250      GOPROXY=off go install \
   251          ./cmd/genswaggertypedocs
   252  
   253      local group_versions=()
   254      IFS=" " read -r -a group_versions <<< "meta/v1 meta/v1beta1 ${KUBE_AVAILABLE_GROUP_VERSIONS}"
   255  
   256      kube::log::status "Generating swagger for ${#group_versions[@]} targets"
   257  
   258      git_find -z ':(glob)**/types_swagger_doc_generated.go' | xargs -0 rm -f
   259  
   260      # Regenerate files.
   261      for group_version in "${group_versions[@]}"; do
   262        gen_types_swagger_doc "${group_version}" "$(kube::util::group-version-to-pkg-path "${group_version}")"
   263      done
   264  }
   265  
   266  # prerelease-lifecycle generation
   267  #
   268  # Any package that wants prerelease-lifecycle functions generated must include a
   269  # comment-tag in column 0 of one file of the form:
   270  #     // +k8s:prerelease-lifecycle-gen=true
   271  function codegen::prerelease() {
   272      # Build the tool.
   273      GOPROXY=off go install \
   274          k8s.io/code-generator/cmd/prerelease-lifecycle-gen
   275  
   276      # The result file, in each pkg, of prerelease-lifecycle generation.
   277      local output_file="${GENERATED_FILE_PREFIX}prerelease-lifecycle.go"
   278  
   279      # Find all the directories that request prerelease-lifecycle generation.
   280      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   281          kube::log::status "DBG: finding all +k8s:prerelease-lifecycle-gen tags"
   282      fi
   283      local tag_dirs=()
   284      kube::util::read-array tag_dirs < <( \
   285          grep -l --null '+k8s:prerelease-lifecycle-gen=true' "${ALL_K8S_TAG_FILES[@]}" \
   286              | while read -r -d $'\0' F; do dirname "${F}"; done \
   287              | sort -u)
   288      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   289          kube::log::status "DBG: found ${#tag_dirs[@]} +k8s:prerelease-lifecycle-gen tagged dirs"
   290      fi
   291  
   292      local tag_pkgs=()
   293      for dir in "${tag_dirs[@]}"; do
   294          tag_pkgs+=("./$dir")
   295      done
   296  
   297      kube::log::status "Generating prerelease-lifecycle code for ${#tag_pkgs[@]} targets"
   298      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   299          kube::log::status "DBG: running prerelease-lifecycle-gen for:"
   300          for dir in "${tag_dirs[@]}"; do
   301              kube::log::status "DBG:     $dir"
   302          done
   303      fi
   304  
   305      git_find -z ':(glob)**'/"${output_file}" | xargs -0 rm -f
   306  
   307      prerelease-lifecycle-gen \
   308          -v "${KUBE_VERBOSE}" \
   309          --go-header-file "${BOILERPLATE_FILENAME}" \
   310          --output-file "${output_file}" \
   311          "${tag_pkgs[@]}" \
   312          "$@"
   313  
   314      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   315          kube::log::status "Generated prerelease-lifecycle code"
   316      fi
   317  }
   318  
   319  # Defaulter generation
   320  #
   321  # Any package that wants defaulter functions generated must include a
   322  # comment-tag in column 0 of one file of the form:
   323  #     // +k8s:defaulter-gen=<VALUE>
   324  #
   325  # The <VALUE> depends on context:
   326  #     on types:
   327  #       true:  always generate a defaulter for this type
   328  #       false: never generate a defaulter for this type
   329  #     on functions:
   330  #       covers: if the function name matches SetDefault_NAME, instructs
   331  #               the generator not to recurse
   332  #     on packages:
   333  #       FIELDNAME: any object with a field of this name is a candidate
   334  #                  for having a defaulter generated
   335  function codegen::defaults() {
   336      # Build the tool.
   337      GOPROXY=off go install \
   338          k8s.io/code-generator/cmd/defaulter-gen
   339  
   340      # The result file, in each pkg, of defaulter generation.
   341      local output_file="${GENERATED_FILE_PREFIX}defaults.go"
   342  
   343      # All directories that request any form of defaulter generation.
   344      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   345          kube::log::status "DBG: finding all +k8s:defaulter-gen tags"
   346      fi
   347      local tag_dirs=()
   348      kube::util::read-array tag_dirs < <( \
   349          grep -l --null '+k8s:defaulter-gen=' "${ALL_K8S_TAG_FILES[@]}" \
   350              | while read -r -d $'\0' F; do dirname "${F}"; done \
   351              | sort -u)
   352      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   353          kube::log::status "DBG: found ${#tag_dirs[@]} +k8s:defaulter-gen tagged dirs"
   354      fi
   355  
   356      local tag_pkgs=()
   357      for dir in "${tag_dirs[@]}"; do
   358          tag_pkgs+=("./$dir")
   359      done
   360  
   361      kube::log::status "Generating defaulter code for ${#tag_pkgs[@]} targets"
   362      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   363          kube::log::status "DBG: running defaulter-gen for:"
   364          for dir in "${tag_dirs[@]}"; do
   365              kube::log::status "DBG:     $dir"
   366          done
   367      fi
   368  
   369      git_find -z ':(glob)**'/"${output_file}" | xargs -0 rm -f
   370  
   371      defaulter-gen \
   372          -v "${KUBE_VERBOSE}" \
   373          --go-header-file "${BOILERPLATE_FILENAME}" \
   374          --output-file "${output_file}" \
   375          "${tag_pkgs[@]}" \
   376          "$@"
   377  
   378      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   379          kube::log::status "Generated defaulter code"
   380      fi
   381  }
   382  
   383  # Conversion generation
   384  
   385  # Any package that wants conversion functions generated into it must
   386  # include one or more comment-tags in its `doc.go` file, of the form:
   387  #     // +k8s:conversion-gen=<INTERNAL_TYPES_DIR>
   388  #
   389  # The INTERNAL_TYPES_DIR is a project-local path to another directory
   390  # which should be considered when evaluating peer types for
   391  # conversions.  An optional additional comment of the form
   392  #     // +k8s:conversion-gen-external-types=<EXTERNAL_TYPES_DIR>
   393  #
   394  # identifies where to find the external types; if there is no such
   395  # comment then the external types are sought in the package where the
   396  # `k8s:conversion` tag is found.
   397  #
   398  # Conversions, in both directions, are generated for every type name
   399  # that is defined in both an internal types package and the external
   400  # types package.
   401  #
   402  # TODO: it might be better in the long term to make peer-types explicit in the
   403  # IDL.
   404  function codegen::conversions() {
   405      # Build the tool.
   406      GOPROXY=off go install \
   407          k8s.io/code-generator/cmd/conversion-gen
   408  
   409      # The result file, in each pkg, of conversion generation.
   410      local output_file="${GENERATED_FILE_PREFIX}conversion.go"
   411  
   412      # All directories that request any form of conversion generation.
   413      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   414          kube::log::status "DBG: finding all +k8s:conversion-gen tags"
   415      fi
   416      local tag_dirs=()
   417      kube::util::read-array tag_dirs < <(\
   418          grep -l --null '^// *+k8s:conversion-gen=' "${ALL_K8S_TAG_FILES[@]}" \
   419              | while read -r -d $'\0' F; do dirname "${F}"; done \
   420              | sort -u)
   421      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   422          kube::log::status "DBG: found ${#tag_dirs[@]} +k8s:conversion-gen tagged dirs"
   423      fi
   424  
   425      local tag_pkgs=()
   426      for dir in "${tag_dirs[@]}"; do
   427          tag_pkgs+=("./$dir")
   428      done
   429  
   430      local extra_peer_pkgs=(
   431          k8s.io/kubernetes/pkg/apis/core
   432          k8s.io/kubernetes/pkg/apis/core/v1
   433          k8s.io/api/core/v1
   434      )
   435  
   436      kube::log::status "Generating conversion code for ${#tag_pkgs[@]} targets"
   437      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   438          kube::log::status "DBG: running conversion-gen for:"
   439          for dir in "${tag_dirs[@]}"; do
   440              kube::log::status "DBG:     $dir"
   441          done
   442      fi
   443  
   444      git_find -z ':(glob)**'/"${output_file}" | xargs -0 rm -f
   445  
   446      conversion-gen \
   447          -v "${KUBE_VERBOSE}" \
   448          --go-header-file "${BOILERPLATE_FILENAME}" \
   449          --output-file "${output_file}" \
   450          $(printf -- " --extra-peer-dirs %s" "${extra_peer_pkgs[@]}") \
   451          "${tag_pkgs[@]}" \
   452          "$@"
   453  
   454      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   455          kube::log::status "Generated conversion code"
   456      fi
   457  }
   458  
   459  # $@: directories to exclude
   460  # example:
   461  #    k8s_tag_files_except foo bat/qux
   462  function k8s_tag_files_except() {
   463      for f in "${ALL_K8S_TAG_FILES[@]}"; do
   464          local excl=""
   465          for x in "$@"; do
   466              if [[ "$f" =~ "$x"/.* ]]; then
   467                  excl="true"
   468                  break
   469              fi
   470          done
   471          if [[ "${excl}" != true ]]; then
   472              echo "$f"
   473          fi
   474      done
   475  }
   476  
   477  # OpenAPI generation
   478  #
   479  # Any package that wants open-api functions generated must include a
   480  # comment-tag in column 0 of one file of the form:
   481  #     // +k8s:openapi-gen=true
   482  function codegen::openapi() {
   483      # Build the tool.
   484      GOPROXY=off go install \
   485          k8s.io/kube-openapi/cmd/openapi-gen
   486  
   487      # The result file, in each pkg, of open-api generation.
   488      local output_file="${GENERATED_FILE_PREFIX}openapi.go"
   489  
   490      local output_dir="pkg/generated/openapi"
   491      local output_pkg="k8s.io/kubernetes/${output_dir}"
   492      local known_violations_file="${API_KNOWN_VIOLATIONS_DIR}/violation_exceptions.list"
   493  
   494      local report_file="${OUT_DIR}/api_violations.report"
   495      # When UPDATE_API_KNOWN_VIOLATIONS is set to be true, let the generator to write
   496      # updated API violations to the known API violation exceptions list.
   497      if [[ "${UPDATE_API_KNOWN_VIOLATIONS}" == true ]]; then
   498          report_file="${known_violations_file}"
   499      fi
   500  
   501      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   502          kube::log::status "DBG: finding all +k8s:openapi-gen tags"
   503      fi
   504  
   505      local tag_files=()
   506      kube::util::read-array tag_files < <(
   507          k8s_tag_files_except \
   508              staging/src/k8s.io/code-generator \
   509              staging/src/k8s.io/sample-apiserver
   510          )
   511  
   512      local tag_dirs=()
   513      kube::util::read-array tag_dirs < <(
   514          grep -l --null '+k8s:openapi-gen=' "${tag_files[@]}" \
   515              | while read -r -d $'\0' F; do dirname "${F}"; done \
   516              | sort -u)
   517  
   518      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   519          kube::log::status "DBG: found ${#tag_dirs[@]} +k8s:openapi-gen tagged dirs"
   520      fi
   521  
   522      local tag_pkgs=()
   523      for dir in "${tag_dirs[@]}"; do
   524          tag_pkgs+=("./$dir")
   525      done
   526  
   527      kube::log::status "Generating openapi code"
   528      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   529          kube::log::status "DBG: running openapi-gen for:"
   530          for dir in "${tag_dirs[@]}"; do
   531              kube::log::status "DBG:     $dir"
   532          done
   533      fi
   534  
   535      git_find -z ':(glob)pkg/generated/**'/"${output_file}" | xargs -0 rm -f
   536  
   537      openapi-gen \
   538          -v "${KUBE_VERBOSE}" \
   539          --go-header-file "${BOILERPLATE_FILENAME}" \
   540          --output-file "${output_file}" \
   541          --output-dir "${output_dir}" \
   542          --output-pkg "${output_pkg}" \
   543          --report-filename "${report_file}" \
   544          "${tag_pkgs[@]}" \
   545          "$@"
   546  
   547      touch "${report_file}"
   548      local known_filename="${known_violations_file}"
   549      if ! diff -u "${known_filename}" "${report_file}"; then
   550          echo -e "ERROR:"
   551          echo -e "\tAPI rule check failed - reported violations differ from known violations"
   552          echo -e "\tPlease read api/api-rules/README.md to resolve the failure in ${known_filename}"
   553      fi
   554  
   555      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   556          kube::log::status "Generated openapi code"
   557      fi
   558  }
   559  
   560  function codegen::applyconfigs() {
   561      GOPROXY=off go install \
   562          k8s.io/kubernetes/pkg/generated/openapi/cmd/models-schema \
   563          k8s.io/code-generator/cmd/applyconfiguration-gen
   564  
   565      local ext_apis=()
   566      kube::util::read-array ext_apis < <(
   567          cd "${KUBE_ROOT}/staging/src"
   568          git_find -z ':(glob)k8s.io/api/**/types.go' \
   569              | while read -r -d $'\0' F; do dirname "${F}"; done \
   570              | sort -u)
   571      ext_apis+=("k8s.io/apimachinery/pkg/apis/meta/v1")
   572  
   573      kube::log::status "Generating apply-config code for ${#ext_apis[@]} targets"
   574      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   575          kube::log::status "DBG: running applyconfiguration-gen for:"
   576          for api in "${ext_apis[@]}"; do
   577              kube::log::status "DBG:     $api"
   578          done
   579      fi
   580  
   581      (git_grep -l --null \
   582          -e '^// Code generated by applyconfiguration-gen. DO NOT EDIT.$' \
   583          -- \
   584          ':(glob)staging/src/k8s.io/client-go/**/*.go' \
   585          || true) \
   586          | xargs -0 rm -f
   587  
   588      applyconfiguration-gen \
   589          -v "${KUBE_VERBOSE}" \
   590          --openapi-schema <(models-schema) \
   591          --go-header-file "${BOILERPLATE_FILENAME}" \
   592          --output-dir "${KUBE_ROOT}/staging/src/${APPLYCONFIG_PKG}" \
   593          --output-pkg "${APPLYCONFIG_PKG}" \
   594          "${ext_apis[@]}" \
   595          "$@"
   596  
   597      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   598          kube::log::status "Generated apply-config code"
   599      fi
   600  }
   601  
   602  function codegen::clients() {
   603      GOPROXY=off go install \
   604          k8s.io/code-generator/cmd/client-gen
   605  
   606      IFS=" " read -r -a group_versions <<< "${KUBE_AVAILABLE_GROUP_VERSIONS}"
   607      local gv_dirs=()
   608      for gv in "${group_versions[@]}"; do
   609          # add items, but strip off any leading apis/ you find to match command expectations
   610          local api_dir
   611          api_dir=$(kube::util::group-version-to-pkg-path "${gv}")
   612          local nopkg_dir=${api_dir#pkg/}
   613          nopkg_dir=${nopkg_dir#staging/src/k8s.io/api/}
   614          local pkg_dir=${nopkg_dir#apis/}
   615  
   616          # skip groups that aren't being served, clients for these don't matter
   617          if [[ " ${KUBE_NONSERVER_GROUP_VERSIONS} " == *" ${gv} "* ]]; then
   618            continue
   619          fi
   620  
   621          gv_dirs+=("${pkg_dir}")
   622      done
   623  
   624      kube::log::status "Generating client code for ${#gv_dirs[@]} targets"
   625      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   626          kube::log::status "DBG: running client-gen for:"
   627          for dir in "${gv_dirs[@]}"; do
   628              kube::log::status "DBG:     $dir"
   629          done
   630      fi
   631  
   632      (git_grep -l --null \
   633          -e '^// Code generated by client-gen. DO NOT EDIT.$' \
   634          -- \
   635          ':(glob)staging/src/k8s.io/client-go/**/*.go' \
   636          || true) \
   637          | xargs -0 rm -f
   638  
   639      client-gen \
   640          -v "${KUBE_VERBOSE}" \
   641          --go-header-file "${BOILERPLATE_FILENAME}" \
   642          --output-dir "${KUBE_ROOT}/staging/src/k8s.io/client-go" \
   643          --output-pkg="k8s.io/client-go" \
   644          --clientset-name="kubernetes" \
   645          --input-base="k8s.io/api" \
   646          --plural-exceptions "${PLURAL_EXCEPTIONS}" \
   647          --apply-configuration-package "${APPLYCONFIG_PKG}" \
   648          $(printf -- " --input %s" "${gv_dirs[@]}") \
   649          "$@"
   650  
   651      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   652          kube::log::status "Generated client code"
   653      fi
   654  }
   655  
   656  function codegen::listers() {
   657      GOPROXY=off go install \
   658          k8s.io/code-generator/cmd/lister-gen
   659  
   660      local ext_apis=()
   661      kube::util::read-array ext_apis < <(
   662          cd "${KUBE_ROOT}/staging/src"
   663          git_find -z ':(glob)k8s.io/api/**/types.go' \
   664              | while read -r -d $'\0' F; do dirname "${F}"; done \
   665              | sort -u)
   666  
   667      kube::log::status "Generating lister code for ${#ext_apis[@]} targets"
   668      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   669          kube::log::status "DBG: running lister-gen for:"
   670          for api in "${ext_apis[@]}"; do
   671              kube::log::status "DBG:     $api"
   672          done
   673      fi
   674  
   675      (git_grep -l --null \
   676          -e '^// Code generated by lister-gen. DO NOT EDIT.$' \
   677          -- \
   678          ':(glob)staging/src/k8s.io/client-go/**/*.go' \
   679          || true) \
   680          | xargs -0 rm -f
   681  
   682      lister-gen \
   683          -v "${KUBE_VERBOSE}" \
   684          --go-header-file "${BOILERPLATE_FILENAME}" \
   685          --output-dir "${KUBE_ROOT}/staging/src/k8s.io/client-go/listers" \
   686          --output-pkg "k8s.io/client-go/listers" \
   687          --plural-exceptions "${PLURAL_EXCEPTIONS}" \
   688          "${ext_apis[@]}" \
   689          "$@"
   690  
   691      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   692          kube::log::status "Generated lister code"
   693      fi
   694  }
   695  
   696  function codegen::informers() {
   697      GOPROXY=off go install \
   698          k8s.io/code-generator/cmd/informer-gen
   699  
   700      local ext_apis=()
   701      kube::util::read-array ext_apis < <(
   702          cd "${KUBE_ROOT}/staging/src"
   703          git_find -z ':(glob)k8s.io/api/**/types.go' \
   704              | while read -r -d $'\0' F; do dirname "${F}"; done \
   705              | sort -u)
   706  
   707      kube::log::status "Generating informer code for ${#ext_apis[@]} targets"
   708      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   709          kube::log::status "DBG: running informer-gen for:"
   710          for api in "${ext_apis[@]}"; do
   711              kube::log::status "DBG:     $api"
   712          done
   713      fi
   714  
   715      (git_grep -l --null \
   716          -e '^// Code generated by informer-gen. DO NOT EDIT.$' \
   717          -- \
   718          ':(glob)staging/src/k8s.io/client-go/**/*.go' \
   719          || true) \
   720          | xargs -0 rm -f
   721  
   722      informer-gen \
   723          -v "${KUBE_VERBOSE}" \
   724          --go-header-file "${BOILERPLATE_FILENAME}" \
   725          --output-dir "${KUBE_ROOT}/staging/src/k8s.io/client-go/informers" \
   726          --output-pkg "k8s.io/client-go/informers" \
   727          --single-directory \
   728          --versioned-clientset-package "k8s.io/client-go/kubernetes" \
   729          --listers-package "k8s.io/client-go/listers" \
   730          --plural-exceptions "${PLURAL_EXCEPTIONS}" \
   731          "${ext_apis[@]}" \
   732          "$@"
   733  
   734      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   735          kube::log::status "Generated informer code"
   736      fi
   737  }
   738  
   739  function indent() {
   740      while read -r X; do
   741          echo "    ${X}"
   742      done
   743  }
   744  
   745  function codegen::subprojects() {
   746      # Call generation on sub-projects.
   747      local subs=(
   748          staging/src/k8s.io/code-generator/examples
   749          staging/src/k8s.io/kube-aggregator
   750          staging/src/k8s.io/sample-apiserver
   751          staging/src/k8s.io/sample-controller
   752          staging/src/k8s.io/metrics
   753          staging/src/k8s.io/apiextensions-apiserver
   754          staging/src/k8s.io/apiextensions-apiserver/examples/client-go
   755      )
   756  
   757      local codegen
   758      codegen="${KUBE_ROOT}/staging/src/k8s.io/code-generator"
   759      for sub in "${subs[@]}"; do
   760          kube::log::status "Generating code for subproject ${sub}"
   761          pushd "${sub}" >/dev/null
   762          CODEGEN_PKG="${codegen}" \
   763          UPDATE_API_KNOWN_VIOLATIONS="${UPDATE_API_KNOWN_VIOLATIONS}" \
   764          API_KNOWN_VIOLATIONS_DIR="${API_KNOWN_VIOLATIONS_DIR}" \
   765              ./hack/update-codegen.sh > >(indent) 2> >(indent >&2)
   766          popd >/dev/null
   767      done
   768  }
   769  
   770  function codegen::protobindings() {
   771      # Each element of this array is a directory containing subdirectories which
   772      # eventually contain a file named "api.proto".
   773      local apis=(
   774          "staging/src/k8s.io/cri-api/pkg/apis/runtime"
   775  
   776          "staging/src/k8s.io/kubelet/pkg/apis/podresources"
   777  
   778          "staging/src/k8s.io/kubelet/pkg/apis/deviceplugin"
   779  
   780          "staging/src/k8s.io/kms/apis"
   781          "staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2"
   782  
   783          "staging/src/k8s.io/kubelet/pkg/apis/dra"
   784  
   785          "staging/src/k8s.io/kubelet/pkg/apis/pluginregistration"
   786          "pkg/kubelet/pluginmanager/pluginwatcher/example_plugin_apis"
   787      )
   788  
   789      kube::log::status "Generating protobuf bindings for ${#apis[@]} targets"
   790      if [[ "${DBG_CODEGEN}" == 1 ]]; then
   791          kube::log::status "DBG: generating protobuf bindings for:"
   792          for dir in "${apis[@]}"; do
   793              kube::log::status "DBG:     $dir"
   794          done
   795      fi
   796  
   797      for api in "${apis[@]}"; do
   798          git_find -z ":(glob)${api}"/'**/api.pb.go' \
   799              | xargs -0 rm -f
   800      done
   801  
   802      if kube::protoc::check_protoc >/dev/null; then
   803        hack/_update-generated-proto-bindings-dockerized.sh "${apis[@]}"
   804      else
   805        kube::log::status "protoc ${PROTOC_VERSION} not found (can install with hack/install-protoc.sh); generating containerized..."
   806        # NOTE: All output from this script needs to be copied back to the calling
   807        # source tree.  This is managed in kube::build::copy_output in build/common.sh.
   808        # If the output set is changed update that function.
   809        build/run.sh hack/_update-generated-proto-bindings-dockerized.sh "${apis[@]}"
   810      fi
   811  }
   812  
   813  #
   814  # main
   815  #
   816  
   817  function list_codegens() {
   818      (
   819          shopt -s extdebug
   820          declare -F \
   821              | cut -f3 -d' ' \
   822              | grep "^codegen::" \
   823              | while read -r fn; do declare -F "$fn"; done \
   824              | sort -n -k2 \
   825              | cut -f1 -d' ' \
   826              | sed 's/^codegen:://'
   827      )
   828  }
   829  
   830  # shellcheck disable=SC2207 # safe, no functions have spaces
   831  all_codegens=($(list_codegens))
   832  
   833  function print_codegens() {
   834      echo "available codegens:"
   835      for g in "${all_codegens[@]}"; do
   836          echo "    $g"
   837      done
   838  }
   839  
   840  # Validate and accumulate flags to pass thru and codegens to run if args are
   841  # specified.
   842  flags_to_pass=()
   843  codegens_to_run=()
   844  for arg; do
   845      # Use -? to list known codegens.
   846      if [[ "${arg}" == "-?" ]]; then
   847          print_codegens
   848          exit 0
   849      fi
   850      if [[ "${arg}" =~ ^- ]]; then
   851          flags_to_pass+=("${arg}")
   852          continue
   853      fi
   854      # Make sure each non-flag arg matches at least one codegen.
   855      nmatches=0
   856      for t in "${all_codegens[@]}"; do
   857          if [[ "$t" =~ ${arg} ]]; then
   858              nmatches=$((nmatches+1))
   859              # Don't run codegens twice, just keep the first match.
   860              # shellcheck disable=SC2076 # we want literal matching
   861              if [[ " ${codegens_to_run[*]} " =~ " $t " ]]; then
   862                  continue
   863              fi
   864              codegens_to_run+=("$t")
   865              continue
   866          fi
   867      done
   868      if [[ ${nmatches} == 0 ]]; then
   869          echo "ERROR: no codegens match pattern '${arg}'"
   870          echo
   871          print_codegens
   872          exit 1
   873      fi
   874      # The array-syntax abomination is to accommodate older bash.
   875      codegens_to_run+=("${matches[@]:+"${matches[@]}"}")
   876  done
   877  
   878  # If no codegens were specified, run them all.
   879  if [[ "${#codegens_to_run[@]}" == 0 ]]; then
   880      codegens_to_run=("${all_codegens[@]}")
   881  fi
   882  
   883  for g in "${codegens_to_run[@]}"; do
   884      # The array-syntax abomination is to accommodate older bash.
   885      "codegen::${g}" "${flags_to_pass[@]:+"${flags_to_pass[@]}"}"
   886  done