Преглед изворни кода

feat: 修正config的包名,加上多配置源的处理

lihf пре 3 месеци
родитељ
комит
bec5909909
7 измењених фајлова са 242 додато и 7 уклоњено
  1. 2 3
      config/config.go
  2. 1 1
      config/config_test.go
  3. 130 0
      config/init.go
  4. 96 0
      config/source.go
  5. 1 1
      config/watcher.go
  6. 5 2
      go.mod
  7. 7 0
      go.sum

+ 2 - 3
config/config.go

@@ -1,13 +1,12 @@
-package etcd
+package config
 
 import (
 	"context"
 	"errors"
+	clientv3 "go.etcd.io/etcd/client/v3"
 	"path/filepath"
 	"strings"
 
-	clientv3 "go.etcd.io/etcd/client/v3"
-
 	"github.com/go-kratos/kratos/v2/config"
 )
 

+ 1 - 1
config/config_test.go

@@ -1,4 +1,4 @@
-package etcd
+package config
 
 import (
 	"context"

+ 130 - 0
config/init.go

@@ -0,0 +1,130 @@
+package config
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+
+	"dario.cat/mergo"
+	"git.ikuban.com/server/yaml"
+	"github.com/elliotchance/orderedmap/v3"
+	"github.com/go-kratos/kratos/v2/config"
+	"google.golang.org/protobuf/encoding/protojson"
+	"google.golang.org/protobuf/proto"
+)
+
+func Init(sources []Source, bc any) error {
+	configSources := make([]config.Source, 0)
+	sourceMap := orderedmap.NewOrderedMap[string, any]() // 有序map控制优先级
+
+	for _, source := range sources {
+		if !source.Validate() {
+			continue
+		}
+		s, err := source.NewSource()
+		if err != nil {
+			return fmt.Errorf("%v, new source error: %v", source.String(), err)
+		}
+
+		kvs, err := s.Load()
+		if err != nil {
+			return fmt.Errorf("%v, source load error: %v", source.String(), err)
+		}
+
+		for _, v := range kvs {
+			sourceMap.Set(v.Key, map[string]any{})
+		}
+
+		configSources = append(configSources, s)
+	}
+
+	if sourceMap.Len() == 0 {
+		return errors.New("config source is empty")
+	}
+
+	c := config.New(
+		config.WithSource(configSources...),
+		config.WithDecoder(func(kv *config.KeyValue, v map[string]interface{}) error {
+
+			ok := sourceMap.Has(kv.Key)
+			if !ok {
+				return errors.New("unknown config key")
+			}
+
+			next := map[string]any{}
+			err := yaml.Unmarshal(kv.Value, next)
+			if err != nil {
+				return err
+			}
+
+			sourceMap.Set(kv.Key, convertMap(next))
+
+			merged := make(map[string]any)
+			for c := range sourceMap.Values() {
+				err = mergo.Merge(&merged, c, mergo.WithOverride)
+				if err != nil {
+					return err
+				}
+			}
+
+			b, err := marshalJSON(merged)
+			if err != nil {
+				return err
+			}
+
+			err = unmarshalJSON(b, bc)
+			if err != nil {
+				return err
+			}
+
+			return nil
+		}),
+	)
+	if err := c.Load(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func marshalJSON(v interface{}) ([]byte, error) {
+	if m, ok := v.(proto.Message); ok {
+		return protojson.MarshalOptions{EmitUnpopulated: true}.Marshal(m)
+	}
+	return json.Marshal(v)
+}
+
+func unmarshalJSON(data []byte, v interface{}) error {
+	if m, ok := v.(proto.Message); ok {
+		return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(data, m)
+	}
+	return json.Unmarshal(data, v)
+}
+
+func convertMap(src interface{}) interface{} {
+	switch m := src.(type) {
+	case map[string]interface{}:
+		dst := make(map[string]interface{}, len(m))
+		for k, v := range m {
+			dst[k] = convertMap(v)
+		}
+		return dst
+	case map[interface{}]interface{}:
+		dst := make(map[string]interface{}, len(m))
+		for k, v := range m {
+			dst[fmt.Sprint(k)] = convertMap(v)
+		}
+		return dst
+	case []interface{}:
+		dst := make([]interface{}, len(m))
+		for k, v := range m {
+			dst[k] = convertMap(v)
+		}
+		return dst
+	case []byte:
+		// there will be no binary data in the config data
+		return string(m)
+	default:
+		return src
+	}
+}

+ 96 - 0
config/source.go

@@ -0,0 +1,96 @@
+package config
+
+import (
+	"fmt"
+	"github.com/go-kratos/kratos/v2/config"
+	"github.com/go-kratos/kratos/v2/config/file"
+	clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+type Source interface {
+	NewSource() (config.Source, error)
+	Validate() bool
+	String() string
+}
+
+type Format string
+
+const (
+	Yaml Format = "yaml"
+)
+
+var formatMap = map[Format]string{
+	Yaml: "yaml",
+}
+
+func (f Format) String() string {
+	return string(f)
+}
+
+func (f Format) Validate() bool {
+	_, ok := formatMap[f]
+	return ok
+}
+
+// =======================================
+
+type EtcdSource struct {
+	Format    Format
+	Client    *clientv3.Client
+	Namespace string
+	Name      string
+}
+
+func (s *EtcdSource) NewSource() (config.Source, error) {
+	source, err := New(s.Client, WithPath(fmt.Sprintf("/%s/config/%s", s.Namespace, s.Name)))
+	if err != nil {
+		return nil, err
+	}
+	return source, nil
+}
+
+func (s *EtcdSource) Validate() bool {
+	if !s.Format.Validate() {
+		return false
+	}
+	if s.Client == nil {
+		return false
+	}
+	if s.Namespace == "" {
+		return false
+	}
+	if s.Name == "" {
+		return false
+	}
+	return true
+}
+
+func (s *EtcdSource) String() string {
+	return fmt.Sprintf("etcd source format:%v, namespace:%v, name:%v", s.Format, s.Namespace, s.Name)
+}
+
+// =======================================
+
+type FileSource struct {
+	Format Format
+	Path   string
+}
+
+func (s *FileSource) NewSource() (config.Source, error) {
+	source := file.NewSource(s.Path)
+	return source, nil
+}
+
+func (s *FileSource) Validate() bool {
+	if !s.Format.Validate() {
+		return false
+	}
+	if s.Path == "" {
+		return false
+	}
+	return true
+}
+
+func (s *FileSource) String() string {
+	return fmt.Sprintf("file source format:%v, path:%v", s.Format, s.Path)
+}

+ 1 - 1
config/watcher.go

@@ -1,4 +1,4 @@
-package etcd
+package config
 
 import (
 	"context"

+ 5 - 2
go.mod

@@ -3,15 +3,19 @@ module git.ikuban.com/server/kratos-etcd
 go 1.23.0
 
 require (
+	dario.cat/mergo v1.0.0
+	git.ikuban.com/server/yaml v0.0.0-20220411094446-ff9c47c8eeaf
+	github.com/elliotchance/orderedmap/v3 v3.1.0
 	github.com/go-kratos/kratos/v2 v2.8.3
 	go.etcd.io/etcd/client/v3 v3.5.18
 	google.golang.org/grpc v1.61.1
+	google.golang.org/protobuf v1.33.0
 )
 
 require (
-	dario.cat/mergo v1.0.0 // indirect
 	github.com/coreos/go-semver v0.3.0 // indirect
 	github.com/coreos/go-systemd/v22 v22.3.2 // indirect
+	github.com/fsnotify/fsnotify v1.6.0 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/kr/text v0.2.0 // indirect
@@ -26,6 +30,5 @@ require (
 	google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
-	google.golang.org/protobuf v1.33.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )

+ 7 - 0
go.sum

@@ -1,5 +1,7 @@
 dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
 dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+git.ikuban.com/server/yaml v0.0.0-20220411094446-ff9c47c8eeaf h1:fqbBaasBkDOOUIGVe1ikz/dHzTLP9e0HayM4NhvhvXs=
+git.ikuban.com/server/yaml v0.0.0-20220411094446-ff9c47c8eeaf/go.mod h1:eovuz52s0DRORmHHZrvkUpRUwDMzFCeKQEm6GwiHOwM=
 github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
@@ -8,6 +10,10 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
+github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/go-kratos/kratos/v2 v2.8.3 h1:kkNBq0gvdX+b8cbaN+p6Sdh95DgMhx7GimefXb4o7Ss=
 github.com/go-kratos/kratos/v2 v2.8.3/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -67,6 +73,7 @@ golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
 golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=