diff --git a/apis/crdpinniped/doc.go.tmpl b/apis/crdpinniped/doc.go.tmpl new file mode 100644 index 00000000..70cb2191 --- /dev/null +++ b/apis/crdpinniped/doc.go.tmpl @@ -0,0 +1,10 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +// +k8s:deepcopy-gen=package +// +groupName=crd.pinniped.dev + +// Package crdpinniped is the internal version of the API. +package crdpinniped diff --git a/apis/crdpinniped/types.go.tmpl b/apis/crdpinniped/types.go.tmpl new file mode 100644 index 00000000..615e5e8d --- /dev/null +++ b/apis/crdpinniped/types.go.tmpl @@ -0,0 +1,6 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +package crdpinniped diff --git a/apis/crdpinniped/v1alpha1/conversion.go.tmpl b/apis/crdpinniped/v1alpha1/conversion.go.tmpl new file mode 100644 index 00000000..63bc360f --- /dev/null +++ b/apis/crdpinniped/v1alpha1/conversion.go.tmpl @@ -0,0 +1,6 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +package v1alpha1 diff --git a/apis/crdpinniped/v1alpha1/defaults.go.tmpl b/apis/crdpinniped/v1alpha1/defaults.go.tmpl new file mode 100644 index 00000000..ba08404d --- /dev/null +++ b/apis/crdpinniped/v1alpha1/defaults.go.tmpl @@ -0,0 +1,14 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} diff --git a/apis/crdpinniped/v1alpha1/doc.go.tmpl b/apis/crdpinniped/v1alpha1/doc.go.tmpl new file mode 100644 index 00000000..f0de984b --- /dev/null +++ b/apis/crdpinniped/v1alpha1/doc.go.tmpl @@ -0,0 +1,13 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package +// +k8s:conversion-gen=github.com/suzerain-io/pinniped/GENERATED_PKG/apis/crdpinniped +// +k8s:defaulter-gen=TypeMeta +// +groupName=crd.pinniped.dev + +// Package v1alpha1 is the v1alpha1 version of the API. +package v1alpha1 diff --git a/apis/crdpinniped/v1alpha1/register.go.tmpl b/apis/crdpinniped/v1alpha1/register.go.tmpl new file mode 100644 index 00000000..1210c96f --- /dev/null +++ b/apis/crdpinniped/v1alpha1/register.go.tmpl @@ -0,0 +1,46 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +//nolint:gochecknoglobals,gochecknoinits +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const GroupName = "crd.pinniped.dev" + +// SchemeGroupVersion is group version used to register these objects. +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} + +var ( + SchemeBuilder runtime.SchemeBuilder + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs) +} + +// Adds the list of known types to the given scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &PinnipedDiscoveryInfo{}, + &PinnipedDiscoveryInfoList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource. +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/apis/crdpinniped/v1alpha1/types.go.tmpl b/apis/crdpinniped/v1alpha1/types.go.tmpl new file mode 100644 index 00000000..7095ad92 --- /dev/null +++ b/apis/crdpinniped/v1alpha1/types.go.tmpl @@ -0,0 +1,35 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +type PinnipedDiscoveryInfoSpec struct { + // The K8s API server URL. Required. + Server string `json:"server,omitempty"` + + // The K8s API server CA bundle. Required. + CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type PinnipedDiscoveryInfo struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PinnipedDiscoveryInfoSpec `json:"spec"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type PinnipedDiscoveryInfoList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []PinnipedDiscoveryInfo `json:"items"` +} diff --git a/apis/pinniped/doc.go.tmpl b/apis/pinniped/doc.go.tmpl new file mode 100644 index 00000000..99df8e0a --- /dev/null +++ b/apis/pinniped/doc.go.tmpl @@ -0,0 +1,10 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +// +k8s:deepcopy-gen=package +// +groupName=pinniped.dev + +// Package pinniped is the internal version of the API. +package pinniped diff --git a/apis/pinniped/register.go.tmpl b/apis/pinniped/register.go.tmpl new file mode 100644 index 00000000..c6570bc7 --- /dev/null +++ b/apis/pinniped/register.go.tmpl @@ -0,0 +1,41 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +//nolint:gochecknoglobals +package pinniped + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const GroupName = "pinniped.dev" + +// SchemeGroupVersion is group version used to register these objects. +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind. +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns back a Group qualified GroupResource. +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to the given scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &CredentialRequest{}, + &CredentialRequestList{}, + ) + return nil +} diff --git a/apis/pinniped/types.go.tmpl b/apis/pinniped/types.go.tmpl new file mode 100644 index 00000000..4c558bd6 --- /dev/null +++ b/apis/pinniped/types.go.tmpl @@ -0,0 +1,75 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +package pinniped + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +type CredentialType string + +const ( + TokenCredentialType = CredentialType("token") +) + +type CredentialRequestTokenCredential struct { + // Value of the bearer token supplied with the credential request. + Value string +} + +type CredentialRequestSpec struct { + // Type of credential. + Type CredentialType + + // Token credential (when Type == TokenCredentialType). + Token *CredentialRequestTokenCredential +} + +type CredentialRequestCredential struct { + // ExpirationTimestamp indicates a time when the provided credentials expire. + ExpirationTimestamp metav1.Time + + // Token is a bearer token used by the client for request authentication. + Token string + + // PEM-encoded client TLS certificates (including intermediates, if any). + ClientCertificateData string + + // PEM-encoded private key for the above certificate. + ClientKeyData string +} + +type CredentialRequestStatus struct { + // A Credential will be returned for a successful credential request. + // +optional + Credential *CredentialRequestCredential + + // An error message will be returned for an unsuccessful credential request. + // +optional + Message *string +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type CredentialRequest struct { + metav1.TypeMeta + metav1.ObjectMeta + + Spec CredentialRequestSpec + Status CredentialRequestStatus +} + +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// CredentialRequestList is a list of CredentialRequest objects. +type CredentialRequestList struct { + metav1.TypeMeta + metav1.ListMeta + + // Items is a list of CredentialRequests + Items []CredentialRequest +} diff --git a/apis/pinniped/v1alpha1/conversion.go.tmpl b/apis/pinniped/v1alpha1/conversion.go.tmpl new file mode 100644 index 00000000..63bc360f --- /dev/null +++ b/apis/pinniped/v1alpha1/conversion.go.tmpl @@ -0,0 +1,6 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +package v1alpha1 diff --git a/apis/pinniped/v1alpha1/defaults.go.tmpl b/apis/pinniped/v1alpha1/defaults.go.tmpl new file mode 100644 index 00000000..ba08404d --- /dev/null +++ b/apis/pinniped/v1alpha1/defaults.go.tmpl @@ -0,0 +1,14 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} diff --git a/apis/pinniped/v1alpha1/doc.go.tmpl b/apis/pinniped/v1alpha1/doc.go.tmpl new file mode 100644 index 00000000..4bea88ef --- /dev/null +++ b/apis/pinniped/v1alpha1/doc.go.tmpl @@ -0,0 +1,13 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package +// +k8s:conversion-gen=github.com/suzerain-io/pinniped/GENERATED_PKG/apis/pinniped +// +k8s:defaulter-gen=TypeMeta +// +groupName=pinniped.dev + +// Package v1alpha1 is the v1alpha1 version of the API. +package v1alpha1 diff --git a/apis/pinniped/v1alpha1/register.go.tmpl b/apis/pinniped/v1alpha1/register.go.tmpl new file mode 100644 index 00000000..b1ae3aea --- /dev/null +++ b/apis/pinniped/v1alpha1/register.go.tmpl @@ -0,0 +1,46 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +//nolint:gochecknoglobals,gochecknoinits +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const GroupName = "pinniped.dev" + +// SchemeGroupVersion is group version used to register these objects. +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} + +var ( + SchemeBuilder runtime.SchemeBuilder + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs) +} + +// Adds the list of known types to the given scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &CredentialRequest{}, + &CredentialRequestList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource. +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/apis/pinniped/v1alpha1/types.go.tmpl b/apis/pinniped/v1alpha1/types.go.tmpl new file mode 100644 index 00000000..0037021e --- /dev/null +++ b/apis/pinniped/v1alpha1/types.go.tmpl @@ -0,0 +1,74 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +type CredentialType string + +const ( + TokenCredentialType = CredentialType("token") +) + +type CredentialRequestTokenCredential struct { + // Value of the bearer token supplied with the credential request. + Value string `json:"value,omitempty" protobuf:"bytes,1,opt,name=value"` +} + +type CredentialRequestSpec struct { + // Type of credential. + Type CredentialType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type"` + + // Token credential (when Type == TokenCredentialType). + Token *CredentialRequestTokenCredential `json:"token,omitempty" protobuf:"bytes,2,opt,name=token"` +} + +type CredentialRequestCredential struct { + // ExpirationTimestamp indicates a time when the provided credentials expire. + ExpirationTimestamp metav1.Time `json:"expirationTimestamp,omitempty"` + + // Token is a bearer token used by the client for request authentication. + Token string `json:"token,omitempty"` + + // PEM-encoded client TLS certificates (including intermediates, if any). + ClientCertificateData string `json:"clientCertificateData,omitempty"` + + // PEM-encoded private key for the above certificate. + ClientKeyData string `json:"clientKeyData,omitempty"` +} + +type CredentialRequestStatus struct { + // A Credential will be returned for a successful credential request. + // +optional + Credential *CredentialRequestCredential `json:"credential,omitempty"` + + // An error message will be returned for an unsuccessful credential request. + // +optional + Message *string `json:"message,omitempty"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type CredentialRequest struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Spec CredentialRequestSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` + Status CredentialRequestStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` +} + +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// CredentialRequestList is a list of CredentialRequest objects. +type CredentialRequestList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Items []CredentialRequest `json:"items" protobuf:"bytes,2,rep,name=items"` +} diff --git a/hack/lib/codegen.sh b/hack/lib/codegen.sh deleted file mode 100755 index 982cda8d..00000000 --- a/hack/lib/codegen.sh +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2020 VMware, Inc. -# SPDX-License-Identifier: Apache-2.0 -set -euo pipefail -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" - -GOPATH="${GOPATH:-$(mktemp -d)}" - -K8S_PKG_VERSION="${K8S_PKG_VERSION:-"1.19"}" -CODEGEN_IMAGE=${CODEGEN_IMAGE:-"gcr.io/tanzu-user-authentication/k8s-code-generator-${K8S_PKG_VERSION}:latest"} - -BASE_PKG="github.com/suzerain-io/pinniped" - -# This script assumes that your current working directory is the top of the module -# in which you would like to generate code. -MOD_DIR=$(pwd) - -function codegen::ensure_module_in_gopath() { - # This should be something like "kubernetes/1.19/api". - local pkg_name="$(realpath "--relative-to=$ROOT" "$MOD_DIR")" - - # Use --canonicalize-missing to since pkg_name could end up as "." - this would - # lead to a pkg_gosrc_path like "foo/bar/bat/." which ln(1) (below) does not like. - local pkg_gosrc_path="$(realpath --canonicalize-missing "${GOPATH}/src/${BASE_PKG}/${pkg_name}")" - - if [[ ! -e "${pkg_gosrc_path}" ]]; then - mkdir -p "$(dirname "${pkg_gosrc_path}")" - ln -s "${ROOT}/${pkg_name}" "${pkg_gosrc_path}" - fi -} - -function codegen::invoke_code_generator() { - local generator_command="${1}" - local mod_basename_for_version="${2}" - shift 2 # generator args are now in $@ - - if [ "${BASH_VERSINFO[0]}" -lt 5 ]; then - echo "ERROR: invalid BASH version" - echo " using v${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}.${BASH_VERSINFO[2]} @ ${BASH}" - echo " require v5.0.0+" - exit 1 - fi - - bash "${GOPATH}/src/k8s.io/code-generator/${generator_command}.sh" \ - "$@" \ - --go-header-file "${ROOT}/hack/boilerplate.go.txt" | - sed "s|^|${mod_basename_for_version} > ${generator_command} > |" -} - -function codegen::generate_for_module() { - local mod_basename_for_version="${1}" - - case "${mod_basename_for_version}" in - 1.19/api) - echo "GENERATING CODE for $mod_basename_for_version" - codegen::invoke_code_generator generate-groups "${mod_basename_for_version}" \ - deepcopy,defaulter \ - "${BASE_PKG}/kubernetes/1.19/api/generated" \ - "${BASE_PKG}/kubernetes/1.19/api/apis" \ - "pinniped:v1alpha1 crdpinniped:v1alpha1" - codegen::invoke_code_generator generate-internal-groups "${mod_basename_for_version}" \ - deepcopy,defaulter,conversion,openapi \ - "${BASE_PKG}/kubernetes/1.19/api/generated" \ - "${BASE_PKG}/kubernetes/1.19/api/apis" \ - "${BASE_PKG}/kubernetes/1.19/api/apis" \ - "pinniped:v1alpha1 crdpinniped:v1alpha1" - ;; - 1.19/client-go) - echo "GENERATING CODE for $mod_basename_for_version" - codegen::invoke_code_generator generate-groups "${mod_basename_for_version}" \ - client,lister,informer \ - "${BASE_PKG}/kubernetes/1.19/client-go" \ - "${BASE_PKG}/kubernetes/1.19/api/apis" \ - "pinniped:v1alpha1 crdpinniped:v1alpha1" - ;; - *) - echo "Skipping $mod_basename_for_version because it does not contain any code to generate" - esac -} - -function codegen::generate() { - local mod_basename_for_version - mod_basename_for_version="${K8S_PKG_VERSION}/$(basename "${MOD_DIR}")" - - codegen::ensure_module_in_gopath - codegen::generate_for_module "${mod_basename_for_version}" -} - -function codegen::verify() { - local have_stash='' - if [[ "$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')" -ne "0" ]]; then - # git stash requires the user.email and user.name to be set. We set these at - # a global scope so they don't overwrite the .git/config in the mounted repo - # from the host. - git config --global user.email "codegen_verify@whatever.com" - git config --global user.name "Codegen Verify" - git stash --all >/dev/null 2>&1 && have_stash=1 - fi - - codegen::generate - - failure=0 - if [[ "$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')" -eq "0" ]]; then - echo "Generated code in ${MOD_DIR} up to date." - else - echo "Generated code in ${MOD_DIR} is out of date." - echo "Please run hack/module.sh codegen" - git diff "${ROOT}" - git checkout "${ROOT}" - failure=1 - fi - - if [[ -n "${have_stash}" ]]; then - git stash pop >/dev/null 2>&1 - fi - - if [[ "$failure" -eq 1 ]]; then - exit 1 - fi -} - -function codegen::usage() { - echo "Error: must be specified" - echo " ${BASH_SOURCE[0]} [codegen::generate, codegen::verify]" - exit 1 -} - -function codegen::main() { - local codegen_command="${1}" - - if [[ -n "${CONTAINED:-}" ]]; then - "${codegen_command}" - else - DOCKER_ROOT_DIR="/tmp/${RANDOM}/${BASE_PKG}" - DOCKER_MOD_DIR="${DOCKER_ROOT_DIR}/$(realpath "--relative-to=$ROOT" "$MOD_DIR")" - - docker run --rm \ - --env CONTAINED=1 \ - --env MOD_DIR="${DOCKER_MOD_DIR}" \ - --volume "${ROOT}:${DOCKER_ROOT_DIR}" \ - --workdir "${DOCKER_MOD_DIR}" \ - "${CODEGEN_IMAGE}" \ - "${DOCKER_ROOT_DIR}/hack/lib/$(basename "${BASH_SOURCE[0]}")" \ - "${codegen_command}" - fi -} - -codegen::main "${1:-"codegen::usage"}" diff --git a/hack/lib/kube-versions.txt b/hack/lib/kube-versions.txt new file mode 100644 index 00000000..c2a52777 --- /dev/null +++ b/hack/lib/kube-versions.txt @@ -0,0 +1,3 @@ +1.17.9 +1.18.6 +1.19.0-rc.0 diff --git a/hack/lib/update-codegen.sh b/hack/lib/update-codegen.sh new file mode 100755 index 00000000..9a3802b9 --- /dev/null +++ b/hack/lib/update-codegen.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env bash + +# Copyright 2020 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +KUBE_VERSIONS=("$@") +BASE_PKG="github.com/suzerain-io/pinniped" +export GO111MODULE="on" + +# If we're not running in a container, assume that we want to loop over and run each build +# in a container. +if [[ -z "${CONTAINED:-}" ]]; then + for kubeVersion in "${KUBE_VERSIONS[@]}"; do + # CODEGEN_IMAGE is the container image to use when running + CODEGEN_IMAGE="gcr.io/tanzu-user-authentication/k8s-code-generator-$(echo "$kubeVersion" | cut -d"." -f1-2):latest" + + echo "generating code for ${kubeVersion} using ${CODEGEN_IMAGE}..." + docker run --rm \ + --env CONTAINED=1 \ + --volume "${ROOT}:/go/src/${BASE_PKG}" \ + --workdir "/go/src/${BASE_PKG}" \ + "${CODEGEN_IMAGE}" \ + "/go/src/${BASE_PKG}/hack/lib/$(basename "${BASH_SOURCE[0]}")" \ + "${kubeVersion}" \ + | sed "s|^|${kubeVersion} > |" + done + exit 0 +fi + +# Now that we know we are running in the nested container, expect there to be only +# a single Kubernetes version +if [[ "${#KUBE_VERSIONS[@]}" -ne 1 ]]; then + echo "when running in a container, we can only generate for a single kubernetes version" >&2 + exit 1 +fi + +# KUBE_VERSION is the full version (e.g., '1.19.0-rc.0'). +KUBE_VERSION="${KUBE_VERSIONS[0]}" +export KUBE_VERSION + +# KUBE_MINOR_VERSION is just the major/minor version (e.g., '1.19'). +KUBE_MINOR_VERSION="$(echo "${KUBE_VERSION}" | cut -d"." -f1-2)" +export KUBE_MINOR_VERSION + +# KUBE_MODULE_VERSION is just version of client libraries (e.g., 'v0.19.9-rc-0'). +KUBE_MODULE_VERSION="v0.$(echo "${KUBE_VERSION}" | cut -d '.' -f 2-)" +export KUBE_MODULE_VERSION + +# Start by picking an output directory and deleting any previously-generated code. +OUTPUT_DIR="${ROOT}/generated/${KUBE_MINOR_VERSION}" +rm -rf "${OUTPUT_DIR}" +mkdir -p "${OUTPUT_DIR}" +cd "${OUTPUT_DIR}" + +echo "running in container to generate ${KUBE_VERSION} into ${OUTPUT_DIR}..." + +# Next, copy in the base definitions of our APIs from ./apis into the generated directory, substituting some +# variables in the template files and renaming them to strip the `.tmpl` extension. +cp -R "${ROOT}/apis" "${OUTPUT_DIR}/apis" +find "${OUTPUT_DIR}" -type f -exec sed -i "s|GENERATED_PKG|generated/${KUBE_MINOR_VERSION}|g" {} \; +find "${OUTPUT_DIR}" -type f -name '*.tmpl' -exec bash -c 'mv "$0" "${0%.tmpl}"' {} \; + +# Make the generated API code its own Go module. +echo "generating ${OUTPUT_DIR}/apis/go.mod..." +cat << EOF > "${OUTPUT_DIR}/apis/go.mod" +// This go.mod file is generated by ./hack/codegen.sh. +module ${BASE_PKG}/generated/${KUBE_MINOR_VERSION}/apis + +go 1.13 + +require ( + k8s.io/apimachinery ${KUBE_MODULE_VERSION} +) +EOF + +# Make the generated client code its own Go module. +echo "generating ${OUTPUT_DIR}/client/go.mod..." +mkdir client +cat << EOF > "./client/go.mod" +// This go.mod file is generated by ./hack/codegen.sh. +module ${BASE_PKG}/generated/${KUBE_MINOR_VERSION}/client + +go 1.13 + +require ( + github.com/go-openapi/spec v0.19.9 + k8s.io/api ${KUBE_MODULE_VERSION} + k8s.io/apimachinery ${KUBE_MODULE_VERSION} + k8s.io/client-go ${KUBE_MODULE_VERSION} + k8s.io/apimachinery ${KUBE_MODULE_VERSION} +) + +replace ${BASE_PKG}/generated/${KUBE_MINOR_VERSION}/apis => ../apis +EOF + +# Generate API-related code for our public API groups +echo "generating API-related code for our public API groups..." +(cd apis && + bash "${GOPATH}/src/k8s.io/code-generator/generate-groups.sh" \ + deepcopy \ + "${BASE_PKG}/generated/${KUBE_MINOR_VERSION}/apis" \ + "${BASE_PKG}/generated/${KUBE_MINOR_VERSION}/apis" \ + "pinniped:v1alpha1 crdpinniped:v1alpha1" \ + --go-header-file "${ROOT}/hack/boilerplate.go.txt" 2>&1 | sed "s|^|gen-api > |" +) + +# Generate API-related code for our internal API groups +echo "generating API-related code for our internal API groups..." +(cd apis && + bash "${GOPATH}/src/k8s.io/code-generator/generate-internal-groups.sh" \ + deepcopy,defaulter,conversion,openapi \ + "${BASE_PKG}/generated/${KUBE_MINOR_VERSION}/client" \ + "${BASE_PKG}/generated/${KUBE_MINOR_VERSION}/apis" \ + "${BASE_PKG}/generated/${KUBE_MINOR_VERSION}/apis" \ + "pinniped:v1alpha1 crdpinniped:v1alpha1" \ + --go-header-file "${ROOT}/hack/boilerplate.go.txt" 2>&1 | sed "s|^|gen-int-api > |" +) + +# Tidy up the .../apis module +echo "tidying ${OUTPUT_DIR}/apis/go.mod..." +(cd apis && go mod tidy 2>&1 | sed "s|^|go-mod-tidy > |") + +# Generate client code for our public API groups +echo "generating client code for our public API groups..." +(cd client && + bash "${GOPATH}/src/k8s.io/code-generator/generate-groups.sh" \ + client,lister,informer \ + "${BASE_PKG}/generated/${KUBE_MINOR_VERSION}/client" \ + "${BASE_PKG}/generated/${KUBE_MINOR_VERSION}/apis" \ + "pinniped:v1alpha1 crdpinniped:v1alpha1" \ + --go-header-file "${ROOT}/hack/boilerplate.go.txt" 2>&1 | sed "s|^|gen-client > |" +) + +# Tidy up the .../client module +echo "tidying ${OUTPUT_DIR}/client/go.mod..." +(cd client && go mod tidy 2>&1 | sed "s|^|go-mod-tidy > |") diff --git a/hack/lib/verify-codegen.sh b/hack/lib/verify-codegen.sh new file mode 100755 index 00000000..7fce755d --- /dev/null +++ b/hack/lib/verify-codegen.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# Copyright 2020 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +KUBE_VERSIONS=("$@") + +GENERATED_DIR="${ROOT}/generated" +BACKUP_DIR="${GENERATED_DIR}.bak" + +# Move the originally generated directory to a backup location +mv "${GENERATED_DIR}" "${BACKUP_DIR}" +mkdir "${GENERATED_DIR}" + +# At exit (even on error), copy it back +cleanup() { + rm -r "${GENERATED_DIR}" + mv -f "${BACKUP_DIR}" "${GENERATED_DIR}" +} +trap "cleanup" EXIT SIGINT + +# Run the code generation into a new empty `./generated` directory. +"${ROOT}/hack/lib/update-codegen.sh" "${KUBE_VERSIONS[@]}" + +# Diff each of the chosen Kubernetes versions (but avoid comparing any other versions). +echo "diffing ${GENERATED_DIR} against freshly generated codegen" +ret=0 +for kubeVersion in "${KUBE_VERSIONS[@]}"; do + kubeMinorVersion="$(echo "${kubeVersion}" | cut -d"." -f1-2)" + generatedVersionDir="${GENERATED_DIR}/${kubeMinorVersion}" + backupVersionDir="${BACKUP_DIR}/${kubeMinorVersion}" + diff -Naupr "${generatedVersionDir}" "${backupVersionDir}" || ret=$? +done + +# If any of the versions differed, exit nonzero with an error message. +if [[ $ret -eq 0 ]] +then + echo "${GENERATED_DIR} up to date." +else + echo "${GENERATED_DIR} is out of date. Please run hack/update.sh" + exit 1 +fi diff --git a/hack/module.sh b/hack/module.sh index 43ea49c8..7794e882 100755 --- a/hack/module.sh +++ b/hack/module.sh @@ -35,20 +35,12 @@ function unittest_cmd() { echo "${cmd} -count 1 -short -race ./..." } -function codegen_cmd() { - echo "${ROOT}/hack/lib/codegen.sh codegen::generate" -} - -function codegen_verify_cmd() { - echo "${ROOT}/hack/lib/codegen.sh codegen::verify" -} - function with_modules() { local cmd_function="${1}" cmd="$(${cmd_function})" pushd "${ROOT}" >/dev/null - for mod_file in $(find . -maxdepth 4 -name go.mod | sort); do + for mod_file in $(find . -maxdepth 4 -not -path "./generated/*" -name go.mod | sort); do mod_dir="$(dirname "${mod_file}")" ( echo "=> " @@ -61,7 +53,7 @@ function with_modules() { function usage() { echo "Error: must be specified" - echo " module.sh [tidy, lint, test, unittest, codegen, codegen_verify]" + echo " module.sh [tidy, lint, test, unittest]" exit 1 } @@ -79,12 +71,6 @@ function main() { 'unittest' | 'unittests' | 'units' | 'unit') with_modules 'unittest_cmd' ;; - 'codegen' | 'codegens') - with_modules 'codegen_cmd' - ;; - 'codegen_verify' | 'verify_codegen') - with_modules 'codegen_verify_cmd' - ;; *) usage ;; diff --git a/hack/update.sh b/hack/update.sh index 32345a0e..d4fa4e91 100755 --- a/hack/update.sh +++ b/hack/update.sh @@ -5,5 +5,5 @@ set -euo pipefail ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" +xargs "$ROOT/hack/lib/update-codegen.sh" < "${ROOT}/hack/lib/kube-versions.txt" "$ROOT/hack/module.sh" tidy -"$ROOT/hack/module.sh" codegen diff --git a/hack/verify.sh b/hack/verify.sh index 5f199725..5d8bc6a3 100755 --- a/hack/verify.sh +++ b/hack/verify.sh @@ -5,5 +5,5 @@ set -euo pipefail ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" +xargs "$ROOT/hack/lib/verify-codegen.sh" < "${ROOT}/hack/lib/kube-versions.txt" "$ROOT/hack/module.sh" lint -"$ROOT/hack/module.sh" codegen_verify