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 &registerGenerator{
+               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{}
+}

Reply via email to