This is an automated email from the ASF dual-hosted git repository.
zhongxjian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-kubernetes.git
The following commit(s) were added to refs/heads/master by this push:
new 68c5886e Added build tools (#840)
68c5886e is described below
commit 68c5886e0a14676fec275449f7cd94671bf78f8c
Author: mfordjody <[email protected]>
AuthorDate: Mon Jan 5 02:14:11 2026 +0800
Added build tools (#840)
---
go.mod | 6 +-
go.sum | 10 ++
kubetype-gen | Bin 0 -> 11522162 bytes
tools/build-tools/docker/Dockerfile | 159 +++++++++++++++++
tools/cmd/kubetype-gen/boilerplate.go.txt | 14 ++
tools/cmd/kubetype-gen/generators/naming.go | 49 ++++++
tools/cmd/kubetype-gen/generators/package.go | 57 ++++++
tools/cmd/kubetype-gen/generators/register.go | 134 ++++++++++++++
tools/cmd/kubetype-gen/generators/types.go | 140 +++++++++++++++
tools/cmd/kubetype-gen/main.go | 47 +++++
tools/cmd/kubetype-gen/metadata/metadata.go | 225 ++++++++++++++++++++++++
tools/cmd/kubetype-gen/scanner/scanner.go | 242 ++++++++++++++++++++++++++
12 files changed, 1081 insertions(+), 2 deletions(-)
diff --git a/go.mod b/go.mod
index 719cf87c..6efb735a 100644
--- a/go.mod
+++ b/go.mod
@@ -41,7 +41,9 @@ require (
github.com/go-git/go-git/v5 v5.13.1
github.com/go-jose/go-jose/v4 v4.1.2
github.com/gogo/protobuf v1.3.2
+ github.com/golang/glog v1.2.5
github.com/golang/protobuf v1.5.4
+ github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a
github.com/google/go-cmp v0.7.0
github.com/google/go-containerregistry v0.20.6
github.com/google/uuid v1.6.0
@@ -67,6 +69,7 @@ require (
golang.org/x/term v0.34.0
golang.org/x/time v0.12.0
gomodules.xyz/jsonpatch/v2 v2.5.0
+ google.golang.org/genproto/googleapis/api
v0.0.0-20250811230008-5f3141c8851a
google.golang.org/genproto/googleapis/rpc
v0.0.0-20250826171959-ef028d996bc1
google.golang.org/grpc v1.75.1
google.golang.org/protobuf v1.36.9
@@ -78,6 +81,7 @@ require (
k8s.io/apiextensions-apiserver v0.34.1
k8s.io/apimachinery v0.34.1
k8s.io/client-go v0.34.1
+ k8s.io/gengo v0.0.0-20251215205346-5ee0d033ba5b
k8s.io/klog/v2 v2.130.1
k8s.io/kubectl v0.33.3
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d
@@ -158,7 +162,6 @@ require (
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da //
indirect
- github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a //
indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
@@ -248,7 +251,6 @@ require (
golang.org/x/sync v0.17.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/tools v0.36.0 // indirect
- google.golang.org/genproto/googleapis/api
v0.0.0-20250811230008-5f3141c8851a // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
diff --git a/go.sum b/go.sum
index 208bf724..581e42ff 100644
--- a/go.sum
+++ b/go.sum
@@ -300,6 +300,7 @@ github.com/go-jose/go-jose/v4 v4.1.2/go.mod
h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8
github.com/go-kit/log v0.1.0/go.mod
h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.4.0/go.mod
h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod
h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-logr/logr v0.2.0/go.mod
h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod
h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@@ -326,6 +327,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod
h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
github.com/golang-jwt/jwt/v4 v4.5.2
h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod
h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod
h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
+github.com/golang/glog v1.2.5/go.mod
h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod
h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod
h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -365,6 +368,7 @@ github.com/google/go-containerregistry v0.20.2/go.mod
h1:z38EKdKh4h7IP2gSfUUqEva
github.com/google/go-querystring v1.1.0
h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod
h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0/go.mod
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a
h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod
h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
@@ -614,6 +618,7 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod
h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6
github.com/spf13/jwalterweatherman v1.1.0
h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod
h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod
h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5/go.mod
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6/go.mod
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
github.com/spf13/pflag v1.0.7/go.mod
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -812,6 +817,7 @@ golang.org/x/tools
v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod
h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod
h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod
h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod
h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod
h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -903,6 +909,9 @@ k8s.io/cli-runtime v0.33.3
h1:Dgy4vPjNIu8LMJBSvs8W0LcdV0PX/8aGG1DA1W8lklA=
k8s.io/cli-runtime v0.33.3/go.mod
h1:yklhLklD4vLS8HNGgC9wGiuHWze4g7x6XQZ+8edsKEo=
k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
+k8s.io/gengo v0.0.0-20251215205346-5ee0d033ba5b
h1:X0Afwan8Q1l7bMcNgh6DAah2jKCQ2irT7EoAXIChFqk=
+k8s.io/gengo v0.0.0-20251215205346-5ee0d033ba5b/go.mod
h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
+k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3
h1:liMHz39T5dJO1aOKHLvwaCjDbf07wVh6yaUlTpunnkE=
@@ -927,5 +936,6 @@ sigs.k8s.io/randfill v1.0.0
h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod
h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0
h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod
h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/kubetype-gen b/kubetype-gen
new file mode 100755
index 00000000..3ec817ea
Binary files /dev/null and b/kubetype-gen differ
diff --git a/tools/build-tools/docker/Dockerfile
b/tools/build-tools/docker/Dockerfile
new file mode 100644
index 00000000..ee40b9c5
--- /dev/null
+++ b/tools/build-tools/docker/Dockerfile
@@ -0,0 +1,159 @@
+# hadolint global ignore=SC2086
+
+################
+# Binary tools
+################
+ARG GOLANG_IMAGE=golang:1.24-bookworm
+# hadolint ignore=DL3006
+FROM ${GOLANG_IMAGE} AS binary_tools_context_base
+# TARGETARCH is an automatic platform ARG enabled by Docker BuildKit.
+ARG TARGETARCH
+
+# Dubbo Kubernetes version/SHA for kubetype-gen
+ARG DUBBO_KUBERNETES_VERSION=main
+
+# Pinned versions for code generators
+ENV K8S_CODE_GENERATOR_VERSION=1.29.4
+
+ENV GO111MODULE=on
+ENV GOPROXY=https://proxy.golang.org
+
+WORKDIR /tmp
+ENV GOPATH=/tmp/go
+# Avoid any attempts to automatically download a new Go version
+ENV GOTOOLCHAIN=local
+
+ENV OUTDIR=/out
+RUN mkdir -p ${OUTDIR}/usr/bin
+RUN mkdir -p ${OUTDIR}/usr/local
+
+# Update distro and install dependencies
+# hadolint ignore=DL3042
+RUN --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \
+ --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ rm -f /etc/apt/apt.conf.d/docker-clean \
+ && apt-get update \
+ && apt-get -y --no-install-recommends install \
+ build-essential \
+ ca-certificates \
+ curl \
+ git \
+ unzip \
+ xz-utils
+
+# Cleanup stuff we don't need in the final image
+RUN rm -fr /usr/local/go/doc
+RUN rm -fr /usr/local/go/test
+RUN rm -fr /usr/local/go/api
+RUN rm -fr /usr/local/go/bin/godoc
+RUN rm -fr /usr/local/go/bin/gofmt
+
+# Go tools: part 1 - Kubernetes code generators
+FROM binary_tools_context_base AS go_tools_1
+
+# Build and install Kubernetes code generators
+RUN --mount=type=cache,target=/tmp/go/pkg/mod \
+ --mount=type=cache,target=/root/.cache/go-build \
+ CGO_ENABLED=0 go install -ldflags="-extldflags -static -s -w" \
+
k8s.io/code-generator/cmd/applyconfiguration-gen@kubernetes-${K8S_CODE_GENERATOR_VERSION}
\
+
k8s.io/code-generator/cmd/defaulter-gen@kubernetes-${K8S_CODE_GENERATOR_VERSION}
\
+
k8s.io/code-generator/cmd/client-gen@kubernetes-${K8S_CODE_GENERATOR_VERSION} \
+
k8s.io/code-generator/cmd/lister-gen@kubernetes-${K8S_CODE_GENERATOR_VERSION} \
+
k8s.io/code-generator/cmd/informer-gen@kubernetes-${K8S_CODE_GENERATOR_VERSION}
\
+
k8s.io/code-generator/cmd/deepcopy-gen@kubernetes-${K8S_CODE_GENERATOR_VERSION}
\
+
k8s.io/code-generator/cmd/go-to-protobuf@kubernetes-${K8S_CODE_GENERATOR_VERSION}
+
+# Go tools: part 2 - Protobuf generators and Dubbo Kubernetes tools
+FROM binary_tools_context_base AS go_tools_2
+
+# Build Dubbo Kubernetes kubetype-gen tool from source
+# Clone the repository and checkout the specified version
+RUN --mount=type=cache,target=/tmp/go/pkg/mod \
+ --mount=type=cache,target=/root/.cache/go-build \
+ if [ "${DUBBO_KUBERNETES_VERSION}" != "main" ]; then \
+ git clone --single-branch --branch "${DUBBO_KUBERNETES_VERSION}" \
+ https://github.com/apache/dubbo-kubernetes.git /tmp/dubbo-kubernetes
2>/dev/null || \
+ (git clone https://github.com/apache/dubbo-kubernetes.git
/tmp/dubbo-kubernetes && \
+ cd /tmp/dubbo-kubernetes && git checkout
"${DUBBO_KUBERNETES_VERSION}"); \
+ else \
+ git clone --single-branch --branch main \
+ https://github.com/apache/dubbo-kubernetes.git /tmp/dubbo-kubernetes
2>/dev/null || \
+ git clone https://github.com/apache/dubbo-kubernetes.git
/tmp/dubbo-kubernetes; \
+ fi && \
+ cd /tmp/dubbo-kubernetes && \
+ test -f go.mod && test -d tools/cmd/kubetype-gen || \
+ (echo "Error: Required files not found. Listing:" && ls -la && ls -la
tools/cmd/ 2>&1 && exit 1) && \
+ go mod download && \
+ CGO_ENABLED=0 go install -ldflags="-extldflags -static -s -w" \
+ ./tools/cmd/kubetype-gen
+
+#############
+# Base OS
+#############
+
+FROM ubuntu:noble AS base_os_context
+
+ENV DEBIAN_FRONTEND=noninteractive
+ENV OUTDIR=/out
+
+# Install minimal dependencies for code generation
+# hadolint ignore=DL3008
+RUN --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \
+ --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ rm -f /etc/apt/apt.conf.d/docker-clean \
+ && apt-get update && apt-get install -y --no-install-recommends \
+ ca-certificates \
+ git \
+ make
+
+# Clean up stuff we don't need in the final image
+RUN rm -rf /var/lib/apt/lists/* \
+ && rm -fr /usr/share/doc \
+ && rm -fr /usr/share/man \
+ && rm -fr /tmp/*
+
+# Prepare final output image
+FROM scratch AS build_tools
+
+# Version from build arguments
+ARG VERSION
+
+# Labels used by Docker
+LABEL "org.apache.dubbo.repo"="https://github.com/apache/dubbo-kubernetes"
+LABEL "org.apache.dubbo.version"="${VERSION}"
+
+# General
+ENV HOME=/home
+ENV LANG=C.UTF-8
+
+# Go support
+ENV GO111MODULE=on
+# Avoid any attempts to automatically download a new Go version
+ENV GOTOOLCHAIN=local
+ENV GOPROXY=https://proxy.golang.org
+ENV GOSUMDB=sum.golang.org
+ENV GOROOT=/usr/local/go
+ENV GOPATH=/go
+ENV GOCACHE=/gocache
+ENV GOBIN=/gobin
+ENV PATH=/usr/local/go/bin:/gobin:/usr/bin:$PATH
+
+# Create the file system
+COPY --link --from=base_os_context / /
+COPY --link --from=binary_tools_context_base /out/ /
+COPY --link --from=binary_tools_context_base /usr/local/go /usr/local/go
+COPY --link --from=go_tools_1 /tmp/go/bin/* /usr/bin/
+COPY --link --from=go_tools_2 /tmp/go/bin/* /usr/bin/
+
+# Create mountpoints
+RUN mkdir -p /go && \
+ mkdir -p /gocache && \
+ mkdir -p /gobin && \
+ mkdir -p /home/.cache
+
+RUN chmod -R 777 /go && \
+ chmod -R 777 /gocache && \
+ chmod -R 777 /gobin && \
+ chmod -R 777 /home/.cache
+
+WORKDIR /
diff --git a/tools/cmd/kubetype-gen/boilerplate.go.txt
b/tools/cmd/kubetype-gen/boilerplate.go.txt
new file mode 100644
index 00000000..969ee92e
--- /dev/null
+++ b/tools/cmd/kubetype-gen/boilerplate.go.txt
@@ -0,0 +1,14 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You 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.
diff --git a/tools/cmd/kubetype-gen/generators/naming.go
b/tools/cmd/kubetype-gen/generators/naming.go
new file mode 100644
index 00000000..b06a884a
--- /dev/null
+++ b/tools/cmd/kubetype-gen/generators/naming.go
@@ -0,0 +1,49 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package generators
+
+import (
+ "strings"
+
+ "k8s.io/gengo/namer"
+)
+
+// NameSystems used by the kubetype generator
+func NameSystems(generatedPackage string, tracker namer.ImportTracker)
namer.NameSystems {
+ return namer.NameSystems{
+ "public": namer.NewPublicNamer(0),
+ "raw": namer.NewRawNamer(generatedPackage, tracker),
+ "publicPlural": namer.NewPublicPluralNamer(map[string]string{}),
+ "lower": newLowerCaseNamer(0),
+ }
+}
+
+// DefaultNameSystem to use if none is specified
+func DefaultNameSystem() string {
+ return "public"
+}
+
+func newLowerCaseNamer(prependPackageNames int, ignoreWords ...string)
*namer.NameStrategy {
+ n := &namer.NameStrategy{
+ Join: namer.Joiner(namer.IL, strings.ToLower),
+ IgnoreWords: map[string]bool{},
+ PrependPackageNames: prependPackageNames,
+ }
+ for _, w := range ignoreWords {
+ n.IgnoreWords[w] = true
+ }
+ return n
+}
diff --git a/tools/cmd/kubetype-gen/generators/package.go
b/tools/cmd/kubetype-gen/generators/package.go
new file mode 100644
index 00000000..55b4adab
--- /dev/null
+++ b/tools/cmd/kubetype-gen/generators/package.go
@@ -0,0 +1,57 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package generators
+
+import (
+ "fmt"
+
+ "k8s.io/gengo/generator"
+ "k8s.io/gengo/types"
+
+ "github.com/apache/dubbo-kubernetes/tools/cmd/kubetype-gen/metadata"
+)
+
+// NewPackageGenerator generates source for a scanned package, specifically
k8s styled doc.go, types.go and register.go files
+func NewPackageGenerator(source metadata.PackageMetadata, boilerplate []byte)
generator.Package {
+ return &generator.DefaultPackage{
+ PackageName: source.TargetPackage().Name,
+ PackagePath: source.TargetPackage().Path,
+ HeaderText: boilerplate,
+ PackageDocumentation: []byte(fmt.Sprintf(`
+// Package has auto-generated kube type wrappers for raw types.
+// +k8s:openapi-gen=true
+// +k8s:deepcopy-gen=package
+// +groupName=%s
+`, source.GroupVersion().Group)),
+ FilterFunc: func(c *generator.Context, t *types.Type) bool {
+ for _, it := range source.RawTypes() {
+ if t == it {
+ return true
+ }
+ }
+ return false
+ },
+ GeneratorList: []generator.Generator{
+ // generate types.go
+ NewTypesGenerator(source),
+ // generate register.go
+ NewRegisterGenerator(source),
+ generator.DefaultGen{
+ OptionalName: "doc",
+ },
+ },
+ }
+}
diff --git a/tools/cmd/kubetype-gen/generators/register.go
b/tools/cmd/kubetype-gen/generators/register.go
new file mode 100644
index 00000000..8b2169bf
--- /dev/null
+++ b/tools/cmd/kubetype-gen/generators/register.go
@@ -0,0 +1,134 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package generators
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+
+ "k8s.io/gengo/generator"
+ "k8s.io/gengo/namer"
+ "k8s.io/gengo/types"
+
+ "github.com/apache/dubbo-kubernetes/tools/cmd/kubetype-gen/metadata"
+)
+
+type registerGenerator struct {
+ generator.DefaultGen
+ source metadata.PackageMetadata
+ imports namer.ImportTracker
+}
+
+// NewRegisterGenerator creates a new generator for creating k8s style
register.go files
+func NewRegisterGenerator(source metadata.PackageMetadata) generator.Generator
{
+ return ®isterGenerator{
+ DefaultGen: generator.DefaultGen{
+ OptionalName: "register",
+ },
+ source: source,
+ imports: generator.NewImportTracker(),
+ }
+}
+
+func (g *registerGenerator) Namers(c *generator.Context) namer.NameSystems {
+ return NameSystems(g.source.TargetPackage().Path, g.imports)
+}
+
+func (g *registerGenerator) PackageConsts(c *generator.Context) []string {
+ return []string{
+ fmt.Sprintf("GroupName = \"%s\"",
g.source.GroupVersion().Group),
+ }
+}
+
+func (g *registerGenerator) PackageVars(c *generator.Context) []string {
+ schemeBuilder := bytes.Buffer{}
+ w := bufio.NewWriter(&schemeBuilder)
+ sw := generator.NewSnippetWriter(w, c, "$", "$")
+ m := map[string]interface{}{
+ "NewSchemeBuilder": c.Universe.Function(types.Name{Name:
"NewSchemeBuilder", Package: "k8s.io/apimachinery/pkg/runtime"}),
+ }
+ sw.Do("SchemeBuilder = $.NewSchemeBuilder|raw$(addKnownTypes)", m)
+ w.Flush()
+ return []string{
+ fmt.Sprintf("SchemeGroupVersion = schema.GroupVersion{Group:
GroupName, Version: \"%s\"}", g.source.GroupVersion().Version),
+ schemeBuilder.String(),
+ "localSchemeBuilder = &SchemeBuilder",
+ "AddToScheme = localSchemeBuilder.AddToScheme",
+ }
+}
+
+func (g *registerGenerator) Imports(c *generator.Context) []string {
+ return g.imports.ImportLines()
+}
+
+func (g registerGenerator) Finalize(c *generator.Context, w io.Writer) error {
+ sw := generator.NewSnippetWriter(w, c, "$", "$")
+ var lowerCaseSchemeKubeTypes, camelCaseSchemeKubeTypes
[]metadata.KubeType
+ for _, k := range g.source.AllKubeTypes() {
+ if isLowerCaseScheme(k.Tags()) {
+ lowerCaseSchemeKubeTypes =
append(lowerCaseSchemeKubeTypes, k)
+ } else {
+ camelCaseSchemeKubeTypes =
append(camelCaseSchemeKubeTypes, k)
+ }
+ }
+ m := map[string]interface{}{
+ "GroupResource": c.Universe.Type(types.Name{Name:
"GroupResource", Package: "k8s.io/apimachinery/pkg/runtime/schema"}),
+ "Scheme": c.Universe.Type(types.Name{Name:
"Scheme", Package: "k8s.io/apimachinery/pkg/runtime"}),
+ "AddToGroupVersion":
c.Universe.Function(types.Name{Name: "AddToGroupVersion", Package:
"k8s.io/apimachinery/pkg/apis/meta/v1"}),
+ "CamelCaseSchemeKubeTypes": camelCaseSchemeKubeTypes,
+ "LowerCaseSchemeKubeTypes": lowerCaseSchemeKubeTypes,
+ }
+ sw.Do(resourceFuncTemplate, m)
+ sw.Do(addKnownTypesFuncTemplate, m)
+
+ return sw.Error()
+}
+
+// isLowerCaseScheme checks if the kubetype is reflected as lower case in
Kubernetes scheme.
+// This is a workaround as Dubbo CRDs should have CamelCase scheme in
Kubernetes, e.g. `VirtualService` instead of `virtualservice`
+func isLowerCaseScheme(tags []string) bool {
+ for _, s := range tags {
+ if s == "kubetype-gen:lowerCaseScheme" {
+ return true
+ }
+ }
+ return false
+}
+
+const resourceFuncTemplate = `
+func Resource(resource string) $.GroupResource|raw$ {
+ return SchemeGroupVersion.WithResource(resource).GroupResource()
+}
+`
+
+const addKnownTypesFuncTemplate = `
+func addKnownTypes(scheme *$.Scheme|raw$) error {
+ scheme.AddKnownTypes(SchemeGroupVersion,
+ $- range .CamelCaseSchemeKubeTypes $
+ &$ .Type|raw ${},
+ &$ .Type|raw $List{},
+ $- end $
+ )
+ $- range .LowerCaseSchemeKubeTypes $
+ scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("$ .Type|lower
$"), &$ .Type|raw ${})
+ scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("$ .Type|lower
$List"), &$ .Type|raw $List{})
+ $- end $
+ $.AddToGroupVersion|raw$(scheme, SchemeGroupVersion)
+ return nil
+}
+`
diff --git a/tools/cmd/kubetype-gen/generators/types.go
b/tools/cmd/kubetype-gen/generators/types.go
new file mode 100644
index 00000000..b4a3f484
--- /dev/null
+++ b/tools/cmd/kubetype-gen/generators/types.go
@@ -0,0 +1,140 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package generators
+
+import (
+ "io"
+ "slices"
+ "strings"
+
+ "k8s.io/gengo/generator"
+ "k8s.io/gengo/namer"
+ "k8s.io/gengo/types"
+
+ "github.com/apache/dubbo-kubernetes/tools/cmd/kubetype-gen/metadata"
+)
+
+type typesGenerator struct {
+ generator.DefaultGen
+ source metadata.PackageMetadata
+ imports namer.ImportTracker
+}
+
+// NewTypesGenerator creates a new generator for creating k8s style types.go
files
+func NewTypesGenerator(source metadata.PackageMetadata) generator.Generator {
+ return &typesGenerator{
+ DefaultGen: generator.DefaultGen{
+ OptionalName: "types",
+ },
+ source: source,
+ imports: generator.NewImportTracker(),
+ }
+}
+
+func (g *typesGenerator) Namers(c *generator.Context) namer.NameSystems {
+ return NameSystems(g.source.TargetPackage().Path, g.imports)
+}
+
+func (g *typesGenerator) Imports(c *generator.Context) []string {
+ return g.imports.ImportLines()
+}
+
+// extracts values for Name and Package from "dubbostatus-override" in the
comments
+func statusOverrideFromComments(commentLines []string) (string, string, bool) {
+ // ServiceEntry has a unique status type which includes addresses for
auto allocated IPs, substitute DubboServiceEntryStatus
+ // for DubboStatus when type is ServiceEntry
+ if index := slices.IndexFunc(commentLines, func(comment string) bool {
+ return strings.Contains(comment, dubboStatusOveride)
+ }); index != -1 {
+ statusOverrideLine := commentLines[index]
+ statusOverridSplit := strings.Split(statusOverrideLine, ":")
+ if len(statusOverridSplit) == 2 {
+ overrideName := statusOverridSplit[1]
+ return strings.TrimSpace(overrideName),
"github.com/apache/dubbo-kubernetes/api/meta/v1alpha1", true
+ } else if len(statusOverridSplit) == 3 {
+ overrideName := statusOverridSplit[1]
+ overridePackage := statusOverridSplit[2]
+ return strings.TrimSpace(overrideName),
strings.TrimSpace(overridePackage), true
+ }
+ }
+ return "", "", false
+}
+
+func (g *typesGenerator) GenerateType(c *generator.Context, t *types.Type, w
io.Writer) error {
+ kubeTypes := g.source.KubeTypes(t)
+ sw := generator.NewSnippetWriter(w, c, "$", "$")
+ m := map[string]interface{}{
+ "KubeType": nil,
+ "RawType": t,
+ "TypeMeta": c.Universe.Type(types.Name{Name: "TypeMeta",
Package: "k8s.io/apimachinery/pkg/apis/meta/v1"}),
+ "ObjectMeta": c.Universe.Type(types.Name{Name: "ObjectMeta",
Package: "k8s.io/apimachinery/pkg/apis/meta/v1"}),
+ "ListMeta": c.Universe.Type(types.Name{Name: "ListMeta",
Package: "k8s.io/apimachinery/pkg/apis/meta/v1"}),
+ "DubboStatus": c.Universe.Type(types.Name{Name: "DubboStatus",
Package: "github.com/apache/dubbo-kubernetes/api/meta/v1alpha1"}),
+ }
+ for _, kubeType := range kubeTypes {
+ localM := m
+ // name, package, found :=
typeFromComments(kubeType.RawType().CommentLines)
+ if name, packageName, found :=
statusOverrideFromComments(kubeType.RawType().CommentLines); found {
+ localM["DubboStatus"] =
c.Universe.Type(types.Name{Name: name, Package: packageName})
+ }
+
+ // make sure local types get imports generated for them to
prevent reusing their local name for real imports,
+ // e.g. generating into package v1alpha1, while also importing
from another package ending with v1alpha1.
+ // adding the import here will ensure the imports will be
something like, precedingpathv1alpha1.
+ g.imports.AddType(kubeType.Type())
+ localM["KubeType"] = kubeType
+ sw.Do(kubeTypeTemplate, localM)
+ }
+ return sw.Error()
+}
+
+const (
+ dubboStatusOveride = `dubbostatus-override:`
+ kubeTypeTemplate = `
+$- range .RawType.SecondClosestCommentLines $
+// $ . $
+$- end $
+$- range .KubeType.Tags $
+// +$ . $
+$- end $
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+$ range .RawType.CommentLines $
+// $ . $
+$- end $
+type $.KubeType.Type|public$ struct {
+ $.TypeMeta|raw$ ` + "`" + `json:",inline"` + "`" + `
+ // +optional
+ $.ObjectMeta|raw$ ` + "`" + `json:"metadata,omitempty"
protobuf:"bytes,1,opt,name=metadata"` + "`" + `
+
+ // Spec defines the implementation of this definition.
+ // +optional
+ Spec $.RawType|raw$ ` + "`" + `json:"spec,omitempty"
protobuf:"bytes,2,opt,name=spec"` + "`" + `
+
+ Status $.DubboStatus|raw$ ` + "`" + `json:"status,omitempty"` + "`" + `
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// $.KubeType.Type|public$List is a collection of
$.KubeType.Type|publicPlural$.
+type $.KubeType.Type|public$List struct {
+ $.TypeMeta|raw$ ` + "`" + `json:",inline"` + "`" + `
+ // +optional
+ $.ListMeta|raw$ ` + "`" + `json:"metadata,omitempty"
protobuf:"bytes,1,opt,name=metadata"` + "`" + `
+ Items []*$.KubeType.Type|raw$ ` + "`" + `json:"items"
protobuf:"bytes,2,rep,name=items"` + "`" + `
+}
+`
+)
diff --git a/tools/cmd/kubetype-gen/main.go b/tools/cmd/kubetype-gen/main.go
new file mode 100644
index 00000000..8a1852ff
--- /dev/null
+++ b/tools/cmd/kubetype-gen/main.go
@@ -0,0 +1,47 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "os"
+
+ "github.com/golang/glog"
+ "k8s.io/gengo/args"
+
+ "github.com/apache/dubbo-kubernetes/tools/cmd/kubetype-gen/generators"
+ "github.com/apache/dubbo-kubernetes/tools/cmd/kubetype-gen/scanner"
+)
+
+func main() {
+ arguments := args.Default()
+
+ arguments.GeneratedByCommentTemplate = "// Code generated by
kubetype-gen. DO NOT EDIT."
+
+ // Don't default the file header
+ // arguments.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(),
"github.com/apache/dubbo-kubernetes/tools/cmd/kubetype-gen/boilerplate.go.txt")
+
+ scanner := scanner.Scanner{}
+
+ if err := arguments.Execute(
+ generators.NameSystems("", nil),
+ generators.DefaultNameSystem(),
+ scanner.Scan,
+ ); err != nil {
+ glog.Errorf("Error: %v", err)
+ os.Exit(1)
+ }
+ glog.V(2).Info("Completed successfully.")
+}
diff --git a/tools/cmd/kubetype-gen/metadata/metadata.go
b/tools/cmd/kubetype-gen/metadata/metadata.go
new file mode 100644
index 00000000..2b77c0c1
--- /dev/null
+++ b/tools/cmd/kubetype-gen/metadata/metadata.go
@@ -0,0 +1,225 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package metadata
+
+import (
+ "fmt"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "github.com/golang/glog"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/gengo/types"
+)
+
+// KubeType is the interface representing a type to be generated.
+type KubeType interface {
+ RawType() *types.Type
+ Type() *types.Type
+ Tags() []string
+}
+
+// PackageMetadata is the interface used to provide source data used by the
package generators.
+type PackageMetadata interface {
+ // GroupVersion is the k8s Group/Version to use for the generated types.
+ GroupVersion() *schema.GroupVersion
+
+ // TargetPackage is the package into which the k8s types will be
generated.
+ TargetPackage() *types.Package
+
+ // RawTypes is the list of types for which k8s types should be
generated.
+ RawTypes() []*types.Type
+
+ // KubeTypes is the list of k8s types to be generated for the given
rawType.
+ KubeTypes(rawType *types.Type) []KubeType
+
+ // AllKubeTypes is the list of all k8s types to be generated
+ AllKubeTypes() []KubeType
+
+ // AddMetadata is used to add metadata collected by the scanner.
+ AddMetadataForType(rawType *types.Type, kubeTypes ...KubeType) error
+
+ // Validate is used to validate the metadata prior to generation
+ Validate() []error
+}
+
+// Store is used to store/access the source metadata collected by the scanner
+type Store interface {
+ // MetadataForGV returns the package metadata associated with the
Group/Version
+ MetadataForGV(gv *schema.GroupVersion) PackageMetadata
+
+ // AllMetadata returns the source metadata.
+ AllMetadata() []PackageMetadata
+
+ // Validate is used to validate the metadata prior to generation
+ Validate() []error
+}
+
+type kubeTypeMetadata struct {
+ rawType *types.Type
+ kubeType *types.Type
+ tags []string
+}
+
+type packageMetadata struct {
+ groupVersion *schema.GroupVersion
+ targetPackage *types.Package
+ rawTypes []*types.Type
+ allKubeTypes []KubeType
+ kubeTypesForRawType map[*types.Type][]KubeType
+}
+
+type metadataStore struct {
+ baseOutputPackage *types.Package
+ universe *types.Universe
+ metadataForGV map[string]PackageMetadata
+ metadata []PackageMetadata
+}
+
+// NewMetadataStore returns a new store used for collecting source metadata
used by the generator.
+func NewMetadataStore(baseOutputPackage *types.Package, universe
*types.Universe) Store {
+ return &metadataStore{
+ baseOutputPackage: baseOutputPackage,
+ universe: universe,
+ metadataForGV: map[string]PackageMetadata{},
+ metadata: []PackageMetadata{},
+ }
+}
+
+func (s *metadataStore) MetadataForGV(gv *schema.GroupVersion) PackageMetadata
{
+ simpleGV := schema.GroupVersion{Group: strings.SplitN(gv.Group, ".",
2)[0], Version: gv.Version}
+ existing := s.metadataForGV[simpleGV.String()]
+ if existing == nil {
+ glog.V(5).Infof("Creating new PackageMetadata for
Group/Version %s", gv)
+ existing = &packageMetadata{
+ groupVersion: gv,
+ targetPackage: s.createTargetPackage(gv),
+ rawTypes: []*types.Type{},
+ allKubeTypes: []KubeType{},
+ kubeTypesForRawType: map[*types.Type][]KubeType{},
+ }
+ s.metadataForGV[simpleGV.String()] = existing
+ s.metadata = append(s.metadata, existing)
+ } else if gv.Group != existing.GroupVersion().Group {
+ glog.Errorf("Overlapping packages for Group/Versions %s and
%s", gv, existing.GroupVersion())
+ return nil
+ }
+ return existing
+}
+
+func (s *metadataStore) AllMetadata() []PackageMetadata {
+ return s.metadata
+}
+
+func (s *metadataStore) Validate() []error {
+ errorSlice := []error{}
+ for _, pm := range s.metadata {
+ errorSlice = append(errorSlice, pm.Validate()...)
+ }
+ return errorSlice
+}
+
+func (s *metadataStore) createTargetPackage(gv *schema.GroupVersion)
*types.Package {
+ groupPath := strings.SplitN(gv.Group, ".", 2)
+ targetPackage :=
s.universe.Package(filepath.Join(s.baseOutputPackage.Path, groupPath[0],
gv.Version))
+ targetPackage.Name = gv.Version
+ return targetPackage
+}
+
+func (m *packageMetadata) GroupVersion() *schema.GroupVersion {
+ return m.groupVersion
+}
+
+func (m *packageMetadata) TargetPackage() *types.Package {
+ return m.targetPackage
+}
+
+func (m *packageMetadata) RawTypes() []*types.Type {
+ return m.rawTypes
+}
+
+func (m *packageMetadata) KubeTypes(rawType *types.Type) []KubeType {
+ return m.kubeTypesForRawType[rawType]
+}
+
+func (m *packageMetadata) AllKubeTypes() []KubeType {
+ return m.allKubeTypes
+}
+
+func (m *packageMetadata) AddMetadataForType(rawType *types.Type, kubeTypes
...KubeType) error {
+ if _, exists := m.kubeTypesForRawType[rawType]; exists {
+ // we should never get here. this means we've scanned a type
twice
+ return fmt.Errorf("type %s already added to scanned metadata",
rawType)
+ }
+
+ m.rawTypes = append(m.rawTypes, rawType)
+ m.kubeTypesForRawType[rawType] = kubeTypes
+ m.allKubeTypes = append(m.allKubeTypes, kubeTypes...)
+
+ return nil
+}
+
+func (m *packageMetadata) Validate() []error {
+ duplicates := map[*types.Type][]*types.Type{}
+
+ // check for duplicates
+ sort.Slice(m.allKubeTypes, func(i, j int) bool {
+ comp := strings.Compare(m.allKubeTypes[i].Type().Name.Name,
m.allKubeTypes[j].Type().Name.Name)
+ if comp == 0 {
+ if sources, exists :=
duplicates[m.allKubeTypes[i].Type()]; exists {
+ duplicates[m.allKubeTypes[i].Type()] =
append(sources, m.allKubeTypes[i].RawType())
+ } else {
+ duplicates[m.allKubeTypes[i].Type()] =
[]*types.Type{m.allKubeTypes[j].RawType(), m.allKubeTypes[i].RawType()}
+ }
+ return false
+ }
+ return comp < 0
+ })
+ if len(duplicates) > 0 {
+ errorSlice := make([]error, 0, len(duplicates))
+ for kubeType, rawTypes := range duplicates {
+ errorSlice = append(errorSlice, fmt.Errorf("duplicate
kube types specified for %s. duplicated by: %v", kubeType, rawTypes))
+ }
+ return errorSlice
+ }
+ return []error{}
+}
+
+// NewKubeType returns a new KubeType object representing the source type, the
target type and its comment tags
+func NewKubeType(rawType *types.Type, kubeType *types.Type, tags []string)
KubeType {
+ return &kubeTypeMetadata{
+ rawType: rawType,
+ kubeType: kubeType,
+ tags: tags,
+ }
+}
+
+func (k *kubeTypeMetadata) RawType() *types.Type {
+ return k.rawType
+}
+
+func (k *kubeTypeMetadata) Type() *types.Type {
+ return k.kubeType
+}
+
+func (k *kubeTypeMetadata) Tags() []string {
+ return k.tags
+}
+
+func (k *kubeTypeMetadata) String() string {
+ return fmt.Sprintf("%s => %s%s", k.rawType, k.kubeType, k.tags)
+}
diff --git a/tools/cmd/kubetype-gen/scanner/scanner.go
b/tools/cmd/kubetype-gen/scanner/scanner.go
new file mode 100644
index 00000000..154be885
--- /dev/null
+++ b/tools/cmd/kubetype-gen/scanner/scanner.go
@@ -0,0 +1,242 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package scanner
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/golang/glog"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/gengo/args"
+ "k8s.io/gengo/generator"
+ "k8s.io/gengo/types"
+
+ "github.com/apache/dubbo-kubernetes/tools/cmd/kubetype-gen/generators"
+ "github.com/apache/dubbo-kubernetes/tools/cmd/kubetype-gen/metadata"
+)
+
+const (
+ // enabledTagName is the root tag used to identify types that need a
corresponding kube type generated
+ enabledTagName = "kubetype-gen"
+
+ // groupVersionTagName is the tag used to identify the k8s
group/version associated with the generated types.
+ groupVersionTagName = enabledTagName + ":groupVersion"
+
+ // kubeTypeTagName is used to identify the name(s) of the types to be
generated from the type with this tag.
+ // If this tag is not present, the k8s type will have the same name as
the source type. If this tag is specified
+ // multiple times, a k8s type will be generated for each value.
+ kubeTypeTagName = enabledTagName + ":kubeType"
+
+ // kubeTagsTagTemplate is used to identify a comment tag that should be
added to the generated kubeType. This
+ // allows different sets of tags to be used when a single type is the
source for multiple kube types (e.g. where one
+ // is namespaced and another is not). The tag should not be prefixed
with '+', as this will be added by the
+ // generator. This may be specified multiple times, once for each tag
to be added to the generated type.
+ kubeTagsTagTemplate = enabledTagName + ":%s:tag"
+)
+
+// Scanner is used to scan input packages for types with kubetype-gen tags
+type Scanner struct {
+ arguments *args.GeneratorArgs
+ context *generator.Context
+}
+
+// Scan the input packages for types with kubetype-gen tags
+func (s *Scanner) Scan(c *generator.Context, arguments *args.GeneratorArgs)
generator.Packages {
+ s.arguments = arguments
+ s.context = c
+
+ boilerplate, err := arguments.LoadGoBoilerplate()
+ if err != nil {
+ glog.Fatalf("Failed loading boilerplate: %v", err)
+ }
+
+ // scan input packages for kubetype-gen
+ metadataStore := metadata.NewMetadataStore(s.getBaseOutputPackage(),
&c.Universe)
+ fail := false
+
+ glog.V(5).Info("Scanning input packages")
+ for _, input := range c.Inputs {
+ glog.V(5).Infof("Scanning package %s", input)
+ pkg := c.Universe[input]
+ if pkg == nil {
+ glog.Warningf("Package not found: %s", input)
+ continue
+ }
+ if strings.HasPrefix(arguments.OutputPackagePath, pkg.Path) {
+ glog.Warningf("Ignoring package %s as it is located in
the output package %s", pkg.Path, arguments.OutputPackagePath)
+ continue
+ }
+
+ pkgTags := types.ExtractCommentTags("+", pkg.DocComments)
+
+ // group/version for generated types from this package
+ defaultGV, err := s.getGroupVersion(pkgTags, nil)
+ if err != nil {
+ glog.Errorf("Could not calculate Group/Version for
package %s: %v", pkg.Path, err)
+ fail = true
+ } else if defaultGV != nil {
+ if len(defaultGV.Group) == 0 {
+ glog.Errorf("Invalid Group/Version for package
%s, Group not specified for Group/Version: %v", pkg.Path, defaultGV)
+ fail = true
+ } else {
+ glog.V(5).Infof("Default Group/Version for
package: %s", defaultGV)
+ }
+ }
+
+ // scan package for types that need kube types generated
+ for _, t := range pkg.Types {
+ comments := make([]string, 0,
len(t.CommentLines)+len(t.SecondClosestCommentLines))
+ comments = append(comments, t.CommentLines...)
+ comments = append(comments,
t.SecondClosestCommentLines...)
+ typeTags := types.ExtractCommentTags("+", comments)
+ if _, exists := typeTags[enabledTagName]; exists {
+ var gv *schema.GroupVersion
+ gv, err = s.getGroupVersion(typeTags, defaultGV)
+ if err != nil {
+ glog.Errorf("Could not calculate
Group/Version for type %s: %v", t, err)
+ fail = true
+ continue
+ } else if gv == nil || len(gv.Group) == 0 {
+ glog.Errorf("Invalid Group/Version for
type %s: %s", t, gv)
+ fail = true
+ continue
+ }
+ versions := extractVersions(comments)
+ // The kubegen doesn't natively handle type
aliases we use, so hack it in
+ for _, v := range versions {
+ gv := *gv
+ gv.Version = v
+ packageMetadata :=
metadataStore.MetadataForGV(&gv)
+ if packageMetadata == nil {
+ glog.Errorf("Could not create
metadata for type: %s", t)
+ fail = true
+ continue
+ }
+
+ kubeTypes :=
s.createKubeTypesForType(t, packageMetadata.TargetPackage())
+ glog.V(5).Infof("Kube types %v will be
generated with Group/Version %s, for raw type in %s", kubeTypes, gv, t)
+ err =
packageMetadata.AddMetadataForType(t, kubeTypes...)
+ if err != nil {
+ glog.Errorf("Error adding
metadata source for %s: %v", t, err)
+ fail = true
+ }
+ }
+ }
+ }
+ }
+
+ glog.V(5).Info("Finished scanning input packages")
+
+ validationErrors := metadataStore.Validate()
+ if len(validationErrors) > 0 {
+ for _, validationErr := range validationErrors {
+ glog.Error(validationErr)
+ }
+ fail = true
+ }
+ if fail {
+ glog.Exit("Errors occurred while scanning input. See previous
output for details.")
+ }
+
+ generatorPackages := []generator.Package{}
+ for _, source := range metadataStore.AllMetadata() {
+ if len(source.RawTypes()) == 0 {
+ glog.Warningf("Skipping generation of %s, no types to
generate", source.GroupVersion())
+ continue
+ }
+ glog.V(2).Infof("Adding package generator for %s.",
source.GroupVersion())
+ generatorPackages = append(generatorPackages,
generators.NewPackageGenerator(source, boilerplate))
+ }
+ return generatorPackages
+}
+
+func extractVersions(comments []string) []string {
+ var versions []string
+ for _, line := range comments {
+ // Looking for something like
'+cue-gen:Simple:versions:v1,v1alpha'
+ if strings.HasPrefix(line, "+cue-gen:") {
+ items := strings.Split(line, ":")
+ if len(items) != 4 {
+ continue
+ }
+ if items[2] == "versions" {
+ versions = append(versions,
strings.Split(items[3], ",")...)
+ }
+ }
+ }
+ return versions
+}
+
+func (s *Scanner) getGroupVersion(tags map[string][]string, defaultGV
*schema.GroupVersion) (*schema.GroupVersion, error) {
+ if value, exists := tags[groupVersionTagName]; exists && len(value) > 0
{
+ gv, err := schema.ParseGroupVersion(value[0])
+ if err == nil {
+ return &gv, nil
+ }
+ return nil, fmt.Errorf("invalid group version '%s' specified:
%v", value[0], err)
+ }
+ return defaultGV, nil
+}
+
+func (s *Scanner) getBaseOutputPackage() *types.Package {
+ return s.context.Universe.Package(s.arguments.OutputPackagePath)
+}
+
+func (s *Scanner) createKubeTypesForType(t *types.Type, outputPackage
*types.Package) []metadata.KubeType {
+ namesForType := s.kubeTypeNamesForType(t)
+ newKubeTypes := make([]metadata.KubeType, 0, len(namesForType))
+ for _, name := range namesForType {
+ tags := s.getTagsForKubeType(t, name)
+ newKubeTypes = append(newKubeTypes, metadata.NewKubeType(t,
s.context.Universe.Type(types.Name{Name: name, Package: outputPackage.Path}),
tags))
+ }
+ return newKubeTypes
+}
+
+func (s *Scanner) kubeTypeNamesForType(t *types.Type) []string {
+ names := []string{}
+ comments := make([]string, 0,
len(t.CommentLines)+len(t.SecondClosestCommentLines))
+ comments = append(comments, t.CommentLines...)
+ comments = append(comments, t.SecondClosestCommentLines...)
+ tags := types.ExtractCommentTags("+", comments)
+ if value, exists := tags[kubeTypeTagName]; exists {
+ if len(value) == 0 || len(value[0]) == 0 {
+ glog.Errorf("Invalid value specified for +%s in type
%s. Using default name %s.", kubeTypeTagName, t, t.Name.Name)
+ names = append(names, t.Name.Name)
+ } else {
+ for _, name := range value {
+ if len(name) > 0 {
+ names = append(names, name)
+ }
+ }
+ }
+ } else {
+ names = append(names, t.Name.Name)
+ }
+ return names
+}
+
+func (s *Scanner) getTagsForKubeType(t *types.Type, name string) []string {
+ tagName := fmt.Sprintf(kubeTagsTagTemplate, name)
+ comments := make([]string, 0,
len(t.CommentLines)+len(t.SecondClosestCommentLines))
+ comments = append(comments, t.CommentLines...)
+ comments = append(comments, t.SecondClosestCommentLines...)
+ tags := types.ExtractCommentTags("+", comments)
+ if value, exists := tags[tagName]; exists {
+ return value
+ }
+ return []string{}
+}