This is an automated email from the ASF dual-hosted git repository.
alexstocks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go-pixiu.git
The following commit(s) were added to refs/heads/develop by this push:
new 41826486 Control Panel: Feat/service metadata (#540)
41826486 is described below
commit 41826486bc82489b55eb2db31ad4260eeb44a6e6
Author: MasterKenway <[email protected]>
AuthorDate: Sun Jun 18 20:04:57 2023 +0800
Control Panel: Feat/service metadata (#540)
* feat: generate service metadata (with namespace)
---
bin/update_crds.sh | 8 +-
go.mod | 4 +-
go.sum | 8 +-
manifests/charts/base/crds/crd-all.gen.yaml | 118 ++++++++
manifests/charts/base/files/gen-istio-cluster.yaml | 122 ++++++++-
.../istio-discovery/files/gen-istio.yaml | 20 +-
.../istiod-remote/templates/crd-all.gen.yaml | 118 ++++++++
pilot/pkg/bootstrap/server.go | 9 +
pilot/pkg/config/kube/crdclient/gen/main.go | 1 +
pilot/pkg/config/kube/crdclient/types.gen.go | 52 ++++
pilot/pkg/features/pilot.go | 22 ++
pilot/pkg/model/push_context.go | 59 ++++
pilot/pkg/model/push_context_test.go | 2 +-
.../networking/dubbo/v1alpha1/debouncehelper.go | 150 ++++++++++
.../dubbo/v1alpha1/servicemetadataserver.go | 302 +++++++++++++++++++++
.../schema/collections/collections.agent.gen.go | 22 ++
pkg/config/schema/collections/collections.gen.go | 22 ++
pkg/config/schema/gvk/resources.gen.go | 1 +
pkg/config/schema/metadata.yaml | 15 +
19 files changed, 1032 insertions(+), 23 deletions(-)
diff --git a/bin/update_crds.sh b/bin/update_crds.sh
index 10d98d84..e329ab6b 100755
--- a/bin/update_crds.sh
+++ b/bin/update_crds.sh
@@ -29,14 +29,14 @@ SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd
)"
ROOTDIR=$(dirname "${SCRIPTPATH}")
cd "${ROOTDIR}"
-REPO="github.com/istio/api"
+REPO="github.com/MasterKenway/operator-api"
# using the pseudo version we have in go.mod file. e.g.
v.0.0.0-<timestamp>-<SHA>
# first check if there's a replace: e.g. replace istio.io/api =>
github.com/USER/istioapi v0.0.0-<timestamp>-<SHA>
-SHA=$(grep "istio.io/api" go.mod | grep "^replace" | awk -F "-" '{print $NF}')
+SHA=$(grep "MasterKenway/operator-api" go.mod | grep "^replace" | awk -F "-"
'{print $NF}')
if [ -n "${SHA}" ]; then
- REPO=$(grep "istio.io/api" go.mod | grep "^replace" | awk '{print $4}')
+ REPO=$(grep "MasterKenway/operator-api" go.mod | grep "^replace" | awk
'{print $4}')
else
- SHA=$(grep "istio.io/api" go.mod | head -n1 | awk -F "-" '{print $NF}')
+ SHA=$(grep "MasterKenway/operator-api" go.mod | head -n1 | awk -F "-"
'{print $NF}')
fi
if [ -z "${SHA}" ]; then
diff --git a/go.mod b/go.mod
index 78c17929..8d79c903 100644
--- a/go.mod
+++ b/go.mod
@@ -8,9 +8,9 @@ exclude k8s.io/kubernetes v1.13.0
// Client-go does not handle different versions of mergo due to some breaking
changes - use the matching version
replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.5
-replace istio.io/api => github.com/dubbo-go-pixiu/operator-api
v0.0.0-20221126054223-dda83ac319f4
+replace istio.io/api => github.com/dubbo-go-pixiu/operator-api
v0.0.0-20230521024122-de7669e54430
-replace istio.io/client-go => github.com/dubbo-go-pixiu/operator-client-go
v1.14.6-0.20221126073212-882e30cca8f6
+replace istio.io/client-go => github.com/dubbo-go-pixiu/operator-client-go
v1.14.6-0.20230521064746-0907a7fb8042
require (
cloud.google.com/go/compute v1.6.0
diff --git a/go.sum b/go.sum
index 703b27d8..fc74b32c 100644
--- a/go.sum
+++ b/go.sum
@@ -491,10 +491,10 @@ github.com/docker/go-units v0.4.0/go.mod
h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod
h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod
h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod
h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dubbo-go-pixiu/operator-api v0.0.0-20221126054223-dda83ac319f4
h1:De7KZcGzrgYado/wA7PDBntNemkR7iYaO36gQagYYQY=
-github.com/dubbo-go-pixiu/operator-api
v0.0.0-20221126054223-dda83ac319f4/go.mod
h1:SWsRqLbdc3vFk5cIRylKHC28nx58hfikw2rjlpwR0Qs=
-github.com/dubbo-go-pixiu/operator-client-go
v1.14.6-0.20221126073212-882e30cca8f6
h1:GD5OCtBql9xl+UnWY+E7P4NCJBWyE+pE/JjY0jW+F8Y=
-github.com/dubbo-go-pixiu/operator-client-go
v1.14.6-0.20221126073212-882e30cca8f6/go.mod
h1:If8h1tIkt2k4P4hKsnQxR8m2bUF2hZBpfLP6VyaFO+g=
+github.com/dubbo-go-pixiu/operator-api v0.0.0-20230521024122-de7669e54430
h1:2Qjn7n3kCSJ60VSNnpA/RbmeXcel6ftjhrN6opQ4SJQ=
+github.com/dubbo-go-pixiu/operator-api
v0.0.0-20230521024122-de7669e54430/go.mod
h1:SWsRqLbdc3vFk5cIRylKHC28nx58hfikw2rjlpwR0Qs=
+github.com/dubbo-go-pixiu/operator-client-go
v1.14.6-0.20230521064746-0907a7fb8042
h1:L/NmoQfKNhNObrfqVpit3q1DxN6hXn8N0EG9fCtDRaI=
+github.com/dubbo-go-pixiu/operator-client-go
v1.14.6-0.20230521064746-0907a7fb8042/go.mod
h1:PvtYHGhoKNXvyX9hPuScG/bQ2CLD47OSNvltcWmE8H4=
github.com/dubbo-go-pixiu/pixiu-api v0.1.6-0.20220612115254-d9a176b25b99
h1:UjDxgIEu6DbJVJTrxm5mwC0j54jNao1pkYVlT8X+KgY=
github.com/dubbo-go-pixiu/pixiu-api
v0.1.6-0.20220612115254-d9a176b25b99/go.mod
h1:1l+6pDTdEHwCyyyJmfckOAdGp6f5PZ33ZVMgxso9q/U=
github.com/dubbogo/go-zookeeper v1.0.3/go.mod
h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c=
diff --git a/manifests/charts/base/crds/crd-all.gen.yaml
b/manifests/charts/base/crds/crd-all.gen.yaml
index dc04ae97..31a291a0 100644
--- a/manifests/charts/base/crds/crd-all.gen.yaml
+++ b/manifests/charts/base/crds/crd-all.gen.yaml
@@ -1,6 +1,124 @@
# DO NOT EDIT - Generated by Cue OpenAPI generator based on Istio APIs.
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
+metadata:
+ annotations:
+ "helm.sh/resource-policy": keep
+ labels:
+ app: istio-pilot
+ chart: istio
+ heritage: Tiller
+ release: istio
+ name: servicemetadatas.extensions.istio.io
+spec:
+ group: extensions.istio.io
+ names:
+ categories:
+ - istio-io
+ - extensions-istio-io
+ kind: ServiceMetadata
+ listKind: ServiceMetadataList
+ plural: servicemetadatas
+ shortNames:
+ - sm
+ singular: servicemetadata
+ scope: Namespaced
+ versions:
+ - additionalPrinterColumns:
+ - description: 'CreationTimestamp is a timestamp representing the server
time
+ when this object was created. It is not guaranteed to be set in
happens-before
+ order across separate operations. Clients may not set this value. It
is represented
+ in RFC3339 form and is in UTC. Populated by the system. Read-only.
Null for
+ lists. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata'
+ jsonPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ spec:
+ description: 'it''s the spec of Service Metadata See more details
at:'
+ properties:
+ applicationName:
+ type: string
+ metadataInfo:
+ type: string
+ revision:
+ type: string
+ type: object
+ status:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ "helm.sh/resource-policy": keep
+ labels:
+ app: istio-pilot
+ chart: istio
+ heritage: Tiller
+ release: istio
+ name: servicenamemappings.extensions.istio.io
+spec:
+ group: extensions.istio.io
+ names:
+ categories:
+ - istio-io
+ - extensions-istio-io
+ kind: ServiceNameMapping
+ listKind: ServiceNameMappingList
+ plural: servicenamemappings
+ shortNames:
+ - snp
+ singular: servicenamemapping
+ scope: Namespaced
+ versions:
+ - additionalPrinterColumns:
+ - description: 'CreationTimestamp is a timestamp representing the server
time
+ when this object was created. It is not guaranteed to be set in
happens-before
+ order across separate operations. Clients may not set this value. It
is represented
+ in RFC3339 form and is in UTC. Populated by the system. Read-only.
Null for
+ lists. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata'
+ jsonPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ spec:
+ description: 'it''s the spec of Service Name Mapping, which is
used to
+ map the interface name See more details at:'
+ properties:
+ applicationNames:
+ items:
+ type: string
+ type: array
+ interfaceName:
+ description: InterfaceName.
+ type: string
+ type: object
+ status:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
metadata:
annotations:
"helm.sh/resource-policy": keep
diff --git a/manifests/charts/base/files/gen-istio-cluster.yaml
b/manifests/charts/base/files/gen-istio-cluster.yaml
index 06cd7846..dd40510a 100644
--- a/manifests/charts/base/files/gen-istio-cluster.yaml
+++ b/manifests/charts/base/files/gen-istio-cluster.yaml
@@ -3,6 +3,124 @@
# DO NOT EDIT - Generated by Cue OpenAPI generator based on Istio APIs.
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
+metadata:
+ annotations:
+ "helm.sh/resource-policy": keep
+ labels:
+ app: istio-pilot
+ chart: istio
+ heritage: Tiller
+ release: istio
+ name: servicemetadatas.extensions.istio.io
+spec:
+ group: extensions.istio.io
+ names:
+ categories:
+ - istio-io
+ - extensions-istio-io
+ kind: ServiceMetadata
+ listKind: ServiceMetadataList
+ plural: servicemetadatas
+ shortNames:
+ - sm
+ singular: servicemetadata
+ scope: Namespaced
+ versions:
+ - additionalPrinterColumns:
+ - description: 'CreationTimestamp is a timestamp representing the server
time
+ when this object was created. It is not guaranteed to be set in
happens-before
+ order across separate operations. Clients may not set this value. It
is represented
+ in RFC3339 form and is in UTC. Populated by the system. Read-only.
Null for
+ lists. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata'
+ jsonPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ spec:
+ description: 'it''s the spec of Service Metadata See more details
at:'
+ properties:
+ applicationName:
+ type: string
+ metadataInfo:
+ type: string
+ revision:
+ type: string
+ type: object
+ status:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ "helm.sh/resource-policy": keep
+ labels:
+ app: istio-pilot
+ chart: istio
+ heritage: Tiller
+ release: istio
+ name: servicenamemappings.extensions.istio.io
+spec:
+ group: extensions.istio.io
+ names:
+ categories:
+ - istio-io
+ - extensions-istio-io
+ kind: ServiceNameMapping
+ listKind: ServiceNameMappingList
+ plural: servicenamemappings
+ shortNames:
+ - snp
+ singular: servicenamemapping
+ scope: Namespaced
+ versions:
+ - additionalPrinterColumns:
+ - description: 'CreationTimestamp is a timestamp representing the server
time
+ when this object was created. It is not guaranteed to be set in
happens-before
+ order across separate operations. Clients may not set this value. It
is represented
+ in RFC3339 form and is in UTC. Populated by the system. Read-only.
Null for
+ lists. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata'
+ jsonPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ spec:
+ description: 'it''s the spec of Service Name Mapping, which is
used to
+ map the interface name See more details at:'
+ properties:
+ applicationNames:
+ items:
+ type: string
+ type: array
+ interfaceName:
+ description: InterfaceName.
+ type: string
+ type: object
+ status:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
metadata:
annotations:
"helm.sh/resource-policy": keep
@@ -6842,14 +6960,14 @@ subjects:
---
# Source: base/templates/default.yaml
apiVersion: admissionregistration.k8s.io/v1
-kind: ValidatingWebhookConfiguration
+kind: Val:wqidatingWebhookConfiguration
metadata:
name: istiod-default-validator
labels:
app: istiod
release: istio
istio: istiod
- istio.io/rev: default
+ istio.io/rev: dubbo
webhooks:
- name: validation.istio.io
clientConfig:
diff --git
a/manifests/charts/istio-control/istio-discovery/files/gen-istio.yaml
b/manifests/charts/istio-control/istio-discovery/files/gen-istio.yaml
index 5c6579b8..aef1c70d 100644
--- a/manifests/charts/istio-control/istio-discovery/files/gen-istio.yaml
+++ b/manifests/charts/istio-control/istio-discovery/files/gen-istio.yaml
@@ -1886,7 +1886,7 @@ metadata:
labels:
istio.io/rev: default
spec:
-
+
configPatches:
- applyTo: HTTP_FILTER
match:
@@ -2011,7 +2011,7 @@ metadata:
labels:
istio.io/rev: default
spec:
-
+
configPatches:
- applyTo: NETWORK_FILTER
match:
@@ -2128,7 +2128,7 @@ metadata:
labels:
istio.io/rev: default
spec:
-
+
configPatches:
- applyTo: HTTP_FILTER
match:
@@ -2253,7 +2253,7 @@ metadata:
labels:
istio.io/rev: default
spec:
-
+
configPatches:
- applyTo: NETWORK_FILTER
match:
@@ -2370,7 +2370,7 @@ metadata:
labels:
istio.io/rev: default
spec:
-
+
configPatches:
- applyTo: HTTP_FILTER
match:
@@ -2495,7 +2495,7 @@ metadata:
labels:
istio.io/rev: default
spec:
-
+
configPatches:
- applyTo: NETWORK_FILTER
match:
@@ -2612,7 +2612,7 @@ metadata:
labels:
istio.io/rev: default
spec:
-
+
configPatches:
- applyTo: HTTP_FILTER
match:
@@ -2737,7 +2737,7 @@ metadata:
labels:
istio.io/rev: default
spec:
-
+
configPatches:
- applyTo: NETWORK_FILTER
match:
@@ -2854,7 +2854,7 @@ metadata:
labels:
istio.io/rev: default
spec:
-
+
configPatches:
- applyTo: HTTP_FILTER
match:
@@ -2979,7 +2979,7 @@ metadata:
labels:
istio.io/rev: default
spec:
-
+
configPatches:
- applyTo: NETWORK_FILTER
match:
diff --git a/manifests/charts/istiod-remote/templates/crd-all.gen.yaml
b/manifests/charts/istiod-remote/templates/crd-all.gen.yaml
index f5f05073..5c623cf6 100644
--- a/manifests/charts/istiod-remote/templates/crd-all.gen.yaml
+++ b/manifests/charts/istiod-remote/templates/crd-all.gen.yaml
@@ -2,6 +2,124 @@
# DO NOT EDIT - Generated by Cue OpenAPI generator based on Istio APIs.
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
+metadata:
+ annotations:
+ "helm.sh/resource-policy": keep
+ labels:
+ app: istio-pilot
+ chart: istio
+ heritage: Tiller
+ release: istio
+ name: servicemetadatas.extensions.istio.io
+spec:
+ group: extensions.istio.io
+ names:
+ categories:
+ - istio-io
+ - extensions-istio-io
+ kind: ServiceMetadata
+ listKind: ServiceMetadataList
+ plural: servicemetadatas
+ shortNames:
+ - sm
+ singular: servicemetadata
+ scope: Namespaced
+ versions:
+ - additionalPrinterColumns:
+ - description: 'CreationTimestamp is a timestamp representing the server
time
+ when this object was created. It is not guaranteed to be set in
happens-before
+ order across separate operations. Clients may not set this value. It
is represented
+ in RFC3339 form and is in UTC. Populated by the system. Read-only.
Null for
+ lists. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata'
+ jsonPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ spec:
+ description: 'it''s the spec of Service Metadata See more details
at:'
+ properties:
+ applicationName:
+ type: string
+ metadataInfo:
+ type: string
+ revision:
+ type: string
+ type: object
+ status:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ "helm.sh/resource-policy": keep
+ labels:
+ app: istio-pilot
+ chart: istio
+ heritage: Tiller
+ release: istio
+ name: servicenamemappings.extensions.istio.io
+spec:
+ group: extensions.istio.io
+ names:
+ categories:
+ - istio-io
+ - extensions-istio-io
+ kind: ServiceNameMapping
+ listKind: ServiceNameMappingList
+ plural: servicenamemappings
+ shortNames:
+ - snp
+ singular: servicenamemapping
+ scope: Namespaced
+ versions:
+ - additionalPrinterColumns:
+ - description: 'CreationTimestamp is a timestamp representing the server
time
+ when this object was created. It is not guaranteed to be set in
happens-before
+ order across separate operations. Clients may not set this value. It
is represented
+ in RFC3339 form and is in UTC. Populated by the system. Read-only.
Null for
+ lists. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata'
+ jsonPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ properties:
+ spec:
+ description: 'it''s the spec of Service Name Mapping, which is
used to
+ map the interface name See more details at:'
+ properties:
+ applicationNames:
+ items:
+ type: string
+ type: array
+ interfaceName:
+ description: InterfaceName.
+ type: string
+ type: object
+ status:
+ type: object
+ x-kubernetes-preserve-unknown-fields: true
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
metadata:
annotations:
"helm.sh/resource-policy": keep
diff --git a/pilot/pkg/bootstrap/server.go b/pilot/pkg/bootstrap/server.go
index fc2da178..59305712 100644
--- a/pilot/pkg/bootstrap/server.go
+++ b/pilot/pkg/bootstrap/server.go
@@ -180,6 +180,9 @@ type Server struct {
// RWConfigStore is the configstore which allows updates, particularly
for status.
RWConfigStore model.ConfigStoreController
+ // ServiceMetadataServer
+ metadataServer *dubbov1alpha1.ServiceMetadataServer
+
//Service Name mapping register
snpServer *dubbov1alpha1.Snp
}
@@ -277,6 +280,10 @@ func NewServer(args *PilotArgs, initFuncs
...func(*Server)) (*Server, error) {
return nil, err
}
+ if s.kubeClient != nil {
+ s.metadataServer =
dubbov1alpha1.NewServiceMetadataServer(s.environment, s.kubeClient)
+ }
+
// Create Service Name mapping server
if s.kubeClient != nil {
s.snpServer = dubbov1alpha1.NewSnp(s.kubeClient)
@@ -745,6 +752,7 @@ func (s *Server) initGrpcServer(options
*istiokeepalive.Options) {
s.grpcServer = grpc.NewServer(grpcOptions...)
s.XDSServer.Register(s.grpcServer)
reflection.Register(s.grpcServer)
+ s.metadataServer.Register(s.grpcServer)
s.snpServer.Register(s.grpcServer)
}
@@ -794,6 +802,7 @@ func (s *Server) initSecureDiscoveryService(args
*PilotArgs) error {
s.secureGrpcServer = grpc.NewServer(opts...)
s.XDSServer.Register(s.secureGrpcServer)
reflection.Register(s.secureGrpcServer)
+ s.metadataServer.Register(s.secureGrpcServer)
s.snpServer.Register(s.secureGrpcServer)
s.addStartFunc(func(stop <-chan struct{}) error {
diff --git a/pilot/pkg/config/kube/crdclient/gen/main.go
b/pilot/pkg/config/kube/crdclient/gen/main.go
index fac0e734..01e621d8 100644
--- a/pilot/pkg/config/kube/crdclient/gen/main.go
+++ b/pilot/pkg/config/kube/crdclient/gen/main.go
@@ -164,6 +164,7 @@ var (
"nodes": "Nodes",
"secrets": "Secrets",
"ingresses": "Ingresses",
+ "servicemetadatas": "ServiceMetadatas",
"servicenamemappings": "ServiceNameMappings",
}
diff --git a/pilot/pkg/config/kube/crdclient/types.gen.go
b/pilot/pkg/config/kube/crdclient/types.gen.go
index 728a56d5..a842439f 100644
--- a/pilot/pkg/config/kube/crdclient/types.gen.go
+++ b/pilot/pkg/config/kube/crdclient/types.gen.go
@@ -58,6 +58,11 @@ import (
func create(ic versionedclient.Interface, sc gatewayapiclient.Interface, cfg
config.Config, objMeta metav1.ObjectMeta) (metav1.Object, error) {
switch cfg.GroupVersionKind {
+ case
collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind():
+ return
ic.ExtensionsV1alpha1().ServiceMetadatas(cfg.Namespace).Create(context.TODO(),
&clientextensionsv1alpha1.ServiceMetadata{
+ ObjectMeta: objMeta,
+ Spec:
*(cfg.Spec.(*extensionsv1alpha1.ServiceMetadata)),
+ }, metav1.CreateOptions{})
case
collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind():
return
ic.ExtensionsV1alpha1().ServiceNameMappings(cfg.Namespace).Create(context.TODO(),
&clientextensionsv1alpha1.ServiceNameMapping{
ObjectMeta: objMeta,
@@ -170,6 +175,11 @@ func create(ic versionedclient.Interface, sc
gatewayapiclient.Interface, cfg con
func update(ic versionedclient.Interface, sc gatewayapiclient.Interface, cfg
config.Config, objMeta metav1.ObjectMeta) (metav1.Object, error) {
switch cfg.GroupVersionKind {
+ case
collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind():
+ return
ic.ExtensionsV1alpha1().ServiceMetadatas(cfg.Namespace).Update(context.TODO(),
&clientextensionsv1alpha1.ServiceMetadata{
+ ObjectMeta: objMeta,
+ Spec:
*(cfg.Spec.(*extensionsv1alpha1.ServiceMetadata)),
+ }, metav1.UpdateOptions{})
case
collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind():
return
ic.ExtensionsV1alpha1().ServiceNameMappings(cfg.Namespace).Update(context.TODO(),
&clientextensionsv1alpha1.ServiceNameMapping{
ObjectMeta: objMeta,
@@ -283,6 +293,12 @@ func update(ic versionedclient.Interface, sc
gatewayapiclient.Interface, cfg con
func updateStatus(ic versionedclient.Interface, sc gatewayapiclient.Interface,
cfg config.Config, objMeta metav1.ObjectMeta) (metav1.Object, error) {
switch cfg.GroupVersionKind {
+ case
collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind():
+ return
ic.ExtensionsV1alpha1().ServiceMetadatas(cfg.Namespace).UpdateStatus(context.TODO(),
&clientextensionsv1alpha1.ServiceMetadata{
+ ObjectMeta: objMeta,
+ Status: *(cfg.Status.(*metav1alpha1.IstioStatus)),
+ }, metav1.UpdateOptions{})
+
case
collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind():
return
ic.ExtensionsV1alpha1().ServiceNameMappings(cfg.Namespace).UpdateStatus(context.TODO(),
&clientextensionsv1alpha1.ServiceNameMapping{
ObjectMeta: objMeta,
@@ -413,6 +429,21 @@ func patch(ic versionedclient.Interface, sc
gatewayapiclient.Interface, orig con
}
// TODO support setting field manager
switch orig.GroupVersionKind {
+ case
collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind():
+ oldRes := &clientextensionsv1alpha1.ServiceMetadata{
+ ObjectMeta: origMeta,
+ Spec:
*(orig.Spec.(*extensionsv1alpha1.ServiceMetadata)),
+ }
+ modRes := &clientextensionsv1alpha1.ServiceMetadata{
+ ObjectMeta: modMeta,
+ Spec:
*(mod.Spec.(*extensionsv1alpha1.ServiceMetadata)),
+ }
+ patchBytes, err := genPatchBytes(oldRes, modRes, typ)
+ if err != nil {
+ return nil, err
+ }
+ return ic.ExtensionsV1alpha1().ServiceMetadatas(orig.Namespace).
+ Patch(context.TODO(), orig.Name, typ, patchBytes,
metav1.PatchOptions{FieldManager: "pilot-discovery"})
case
collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind():
oldRes := &clientextensionsv1alpha1.ServiceNameMapping{
ObjectMeta: origMeta,
@@ -739,6 +770,8 @@ func delete(ic versionedclient.Interface, sc
gatewayapiclient.Interface, typ con
deleteOptions.Preconditions =
&metav1.Preconditions{ResourceVersion: resourceVersion}
}
switch typ {
+ case
collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind():
+ return
ic.ExtensionsV1alpha1().ServiceMetadatas(namespace).Delete(context.TODO(),
name, deleteOptions)
case
collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind():
return
ic.ExtensionsV1alpha1().ServiceNameMappings(namespace).Delete(context.TODO(),
name, deleteOptions)
case
collections.IstioExtensionsV1Alpha1Wasmplugins.Resource().GroupVersionKind():
@@ -787,6 +820,25 @@ func delete(ic versionedclient.Interface, sc
gatewayapiclient.Interface, typ con
}
var translationMap = map[config.GroupVersionKind]func(r runtime.Object)
config.Config{
+
collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind():
func(r runtime.Object) config.Config {
+ obj := r.(*clientextensionsv1alpha1.ServiceMetadata)
+ return config.Config{
+ Meta: config.Meta{
+ GroupVersionKind:
collections.IstioExtensionsV1Alpha1Servicemetadatas.Resource().GroupVersionKind(),
+ Name: obj.Name,
+ Namespace: obj.Namespace,
+ Labels: obj.Labels,
+ Annotations: obj.Annotations,
+ ResourceVersion: obj.ResourceVersion,
+ CreationTimestamp: obj.CreationTimestamp.Time,
+ OwnerReferences: obj.OwnerReferences,
+ UID: string(obj.UID),
+ Generation: obj.Generation,
+ },
+ Spec: &obj.Spec,
+ Status: &obj.Status,
+ }
+ },
collections.IstioExtensionsV1Alpha1Servicenamemappings.Resource().GroupVersionKind():
func(r runtime.Object) config.Config {
obj := r.(*clientextensionsv1alpha1.ServiceNameMapping)
return config.Config{
diff --git a/pilot/pkg/features/pilot.go b/pilot/pkg/features/pilot.go
index 484c1055..c3392a42 100644
--- a/pilot/pkg/features/pilot.go
+++ b/pilot/pkg/features/pilot.go
@@ -110,6 +110,28 @@ var (
" EDS pushes may be delayed, but there will be fewer
pushes. By default this is enabled",
).Get()
+ SMDebounceAfter = env.RegisterDurationVar(
+ "PILOT_SM_DEBOUNCE_AFTER",
+ 100*time.Millisecond,
+ "The delay added to config/registry events for debouncing. This
will delay the push by "+
+ "at least this interval. If no change is detected
within this period, the push will happen, "+
+ " otherwise we'll keep delaying until things settle, up
to a max of PILOT_SM_DEBOUNCE_MAX.",
+ ).Get()
+
+ SMDebounceMax = env.RegisterDurationVar(
+ "PILOT_SM_DEBOUNCE_MAX",
+ 1*time.Second,
+ "The maximum amount of time to wait for events while
debouncing. If events keep showing up with no breaks "+
+ "for this time, we'll trigger a push.",
+ ).Get()
+
+ SMEnableDebounce = env.RegisterBoolVar(
+ "PILOT_SM_ENABLE_DEBOUNCE",
+ true,
+ "If enabled, Pilot will include EDS pushes in the push
debouncing, configured by PILOT_SM_DEBOUNCE_AFTER and PILOT_SM_DEBOUNCE_MAX."+
+ " SNP register may be delayed, but there will be fewer
pushes. By default this is enabled",
+ ).Get()
+
SNPDebounceAfter = env.RegisterDurationVar(
"PILOT_SNP_DEBOUNCE_AFTER",
100*time.Millisecond,
diff --git a/pilot/pkg/model/push_context.go b/pilot/pkg/model/push_context.go
index f7a3076d..695abd9b 100644
--- a/pilot/pkg/model/push_context.go
+++ b/pilot/pkg/model/push_context.go
@@ -173,6 +173,21 @@ func newGatewayIndex() gatewayIndex {
}
}
+// serviceMetadataIndex is the index of service metadata by various fields.
+type serviceMetadataIndex struct {
+ namespace map[string][]*config.Config
+ applicationNameByNamespace map[string]map[string]*config.Config
+ all []*config.Config
+}
+
+func newServiceMetadataIndex() serviceMetadataIndex {
+ return serviceMetadataIndex{
+ namespace: map[string][]*config.Config{},
+ applicationNameByNamespace:
map[string]map[string]*config.Config{},
+ all: []*config.Config{},
+ }
+}
+
// serviceNameMappingIndex is the index of Service Name Mapping by various
fields.
type serviceNameMappingIndex struct {
// namespace contains Service Name Mapping by namespace.
@@ -226,6 +241,9 @@ type PushContext struct {
// sidecarIndex stores sidecar resources
sidecarIndex sidecarIndex
+ // serviceMetadataIndex stores service metadata resources
+ serviceMetadataIndex serviceMetadataIndex
+
// serviceNameMappingIndex is the index of service name mapping.
serviceNameMappingIndex serviceNameMappingIndex
@@ -660,6 +678,7 @@ func NewPushContext() *PushContext {
gatewayIndex: newGatewayIndex(),
ProxyStatus:
map[string]map[string]ProxyPushStatus{},
ServiceAccounts: map[host.Name]map[int][]string{},
+ serviceMetadataIndex: newServiceMetadataIndex(),
serviceNameMappingIndex: newServiceNameMappingIndex(),
}
}
@@ -1117,6 +1136,13 @@ func (ps *PushContext)
getExportedDestinationRuleFromNamespace(owningNamespace s
return nil
}
+func (ps *PushContext) ServiceMetadata(namespace, applicationName, revision
string) *config.Config {
+ if conf, ok :=
ps.serviceMetadataIndex.applicationNameByNamespace[namespace][strings.ToLower(fmt.Sprintf("%s-%s",
applicationName, revision))]; ok {
+ return conf
+ }
+ return nil
+}
+
// IsClusterLocal indicates whether the endpoints for the service should only
be accessible to clients
// within the cluster.
func (ps *PushContext) IsClusterLocal(service *Service) bool {
@@ -1211,6 +1237,10 @@ func (ps *PushContext) createNewContext(env
*Environment) error {
return err
}
+ if err := ps.initServiceMetadata(env); err != nil {
+ return err
+ }
+
// Must be initialized in the end
if err := ps.initSidecarScopes(env); err != nil {
return err
@@ -2007,6 +2037,35 @@ func (ps *PushContext) initGateways(env *Environment)
error {
return nil
}
+func (ps *PushContext) initServiceMetadata(env *Environment) error {
+ metadataConfig, err := env.List(gvk.ServiceMetadata, NamespaceAll)
+ if err != nil {
+ return err
+ }
+
+ sortConfigByCreationTime(metadataConfig)
+
+ ps.serviceMetadataIndex.namespace = make(map[string][]*config.Config)
+ ps.serviceMetadataIndex.applicationNameByNamespace =
make(map[string]map[string]*config.Config)
+ ps.serviceMetadataIndex.all = make([]*config.Config, 0)
+
+ for _, conf := range metadataConfig {
+ if _, ok := ps.serviceMetadataIndex.namespace[conf.Namespace];
!ok {
+ ps.serviceMetadataIndex.namespace[conf.Namespace] =
make([]*config.Config, 0)
+ }
+
+ if _, ok :=
ps.serviceMetadataIndex.applicationNameByNamespace[conf.Namespace]; !ok {
+
ps.serviceMetadataIndex.applicationNameByNamespace[conf.Namespace] =
make(map[string]*config.Config, 0)
+ }
+
+ ps.serviceMetadataIndex.namespace[conf.Namespace] =
append(ps.serviceMetadataIndex.namespace[conf.Namespace], &conf)
+
ps.serviceMetadataIndex.applicationNameByNamespace[conf.Namespace][conf.Name] =
&conf
+ ps.serviceMetadataIndex.all =
append(ps.serviceMetadataIndex.all, &conf)
+ }
+
+ return nil
+}
+
// InternalGatewayServiceAnnotation represents the hostname of the service a
gateway will use. This is
// only used internally to transfer information from the Kubernetes Gateway
API to the Istio Gateway API
// which does not have a field to represent this.
diff --git a/pilot/pkg/model/push_context_test.go
b/pilot/pkg/model/push_context_test.go
index cdf6c068..ee152d84 100644
--- a/pilot/pkg/model/push_context_test.go
+++ b/pilot/pkg/model/push_context_test.go
@@ -949,7 +949,7 @@ func TestInitPushContext(t *testing.T) {
// Allow looking into exported fields for parts of push context
cmp.AllowUnexported(PushContext{}, exportToDefaults{},
serviceIndex{}, virtualServiceIndex{},
destinationRuleIndex{}, gatewayIndex{},
consolidatedDestRules{}, IstioEgressListenerWrapper{}, SidecarScope{},
- AuthenticationPolicies{}, NetworkManager{},
sidecarIndex{}, Telemetries{}, ProxyConfigs{}, consolidatedDestRule{},
serviceNameMappingIndex{}),
+ AuthenticationPolicies{}, NetworkManager{},
sidecarIndex{}, Telemetries{}, ProxyConfigs{}, consolidatedDestRule{},
serviceNameMappingIndex{}, serviceMetadataIndex{}),
// These are not feasible/worth comparing
cmpopts.IgnoreTypes(sync.RWMutex{}, localServiceDiscovery{},
FakeStore{}, atomic.Bool{}, sync.Mutex{}),
cmpopts.IgnoreInterfaces(struct{ mesh.Holder }{}),
diff --git a/pilot/pkg/networking/dubbo/v1alpha1/debouncehelper.go
b/pilot/pkg/networking/dubbo/v1alpha1/debouncehelper.go
new file mode 100644
index 00000000..2e9ff409
--- /dev/null
+++ b/pilot/pkg/networking/dubbo/v1alpha1/debouncehelper.go
@@ -0,0 +1,150 @@
+/*
+ * 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 v1alpha1
+
+import (
+ "fmt"
+ "time"
+)
+
+import (
+ "go.uber.org/atomic"
+)
+
+/**
+Copy From pilot/pkg/xds/discovery.go
+*/
+type debounceOptions struct {
+ // debounceAfter is the delay added to events to wait
+ // after a registry/config event for debouncing.
+ // This will delay the push by at least this interval, plus
+ // the time getting subsequent events. If no change is
+ // detected the push will happen, otherwise we'll keep
+ // delaying until things settle.
+ debounceAfter time.Duration
+
+ // debounceMax is the maximum time to wait for events
+ // while debouncing. Defaults to 10 seconds. If events keep
+ // showing up with no break for this time, we'll trigger a push.
+ debounceMax time.Duration
+
+ // enableDebounce indicates whether EDS pushes should be debounced.
+ enableDebounce bool
+}
+
+type DebounceHelper struct {
+}
+
+func (h *DebounceHelper) Debounce(ch chan *pushRequest, stopCh <-chan
struct{}, opts debounceOptions, pushFn func(req *pushRequest), updateSent
*atomic.Int64) {
+ defer func() {
+ err := recover()
+ if err != nil {
+ log.Infof("Debounce panic caused by: {%+v}", err)
+ }
+ }()
+
+ var timeChan <-chan time.Time
+ var startDebounce time.Time
+ var lastConfigUpdateTime time.Time
+
+ pushCounter := 0
+ debouncedEvents := 0
+
+ // Keeps track of the push requests. If updates are debounce they will
be merged.
+ var req *pushRequest
+
+ free := true
+ freeCh := make(chan struct{}, 1)
+
+ push := func(req *pushRequest, debouncedEvents int) {
+ pushFn(req)
+ updateSent.Add(int64(debouncedEvents))
+ freeCh <- struct{}{}
+ }
+
+ pushWorker := func() {
+ eventDelay := time.Since(startDebounce)
+ quietTime := time.Since(lastConfigUpdateTime)
+ // it has been too long or quiet enough
+ if eventDelay >= opts.debounceMax || quietTime >=
opts.debounceAfter {
+ if req != nil {
+ pushCounter++
+ if req.ConfigsUpdated == nil {
+ log.Infof("Push debounce stable[%d] %d
: %v since last change, %v since last push",
+ pushCounter, debouncedEvents,
+ quietTime, eventDelay)
+ } else {
+ log.Infof("Push debounce stable[%d] %d
for config %s: %v since last change, %v since last push",
+ pushCounter, debouncedEvents,
PushRequestConfigsUpdated(req),
+ quietTime, eventDelay)
+ }
+ free = false
+ go push(req, debouncedEvents)
+ req = nil
+ debouncedEvents = 0
+ }
+ } else {
+ timeChan = time.After(opts.debounceAfter - quietTime)
+ }
+ }
+
+ for {
+ select {
+ case <-freeCh:
+ free = true
+ pushWorker()
+ case r := <-ch:
+ if !opts.enableDebounce {
+ // trigger push now, just for EDS
+ go func(req *pushRequest) {
+ pushFn(req)
+ updateSent.Inc()
+ }(r)
+ continue
+ }
+
+ lastConfigUpdateTime = time.Now()
+ if debouncedEvents == 0 {
+ timeChan = time.After(opts.debounceAfter)
+ startDebounce = lastConfigUpdateTime
+ }
+ debouncedEvents++
+
+ req = req.Merge(r)
+ case <-timeChan:
+ if free {
+ pushWorker()
+ }
+ case <-stopCh:
+ return
+ }
+ }
+}
+
+func PushRequestConfigsUpdated(req *pushRequest) string {
+ configs := ""
+ for key := range req.ConfigsUpdated {
+ configs += key.String()
+ break
+ }
+ if len(req.ConfigsUpdated) > 1 {
+ more := fmt.Sprintf(" and %d more configs",
len(req.ConfigsUpdated)-1)
+ configs += more
+ }
+ return configs
+}
diff --git a/pilot/pkg/networking/dubbo/v1alpha1/servicemetadataserver.go
b/pilot/pkg/networking/dubbo/v1alpha1/servicemetadataserver.go
new file mode 100644
index 00000000..4907035e
--- /dev/null
+++ b/pilot/pkg/networking/dubbo/v1alpha1/servicemetadataserver.go
@@ -0,0 +1,302 @@
+/*
+ * 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 v1alpha1
+
+import (
+ "context"
+ "fmt"
+ "strconv"
+ "time"
+)
+
+import (
+ "github.com/pkg/errors"
+ "go.uber.org/atomic"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+ dubbov1alpha1 "istio.io/api/dubbo/v1alpha1"
+ istioextensionsv1alpha1 "istio.io/api/extensions/v1alpha1"
+ "istio.io/client-go/pkg/apis/extensions/v1alpha1"
+ apierror "k8s.io/apimachinery/pkg/api/errors"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+import (
+ "github.com/apache/dubbo-go-pixiu/pilot/pkg/features"
+ "github.com/apache/dubbo-go-pixiu/pilot/pkg/model"
+ "github.com/apache/dubbo-go-pixiu/pkg/kube"
+)
+
+type ServiceMetadataServer struct {
+ dubbov1alpha1.UnimplementedServiceMetadataServiceServer
+
+ KubeClient kube.Client
+
+ // CommittedUpdates describes the number of configuration updates the
discovery server has
+ // received, process, and stored in the push context. If this number is
less than InboundUpdates,
+ // there are updates we have not yet processed.
+ // Note: This does not mean that all proxies have received these
configurations; it is strictly
+ // the push context, which means that the next push to a proxy will
receive this configuration.
+ CommittedUpdates *atomic.Int64
+
+ ch chan *pushRequest
+
+ debounceOptions debounceOptions
+ debounceHelper DebounceHelper
+}
+
+type pushRequest struct {
+ // application name + revision => metadata info
+ ConfigsUpdated map[model.ConfigKey]metadataConfig
+}
+
+type metadataConfig struct {
+ applicationName string
+ revision string
+ metadataInfo string
+ timestamp time.Time
+}
+
+func (pr *pushRequest) Merge(other *pushRequest) *pushRequest {
+ if pr == nil {
+ return other
+ }
+ if other == nil {
+ return pr
+ }
+
+ // Keep the first (older) start time
+
+ // Do not merge when any one is empty
+ if len(pr.ConfigsUpdated) == 0 || len(other.ConfigsUpdated) == 0 {
+ pr.ConfigsUpdated = nil
+ } else {
+ for key, value := range other.ConfigsUpdated {
+ if prValue, ok := pr.ConfigsUpdated[key]; ok {
+ if value.timestamp.After(prValue.timestamp) {
+ pr.ConfigsUpdated[key] = value
+ }
+ } else {
+ pr.ConfigsUpdated[key] = value
+ }
+ }
+ }
+
+ return pr
+}
+
+func NewServiceMetadataServer(env *model.Environment, client kube.Client)
*ServiceMetadataServer {
+ return &ServiceMetadataServer{
+ CommittedUpdates: atomic.NewInt64(0),
+ KubeClient: client,
+ ch: make(chan *pushRequest, 10),
+ debounceOptions: debounceOptions{
+ debounceAfter: features.SMDebounceAfter,
+ debounceMax: features.SMDebounceMax,
+ enableDebounce: features.SMEnableDebounce,
+ },
+ }
+}
+
+func (s *ServiceMetadataServer) Start(stopCh <-chan struct{}) {
+ go s.handleUpdate(stopCh)
+ go s.removeOutdatedCRD(stopCh)
+}
+
+func (s *ServiceMetadataServer) Register(rpcs *grpc.Server) {
+ // Register v3 server
+ dubbov1alpha1.RegisterServiceMetadataServiceServer(rpcs, s)
+}
+
+func (s *ServiceMetadataServer) Publish(ctx context.Context, request
*dubbov1alpha1.PublishServiceMetadataRequest)
(*dubbov1alpha1.PublishServiceMetadataResponse, error) {
+ if exists, err := s.isNamespaceExists(ctx, request.GetNamespace()); err
!= nil {
+ if !exists {
+ return nil, status.Errorf(codes.Aborted, "Namespace Not
Exists")
+ }
+ } else {
+ return nil, status.Errorf(codes.Aborted, err.Error())
+ }
+
+ pushReq := &pushRequest{ConfigsUpdated:
map[model.ConfigKey]metadataConfig{}}
+
+ key := model.ConfigKey{
+ Name: getConfigKeyName(request.GetApplicationName(),
request.GetRevision()),
+ Namespace: request.GetNamespace(),
+ }
+
+ pushReq.ConfigsUpdated[key] = metadataConfig{
+ applicationName: request.GetApplicationName(),
+ revision: request.GetRevision(),
+ metadataInfo: request.GetMetadataInfo(),
+ timestamp: time.Now(),
+ }
+
+ s.ch <- pushReq
+
+ return &dubbov1alpha1.PublishServiceMetadataResponse{}, nil
+}
+
+func getConfigKeyName(applicationName, revision string) string {
+ log.Infof("application name: %s, revision: %s", applicationName,
revision)
+ return fmt.Sprintf("%s-%s", applicationName, revision)
+}
+
+func (s *ServiceMetadataServer) Get(ctx context.Context, request
*dubbov1alpha1.GetServiceMetadataRequest)
(*dubbov1alpha1.GetServiceMetadataResponse, error) {
+ metadata, err :=
s.KubeClient.Istio().ExtensionsV1alpha1().ServiceMetadatas(request.Namespace).Get(ctx,
getConfigKeyName(request.GetApplicationName(), request.GetRevision()),
v1.GetOptions{})
+ if err != nil {
+ return nil, status.Errorf(codes.Aborted, err.Error())
+ }
+
+ return &dubbov1alpha1.GetServiceMetadataResponse{MetadataInfo:
metadata.Spec.GetMetadataInfo()}, nil
+}
+
+func (s *ServiceMetadataServer) isNamespaceExists(ctx context.Context,
namespace string) (bool, error) {
+ _, err := s.KubeClient.Kube().CoreV1().Namespaces().Get(ctx, namespace,
v1.GetOptions{})
+ if err != nil {
+ if !apierror.IsNotFound(err) {
+ return false, err
+ }
+ }
+ return true, nil
+}
+
+func (s *ServiceMetadataServer) handleUpdate(stopCh <-chan struct{}) {
+ s.debounce(stopCh)
+}
+
+func (s *ServiceMetadataServer) Push(req *pushRequest) {
+ ctx := context.TODO()
+
+ for key, config := range req.ConfigsUpdated {
+ metadata, err := getOrCreateCRD(s.KubeClient, ctx,
key.Namespace, config.applicationName, config.revision, config.metadataInfo,
config.timestamp)
+ if err != nil {
+ log.Errorf("Failed to getOrCreateCRD, %s", err.Error())
+ return
+ }
+
+ metadata.Spec.MetadataInfo = config.metadataInfo
+
+ _, err =
s.KubeClient.Istio().ExtensionsV1alpha1().ServiceMetadatas(metadata.Namespace).Update(ctx,
metadata, v1.UpdateOptions{})
+ if err != nil {
+ log.Errorf("Failed to update metadata, %s", err.Error())
+ return
+ }
+ }
+}
+
+func getOrCreateCRD(kubeClient kube.Client, ctx context.Context, namespace
string, applicationName, revision, metadataInfo string, createTime time.Time)
(*v1alpha1.ServiceMetadata, error) {
+ var (
+ metadata *v1alpha1.ServiceMetadata
+ )
+
+ serviceMetadataInterface :=
kubeClient.Istio().ExtensionsV1alpha1().ServiceMetadatas(namespace)
+ crdName := getConfigKeyName(applicationName, revision)
+ metadata, err := serviceMetadataInterface.Get(ctx, crdName,
v1.GetOptions{})
+
+ log.Infof("metadata: %+v", metadata)
+
+ if err != nil {
+ if apierror.IsNotFound(err) {
+ mt := &v1alpha1.ServiceMetadata{
+ ObjectMeta: v1.ObjectMeta{
+ Name: crdName,
+ Namespace: namespace,
+ Annotations: map[string]string{
+ "updateTime":
strconv.FormatInt(createTime.Unix(), 10),
+ },
+ },
+ Spec: istioextensionsv1alpha1.ServiceMetadata{
+ ApplicationName: applicationName,
+ Revision: revision,
+ MetadataInfo: metadataInfo,
+ },
+ }
+
+ metadata, err = serviceMetadataInterface.Create(ctx,
mt, v1.CreateOptions{})
+ if err != nil {
+ return nil, errors.Wrap(err, "Failed to create
metadata")
+ }
+ } else {
+ return nil, errors.Wrap(err, "Failed to check metadata
exists or not")
+ }
+ } else {
+ // if metadata exist in crd, then update timestamp
+ if metadata.ObjectMeta.Annotations != nil {
+ metadata.ObjectMeta.Annotations["updateTime"] =
strconv.FormatInt(createTime.Unix(), 10)
+ } else {
+ metadata.ObjectMeta.Annotations = map[string]string{
+ "updateTime":
strconv.FormatInt(createTime.Unix(), 10),
+ }
+ }
+ }
+
+ return metadata, nil
+}
+
+func (s *ServiceMetadataServer) debounce(stopCh <-chan struct{}) {
+ s.debounceHelper.Debounce(s.ch, stopCh, s.debounceOptions, s.Push,
s.CommittedUpdates)
+}
+
+func (s *ServiceMetadataServer) removeOutdatedCRD(stopChan <-chan struct{}) {
+ ctx, cancel := context.WithCancel(context.TODO())
+ ticker := time.NewTicker(2 * 60 * time.Hour)
+
+ for {
+ select {
+ case <-stopChan:
+ cancel()
+ break
+ case <-ticker.C:
+ namespaces, err :=
s.KubeClient.Kube().CoreV1().Namespaces().List(ctx, v1.ListOptions{})
+ if err != nil {
+ log.Errorf("Failed to get namespaces, %s",
err.Error())
+ continue
+ }
+
+ for _, namespace := range namespaces.Items {
+ metadataList, err :=
s.KubeClient.Istio().ExtensionsV1alpha1().ServiceMetadatas(namespace.GetNamespace()).List(ctx,
v1.ListOptions{})
+ if err != nil {
+ log.Errorf("Failed to get metadata with
namespace {%s}, %s", namespace, err.Error())
+ continue
+ }
+
+ for _, metadata := range metadataList.Items {
+ if metadata.Annotations != nil {
+ if ts, ok :=
metadata.Annotations["updateTime"]; ok {
+ tsInt, err :=
strconv.ParseInt(ts, 10, 64)
+ if err != nil {
+
log.Errorf("Failed to get metadata with namespace {%s}, %s", namespace,
err.Error())
+ } else {
+ if
time.Now().Sub(time.Unix(tsInt, 0)) > 24*time.Hour {
+ err :=
s.KubeClient.Istio().ExtensionsV1alpha1().ServiceMetadatas(namespace.GetNamespace()).Delete(ctx,
metadata.GetName(), v1.DeleteOptions{})
+ if err
!= nil {
+
log.Errorf("Failed to delete outdated metadata with namespace {%s}, metadata
{%s}, err {%s}", namespace, metadata.GetName(), err.Error())
+ }
+ }
+ }
+ } else {
+ log.Errorf("Failed to
get metadata with namespace {%s}, metadata {%s}", namespace, metadata.GetName())
+ }
+ }
+ }
+ }
+
+ }
+ }
+}
diff --git a/pkg/config/schema/collections/collections.agent.gen.go
b/pkg/config/schema/collections/collections.agent.gen.go
index 7fc72567..4b0ac04d 100755
--- a/pkg/config/schema/collections/collections.agent.gen.go
+++ b/pkg/config/schema/collections/collections.agent.gen.go
@@ -28,6 +28,24 @@ import (
var (
+ // IstioExtensionsV1Alpha1Servicemetadatas describes the collection
+ // istio/extensions/v1alpha1/servicemetadatas
+ IstioExtensionsV1Alpha1Servicemetadatas = collection.Builder{
+ Name: "istio/extensions/v1alpha1/servicemetadatas",
+ VariableName: "IstioExtensionsV1Alpha1Servicemetadatas",
+ Resource: resource.Builder{
+ Group: "extensions.istio.io",
+ Kind: "ServiceMetadata",
+ Plural: "servicemetadatas",
+ Version: "v1alpha1",
+ Proto: "istio.extensions.v1alpha1.ServiceMetadata",
StatusProto: "istio.meta.v1alpha1.IstioStatus",
+ ReflectType:
reflect.TypeOf(&istioioapiextensionsv1alpha1.ServiceMetadata{}).Elem(),
StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(),
+ ProtoPackage: "istio.io/api/extensions/v1alpha1",
StatusPackage: "istio.io/api/meta/v1alpha1",
+ ClusterScoped: false,
+ ValidateProto: validation.EmptyValidate,
+ }.MustBuild(),
+ }.MustBuild()
+
// IstioExtensionsV1Alpha1Servicenamemappings describes the collection
// istio/extensions/v1alpha1/servicenamemappings
IstioExtensionsV1Alpha1Servicenamemappings = collection.Builder{
@@ -336,6 +354,7 @@ var (
// All contains all collections in the system.
All = collection.NewSchemasBuilder().
+ MustAdd(IstioExtensionsV1Alpha1Servicemetadatas).
MustAdd(IstioExtensionsV1Alpha1Servicenamemappings).
MustAdd(IstioExtensionsV1Alpha1Wasmplugins).
MustAdd(IstioMeshV1Alpha1MeshConfig).
@@ -358,6 +377,7 @@ var (
// Istio contains only Istio collections.
Istio = collection.NewSchemasBuilder().
MustAdd(IstioExtensionsV1Alpha1Servicenamemappings).
+ MustAdd(IstioExtensionsV1Alpha1Servicemetadatas).
MustAdd(IstioExtensionsV1Alpha1Wasmplugins).
MustAdd(IstioMeshV1Alpha1MeshConfig).
MustAdd(IstioMeshV1Alpha1MeshNetworks).
@@ -387,6 +407,7 @@ var (
// Pilot contains only collections used by Pilot.
Pilot = collection.NewSchemasBuilder().
+ MustAdd(IstioExtensionsV1Alpha1Servicemetadatas).
MustAdd(IstioExtensionsV1Alpha1Servicenamemappings).
MustAdd(IstioExtensionsV1Alpha1Wasmplugins).
MustAdd(IstioNetworkingV1Alpha3Destinationrules).
@@ -406,6 +427,7 @@ var (
// PilotGatewayAPI contains only collections used by Pilot, including
experimental Service Api.
PilotGatewayAPI = collection.NewSchemasBuilder().
+ MustAdd(IstioExtensionsV1Alpha1Servicemetadatas).
MustAdd(IstioExtensionsV1Alpha1Servicenamemappings).
MustAdd(IstioExtensionsV1Alpha1Wasmplugins).
MustAdd(IstioNetworkingV1Alpha3Destinationrules).
diff --git a/pkg/config/schema/collections/collections.gen.go
b/pkg/config/schema/collections/collections.gen.go
index df9d0fa5..45fa0bb9 100755
--- a/pkg/config/schema/collections/collections.gen.go
+++ b/pkg/config/schema/collections/collections.gen.go
@@ -34,6 +34,24 @@ import (
var (
+ // IstioExtensionsV1Alpha1Servicemetadatas describes the collection
+ // istio/extensions/v1alpha1/servicemetadatas
+ IstioExtensionsV1Alpha1Servicemetadatas = collection.Builder{
+ Name: "istio/extensions/v1alpha1/servicemetadatas",
+ VariableName: "IstioExtensionsV1Alpha1Servicemetadatas",
+ Resource: resource.Builder{
+ Group: "extensions.istio.io",
+ Kind: "ServiceMetadata",
+ Plural: "servicemetadatas",
+ Version: "v1alpha1",
+ Proto: "istio.extensions.v1alpha1.ServiceMetadata",
StatusProto: "istio.meta.v1alpha1.IstioStatus",
+ ReflectType:
reflect.TypeOf(&istioioapiextensionsv1alpha1.ServiceMetadata{}).Elem(),
StatusType: reflect.TypeOf(&istioioapimetav1alpha1.IstioStatus{}).Elem(),
+ ProtoPackage: "istio.io/api/extensions/v1alpha1",
StatusPackage: "istio.io/api/meta/v1alpha1",
+ ClusterScoped: false,
+ ValidateProto: validation.EmptyValidate,
+ }.MustBuild(),
+ }.MustBuild()
+
// IstioExtensionsV1Alpha1Servicenamemappings describes the collection
// istio/extensions/v1alpha1/servicenamemappings
IstioExtensionsV1Alpha1Servicenamemappings = collection.Builder{
@@ -641,6 +659,7 @@ var (
// All contains all collections in the system.
All = collection.NewSchemasBuilder().
+ MustAdd(IstioExtensionsV1Alpha1Servicemetadatas).
MustAdd(IstioExtensionsV1Alpha1Servicenamemappings).
MustAdd(IstioExtensionsV1Alpha1Wasmplugins).
MustAdd(IstioMeshV1Alpha1MeshConfig).
@@ -679,6 +698,7 @@ var (
// Istio contains only Istio collections.
Istio = collection.NewSchemasBuilder().
+ MustAdd(IstioExtensionsV1Alpha1Servicemetadatas).
MustAdd(IstioExtensionsV1Alpha1Servicenamemappings).
MustAdd(IstioExtensionsV1Alpha1Wasmplugins).
MustAdd(IstioMeshV1Alpha1MeshConfig).
@@ -737,6 +757,7 @@ var (
// Pilot contains only collections used by Pilot.
Pilot = collection.NewSchemasBuilder().
+ MustAdd(IstioExtensionsV1Alpha1Servicemetadatas).
MustAdd(IstioExtensionsV1Alpha1Servicenamemappings).
MustAdd(IstioExtensionsV1Alpha1Wasmplugins).
MustAdd(IstioNetworkingV1Alpha3Destinationrules).
@@ -756,6 +777,7 @@ var (
// PilotGatewayAPI contains only collections used by Pilot, including
experimental Service Api.
PilotGatewayAPI = collection.NewSchemasBuilder().
+ MustAdd(IstioExtensionsV1Alpha1Servicemetadatas).
MustAdd(IstioExtensionsV1Alpha1Servicenamemappings).
MustAdd(IstioExtensionsV1Alpha1Wasmplugins).
MustAdd(IstioNetworkingV1Alpha3Destinationrules).
diff --git a/pkg/config/schema/gvk/resources.gen.go
b/pkg/config/schema/gvk/resources.gen.go
index 20703626..a52a9f88 100755
--- a/pkg/config/schema/gvk/resources.gen.go
+++ b/pkg/config/schema/gvk/resources.gen.go
@@ -33,6 +33,7 @@ var (
Secret = config.GroupVersionKind{Group: "",
Version: "v1", Kind: "Secret"}
Service = config.GroupVersionKind{Group: "",
Version: "v1", Kind: "Service"}
ServiceEntry = config.GroupVersionKind{Group:
"networking.istio.io", Version: "v1alpha3", Kind: "ServiceEntry"}
+ ServiceMetadata = config.GroupVersionKind{Group:
"extensions.istio.io", Version: "v1alpha1", Kind: "ServiceMetadata"}
ServiceNameMapping = config.GroupVersionKind{Group:
"extensions.istio.io", Version: "v1alpha1", Kind: "ServiceNameMapping"}
Sidecar = config.GroupVersionKind{Group:
"networking.istio.io", Version: "v1alpha3", Kind: "Sidecar"}
TCPRoute = config.GroupVersionKind{Group:
"gateway.networking.k8s.io", Version: "v1alpha2", Kind: "TCPRoute"}
diff --git a/pkg/config/schema/metadata.yaml b/pkg/config/schema/metadata.yaml
index 9815f135..d1c0d965 100644
--- a/pkg/config/schema/metadata.yaml
+++ b/pkg/config/schema/metadata.yaml
@@ -180,6 +180,11 @@ collections:
name: "k8s/gateway_api/v1alpha2/referencepolicies"
group: "gateway.networking.k8s.io"
+ - kind: "ServiceMetadata"
+ name: "istio/extensions/v1alpha1/servicemetadatas"
+ group: "extensions.istio.io"
+ pilot: true
+
- name: "istio/extensions/v1alpha1/servicenamemappings"
kind: ServiceNameMapping
group: "extensions.istio.io"
@@ -474,6 +479,16 @@ resources:
statusProto: "istio.meta.v1alpha1.IstioStatus"
statusProtoPackage: "istio.io/api/meta/v1alpha1"
+ - kind: ServiceMetadata
+ plural: "servicemetadatas"
+ group: "extensions.istio.io"
+ version: "v1alpha1"
+ proto: "istio.extensions.v1alpha1.ServiceMetadata"
+ protoPackage: "istio.io/api/extensions/v1alpha1"
+ description: "describes service metadata"
+ statusProto: "istio.meta.v1alpha1.IstioStatus"
+ statusProtoPackage: "istio.io/api/meta/v1alpha1"
+
- kind: ServiceNameMapping
plural: "servicenamemappings"
group: "extensions.istio.io"