Skip to content

Commit 6a84463

Browse files
authored
[navi] add discovery init mesh config v2 (#769)
1 parent 020d3aa commit 6a84463

File tree

32 files changed

+2094
-111
lines changed

32 files changed

+2094
-111
lines changed

go.mod

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,12 @@ require (
100100
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
101101
github.com/aws/smithy-go v1.20.3 // indirect
102102
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20230522190001-adf1bafd791a // indirect
103+
github.com/beorn7/perks v1.0.1 // indirect
103104
github.com/blang/semver/v4 v4.0.0 // indirect
104105
github.com/buildpacks/imgutil v0.0.0-20230626185301-726f02e4225c // indirect
105106
github.com/buildpacks/lifecycle v0.17.0 // indirect
106107
github.com/cespare/xxhash v1.1.0 // indirect
108+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
107109
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
108110
github.com/cloudflare/circl v1.3.7 // indirect
109111
github.com/containerd/containerd v1.7.27 // indirect
@@ -125,6 +127,7 @@ require (
125127
github.com/go-errors/errors v1.5.1 // indirect
126128
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
127129
github.com/go-logr/logr v1.4.3 // indirect
130+
github.com/go-logr/stdr v1.2.2 // indirect
128131
github.com/go-openapi/jsonpointer v0.21.1 // indirect
129132
github.com/go-openapi/jsonreference v0.21.0 // indirect
130133
github.com/go-openapi/swag v0.23.1 // indirect
@@ -177,8 +180,6 @@ require (
177180
github.com/morikuni/aec v1.0.0 // indirect
178181
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
179182
github.com/nikolalohinski/gonja v1.5.3 // indirect
180-
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
181-
github.com/onsi/gomega v1.37.0 // indirect
182183
github.com/opencontainers/go-digest v1.0.0 // indirect
183184
github.com/opencontainers/image-spec v1.1.1 // indirect
184185
github.com/opencontainers/runc v1.1.7 // indirect
@@ -190,6 +191,7 @@ require (
190191
github.com/pjbgf/sha1cd v0.3.0 // indirect
191192
github.com/pkoukk/tiktoken-go v0.1.6 // indirect
192193
github.com/planetscale/vtprotobuf v0.6.1-0.20240409071808-615f978279ca // indirect
194+
github.com/prometheus/client_golang v1.22.0 // indirect
193195
github.com/prometheus/client_model v0.6.2 // indirect
194196
github.com/prometheus/common v0.65.0 // indirect
195197
github.com/prometheus/procfs v0.16.1 // indirect
@@ -215,7 +217,16 @@ require (
215217
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
216218
github.com/xlab/treeprint v1.2.0 // indirect
217219
github.com/yargevad/filepathx v1.0.0 // indirect
220+
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
221+
go.opentelemetry.io/otel v1.36.0 // indirect
222+
go.opentelemetry.io/otel/exporters/prometheus v0.57.0 // indirect
223+
go.opentelemetry.io/otel/metric v1.36.0 // indirect
224+
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
225+
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
226+
go.opentelemetry.io/otel/trace v1.36.0 // indirect
218227
go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // indirect
228+
go.uber.org/multierr v1.11.0 // indirect
229+
go.uber.org/zap v1.27.0 // indirect
219230
go.yaml.in/yaml/v2 v2.4.2 // indirect
220231
go.yaml.in/yaml/v3 v3.0.3 // indirect
221232
golang.org/x/mod v0.25.0 // indirect
@@ -230,6 +241,7 @@ require (
230241
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
231242
gopkg.in/inf.v0 v0.9.1 // indirect
232243
gopkg.in/ini.v1 v1.67.0 // indirect
244+
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
233245
gopkg.in/warnings.v0 v0.1.2 // indirect
234246
gopkg.in/yaml.v2 v2.4.0 // indirect
235247
k8s.io/cli-runtime v0.33.2 // indirect

go.sum

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod
276276
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
277277
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
278278
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
279+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
279280
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
280281
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
281282
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@@ -338,8 +339,8 @@ github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lw
338339
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
339340
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
340341
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
341-
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
342-
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
342+
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a h1:rDA3FfmxwXR+BVKKdz55WwMJ1pD2hJQNW31d+l3mPk4=
343+
github.com/google/pprof v0.0.0-20250501235452-c0086092b71a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
343344
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
344345
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
345346
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
@@ -410,6 +411,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
410411
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
411412
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
412413
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
414+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
415+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
413416
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
414417
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
415418
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@@ -479,6 +482,7 @@ github.com/nikolalohinski/gonja v1.5.3 h1:GsA+EEaZDZPGJ8JtpeGN78jidhOlxeJROpqMT9
479482
github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4=
480483
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
481484
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
485+
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
482486
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
483487
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
484488
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@@ -644,10 +648,12 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
644648
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
645649
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
646650
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
647-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
648-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
651+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
652+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
649653
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
650654
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
655+
go.opentelemetry.io/otel/exporters/prometheus v0.57.0 h1:AHh/lAP1BHrY5gBwk8ncc25FXWm/gmmY3BX258z5nuk=
656+
go.opentelemetry.io/otel/exporters/prometheus v0.57.0/go.mod h1:QpFWz1QxqevfjwzYdbMb4Y1NnlJvqSGwyuU0B4iuc9c=
651657
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
652658
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
653659
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
@@ -664,6 +670,10 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
664670
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
665671
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
666672
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
673+
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
674+
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
675+
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
676+
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
667677
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
668678
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
669679
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
@@ -819,6 +829,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
819829
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
820830
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
821831
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
832+
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
833+
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
822834
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
823835
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
824836
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
@@ -840,6 +852,8 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
840852
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
841853
istio.io/api v1.26.3 h1:/TiA7bJi24yBQSgpLy5vHhFkobf4DWS1L+CuUxNk4os=
842854
istio.io/api v1.26.3/go.mod h1:DTVGH6CLXj5W8FF9JUD3Tis78iRgT1WeuAnxfTz21Wg=
855+
istio.io/istio v0.0.0-20250803052519-90ec2958505f h1:Yx4yf5/ikWf/X/tt6rBKZ6NcWpEROYW5JyDAr1MuAQQ=
856+
istio.io/istio v0.0.0-20250803052519-90ec2958505f/go.mod h1:TY8IgDEUQKj+8eRtT8zxs8Mfsqxv1d/ecErZhfzF73U=
843857
k8s.io/api v0.33.2 h1:YgwIS5jKfA+BZg//OQhkJNIfie/kmRsO0BmNaVSimvY=
844858
k8s.io/api v0.33.2/go.mod h1:fhrbphQJSM2cXzCWgqU29xLDuks4mu7ti9vveEnpSXs=
845859
k8s.io/apiextensions-apiserver v0.33.2 h1:6gnkIbngnaUflR3XwE1mCefN3YS8yTD631JXQhsU6M8=

navigator/pkg/bootstrap/mesh.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/apache/dubbo-kubernetes/pkg/filewatcher"
2525
"github.com/apache/dubbo-kubernetes/pkg/kube/krt"
2626
"github.com/apache/dubbo-kubernetes/pkg/mesh/meshwatcher"
27+
"github.com/apache/dubbo-kubernetes/pkg/ptr"
2728
"os"
2829
)
2930

@@ -32,7 +33,7 @@ const (
3233
)
3334

3435
func (s *Server) initMeshConfiguration(args *NaviArgs, fileWatcher filewatcher.FileWatcher) {
35-
fmt.Printf("initializing mesh configuration %v", args.MeshConfigFile)
36+
fmt.Printf("\ninitializing mesh configuration %v", args.MeshConfigFile)
3637
col := s.getMeshConfiguration(args, fileWatcher)
3738
col.AsCollection().WaitUntilSynced(s.internalStop)
3839
}
@@ -50,6 +51,7 @@ func (s *Server) getConfigurationSources(args *NaviArgs, fileWatcher filewatcher
5051
opts := krt.NewOptionsBuilder(s.internalStop, "")
5152
var userMeshConfig *meshwatcher.MeshConfigSource
5253
if features.SharedMeshConfig != "" && s.kubeClient != nil {
54+
userMeshConfig = ptr.Of(kubemesh.NewConfigMapSource(s.kubeClient, args.Namespace, features.SharedMeshConfig, cmKey, opts))
5355
}
5456
if _, err := os.Stat(file); !os.IsNotExist(err) {
5557
fileSource, err := meshwatcher.NewFileSource(fileWatcher, file, opts)

navigator/pkg/bootstrap/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ func (s *Server) initKubeClient(args *NaviArgs) error {
175175
return fmt.Errorf("failed creating kube config: %v", err)
176176
}
177177

178-
s.kubeClient, err = kubelib.NewCLIClient(kubelib.NewClientConfigForRestConfig(kubeRestConfig), nil)
178+
s.kubeClient, err = kubelib.NewClient(kubelib.NewClientConfigForRestConfig(kubeRestConfig), s.clusterID)
179179
if err != nil {
180180
return fmt.Errorf("failed creating kube client: %v", err)
181181
}

navigator/pkg/features/navigator.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@ var (
3636
"Warning: only a single namespace can be set.").Get()
3737
ClusterName = env.Register("CLUSTER_ID", constants.DefaultClusterName,
3838
"Defines the cluster and service registry that this Dubbod instance belongs to").Get()
39+
EnableVtprotobuf = env.Register("ENABLE_VTPROTOBUF", true,
40+
"If true, will use optimized vtprotobuf based marshaling. Requires a build with -tags=vtprotobuf.").Get()
3941
)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package protoconv
2+
3+
import (
4+
"github.com/apache/dubbo-kubernetes/navigator/pkg/features"
5+
"google.golang.org/protobuf/proto"
6+
"google.golang.org/protobuf/types/known/anypb"
7+
)
8+
9+
// MessageToAnyWithError converts from proto message to proto Any
10+
func MessageToAnyWithError(msg proto.Message) (*anypb.Any, error) {
11+
b, err := marshal(msg)
12+
if err != nil {
13+
return nil, err
14+
}
15+
return &anypb.Any{
16+
// nolint: staticcheck
17+
TypeUrl: "type.googleapis.com/" + string(msg.ProtoReflect().Descriptor().FullName()),
18+
Value: b,
19+
}, nil
20+
}
21+
22+
func marshal(msg proto.Message) ([]byte, error) {
23+
if features.EnableVtprotobuf {
24+
if vt, ok := msg.(vtStrictMarshal); ok {
25+
// Attempt to use more efficient implementation
26+
// "Strict" is the equivalent to Deterministic=true below
27+
return vt.MarshalVTStrict()
28+
}
29+
}
30+
// If not available, fallback to normal implementation
31+
return proto.MarshalOptions{Deterministic: true}.Marshal(msg)
32+
}
33+
34+
// MessageToAny converts from proto message to proto Any
35+
func MessageToAny(msg proto.Message) *anypb.Any {
36+
out, err := MessageToAnyWithError(msg)
37+
if err != nil {
38+
return nil
39+
}
40+
return out
41+
}
42+
43+
// https://github.com/planetscale/vtprotobuf#available-features
44+
type vtStrictMarshal interface {
45+
MarshalVTStrict() ([]byte, error)
46+
}

operator/pkg/config/gvk.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ func (g GroupVersionKind) Kubernetes() schema.GroupVersionKind {
4343
}
4444
}
4545

46-
// CanonicalGroup returns the group with defaulting applied. This means an empty group will
47-
// be treated as "core", following Kubernetes API standards
4846
func CanonicalGroup(group string) string {
4947
if group != "" {
5048
return group

0 commit comments

Comments
 (0)