@@ -19,6 +19,7 @@ package etcd
1919
2020import (
2121 "encoding/json"
22+ "errors"
2223 "fmt"
2324 "strings"
2425)
@@ -29,6 +30,8 @@ import (
2930 "github.com/dubbogo/gost/log/logger"
3031
3132 perrors "github.com/pkg/errors"
33+
34+ clientv3 "go.etcd.io/etcd/client/v3"
3235)
3336
3437import (
@@ -40,6 +43,26 @@ import (
4043 "dubbo.apache.org/dubbo-go/v3/metadata/report"
4144)
4245
46+ // etcdClient abstracts the etcd Client operations used by etcdMetadataReport.
47+ type etcdClient interface {
48+ Get (key string ) (string , error )
49+ Put (key , value string ) error
50+ Delete (key string ) error
51+ GetChildren (key string ) ([]string , []string , error )
52+ GetValAndRev (key string ) (string , int64 , error )
53+ Create (key , value string ) error
54+ UpdateWithRev (key , value string , rev int64 , opts ... clientv3.OpOption ) error
55+ }
56+
57+ // etcdClientWrapper wraps *gxetcd.Client to implement etcdClient.
58+ type etcdClientWrapper struct {
59+ * gxetcd.Client
60+ }
61+
62+ func (w etcdClientWrapper ) Put (key , value string ) error {
63+ return w .Client .Put (key , value )
64+ }
65+
4366const DEFAULT_ROOT = "dubbo"
4467
4568func init () {
@@ -50,13 +73,19 @@ func init() {
5073
5174// etcdMetadataReport is the implementation of MetadataReport based etcd
5275type etcdMetadataReport struct {
53- client * gxetcd. Client
76+ client etcdClient
5477 rootDir string
78+ url * common.URL
79+ }
80+
81+ // URL returns the URL used to create this metadata report.
82+ func (e * etcdMetadataReport ) URL () * common.URL {
83+ return e .url
5584}
5685
5786// GetAppMetadata get metadata info from etcd
5887func (e * etcdMetadataReport ) GetAppMetadata (application , revision string ) (* info.MetadataInfo , error ) {
59- key := e .rootDir + application + constant .PathSeparator + revision
88+ key := e .rootDir + constant . PathSeparator + application + constant .PathSeparator + revision
6089 data , err := e .client .Get (key )
6190 if err != nil {
6291 return nil , err
@@ -68,7 +97,7 @@ func (e *etcdMetadataReport) GetAppMetadata(application, revision string) (*info
6897
6998// PublishAppMetadata publish metadata info to etcd
7099func (e * etcdMetadataReport ) PublishAppMetadata (application , revision string , info * info.MetadataInfo ) error {
71- key := e .rootDir + application + constant .PathSeparator + revision
100+ key := e .rootDir + constant . PathSeparator + application + constant .PathSeparator + revision
72101 value , err := json .Marshal (info )
73102 if err == nil {
74103 err = e .client .Put (key , string (value ))
@@ -125,6 +154,35 @@ func (e *etcdMetadataReport) RemoveServiceAppMappingListener(key string, group s
125154 return nil
126155}
127156
157+ // UnPublishAppMetadata removes metadata for a specific revision from etcd.
158+ // This operation is idempotent.
159+ func (e * etcdMetadataReport ) UnPublishAppMetadata (application , revision string ) error {
160+ key := e .rootDir + constant .PathSeparator + application + constant .PathSeparator + revision
161+ return e .client .Delete (key )
162+ }
163+
164+ func (e * etcdMetadataReport ) ListAppRevisions (application string ) ([]report.AppRevision , error ) {
165+ prefix := e .rootDir + constant .PathSeparator + application + constant .PathSeparator
166+ keys , values , err := e .client .GetChildren (prefix )
167+ if err != nil {
168+ if errors .Is (perrors .Cause (err ), gxetcd .ErrKVPairNotFound ) {
169+ return nil , nil
170+ }
171+ return nil , err
172+ }
173+
174+ result := make ([]report.AppRevision , 0 , len (keys ))
175+ for i , key := range keys {
176+ // Extract revision from key suffix (key is full path, revision is last segment)
177+ revision := key [strings .LastIndex (key , constant .PathSeparator )+ 1 :]
178+ result = append (result , report.AppRevision {
179+ Revision : revision ,
180+ ModifyTime : report .ParseMetadataLastUpdatedTime ([]byte (values [i ])),
181+ })
182+ }
183+ return result , nil
184+ }
185+
128186type etcdMetadataReportFactory struct {}
129187
130188// CreateMetadataReport get the MetadataReport instance of etcd
@@ -138,5 +196,5 @@ func (e *etcdMetadataReportFactory) CreateMetadataReport(url *common.URL) report
138196 }
139197 group := url .GetParam (constant .MetadataReportGroupKey , DEFAULT_ROOT )
140198 group = constant .PathSeparator + strings .TrimPrefix (group , constant .PathSeparator )
141- return & etcdMetadataReport {client : client , rootDir : group }
199+ return & etcdMetadataReport {client : etcdClientWrapper { client } , rootDir : group , url : url }
142200}
0 commit comments