diff --git a/Makefile b/Makefile index c32f4996f..30289a9c8 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ PROJECT_NAME := "d7y.io/dragonfly/v2" DFGET_NAME := "dfget" VERSION := "2.0.0" PKG := "$(PROJECT_NAME)" -PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/ | grep -v '\(/cdnsystem/\|manager\)') +PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/ | grep -v '\(/cdnsystem/\)') GIT_COMMIT := $(shell git rev-parse --verify HEAD --short=7) GIT_COMMIT_LONG := $(shell git rev-parse --verify HEAD) DFGET_ARCHIVE_PREFIX := "$(DFGET_NAME)_$(GIT_COMMIT)" diff --git a/docs/en/config/manager.yaml b/docs/en/config/manager.yaml new file mode 100644 index 000000000..426f888c1 --- /dev/null +++ b/docs/en/config/manager.yaml @@ -0,0 +1,20 @@ +server: + ip: "" + port: 8004 + +configure: + store-name: "store1" + +redis: + addrs: + - "127.0.0.1:6379" + +stores: + - + name: "store1" + source: + mysql: + user: "root" + password: "root1234" + addr: "127.0.0.1:3306" + db: "dragonfly_manager" diff --git a/go.mod b/go.mod index cc421394d..23751e7f9 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.15 require ( github.com/HuKeping/rbtree v0.0.0-20210106022122-8ad34838eb2b github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 + github.com/alicebob/miniredis/v2 v2.14.5 github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect github.com/docker/go-units v0.4.0 @@ -60,6 +61,7 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 gorm.io/driver/mysql v1.0.4 + gorm.io/driver/sqlite v1.1.4 gorm.io/gorm v1.21.3 k8s.io/apimachinery v0.20.1 k8s.io/client-go v11.0.0+incompatible diff --git a/go.sum b/go.sum index ad7802c4f..6ce1e37e9 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,10 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis/v2 v2.14.5 h1:iCFJiSur7871KaFJLAsBEpmc3DJHJ4YuB7W1hYLWs+U= +github.com/alicebob/miniredis/v2 v2.14.5/go.mod h1:gquAfGbzn92jvtrSC69+6zZnwSODVXVpYDRaGhWaL6I= github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible h1:Ft+KeWIJxFP76LqgJbvtOA1qBIoC8vGkTV3QeCOeJC4= github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -47,6 +51,9 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -287,6 +294,8 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ= +github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -436,6 +445,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xo/dburl v0.7.0 h1:sGcxE2sefO6yJwX99EDzZCU0AACvWUKEqT4eIHFDPmY= github.com/xo/dburl v0.7.0/go.mod h1:W68zXnBfTb4zcKLI1yEYRyYIQjcjoyCRn4YMD/QzcpE= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da h1:NimzV1aGyq29m5ukMK0AMWEhFaL/lrEOaephfuoiARg= +github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -548,6 +559,7 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -692,6 +704,9 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclp gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.0.4 h1:TATTzt+kR+IV0+h3iUB3dHUe8omCvQ0rOkmfCsUBohk= gorm.io/driver/mysql v1.0.4/go.mod h1:MEgp8tk2n60cSBCq5iTcPDw3ns8Gs+zOva9EUhkknTs= +gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM= +gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw= +gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.3 h1:qDFi55ZOsjZTwk5eN+uhAmHi8GysJ/qCTichM/yO7ME= gorm.io/gorm v1.21.3/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= diff --git a/manager/config/config.go b/manager/config/config.go index 05e7751b9..d008b4312 100644 --- a/manager/config/config.go +++ b/manager/config/config.go @@ -31,14 +31,23 @@ type MysqlConfig struct { Db string `yaml:"db" mapstructure:"db"` } +type SQLiteConfig struct { + Db string `yaml:"db" mapstructure:"db"` +} + type OssConfig struct { } +// Only one of Mysql, SQLit and Oss can be used at the same time +type StoreSource struct { + Mysql *MysqlConfig `yaml:"mysql,omitempty" mapstructure:"mysql,omitempty"` + SQLite *SQLiteConfig `yaml:"sqlite,omitempty" mapstructure:"sqlite,omitempty"` + Oss *OssConfig `yaml:"oss,omitempty" mapstructure:"oss,omitempty"` +} + type StoreConfig struct { - Name string `yaml:"name" mapstructure:"name"` - Type string `yaml:"type" mapstructure:"type"` - Mysql *MysqlConfig `yaml:"mysql,omitempty" mapstructure:"mysql,omitempty"` - Oss *OssConfig `yaml:"oss,omitempty" mapstructure:"oss,omitempty"` + Name string `yaml:"name" mapstructure:"name"` + Source *StoreSource `yaml:"source" mapstructure:"source"` } type HostService struct { @@ -73,50 +82,63 @@ func New() *Config { Stores: []*StoreConfig{ { Name: "store1", - Type: "mysql", - Mysql: &MysqlConfig{ - User: "root", - Password: "root1234", - Addr: "127.0.0.1:3306", - Db: "dragonfly_manager", + Source: &StoreSource{ + Mysql: &MysqlConfig{ + User: "root", + Password: "root1234", + Addr: "127.0.0.1:3306", + Db: "dragonfly_manager", + }, }, - Oss: nil, }, }, HostService: &HostService{}, } } -func (cfg *StoreConfig) Valid() error { - if (cfg.Mysql == nil && cfg.Oss == nil) || (cfg.Mysql != nil && cfg.Oss != nil) { - return dferrors.Newf(dfcodes.ManagerConfigError, "store config error: please select one of mysql or oss") +func (cfg *StoreConfig) Valid() (string, error) { + if len(cfg.Name) <= 0 { + return "", dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Name is null") } - if cfg.Mysql != nil { - if len(cfg.Mysql.User) == 0 { - return dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Mysql.User is null") - } - - if len(cfg.Mysql.Password) == 0 { - return dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Mysql.Password is null") - } - - if len(cfg.Mysql.Addr) == 0 { - return dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Mysql.Addr is null") - } - - if len(cfg.Mysql.Db) == 0 { - return dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Mysql.Db is null") - } - - return nil + if cfg.Source == nil { + return "", dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Source is null") } - if cfg.Oss != nil { - return dferrors.Newf(dfcodes.ManagerConfigError, "store config error: oss not support yet") + source := cfg.Source + if source.Mysql != nil { + if len(source.Mysql.User) == 0 { + return "", dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Source.Mysql.User is null") + } + + if len(source.Mysql.Password) == 0 { + return "", dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Source.Mysql.Password is null") + } + + if len(source.Mysql.Addr) == 0 { + return "", dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Source.Mysql.Addr is null") + } + + if len(source.Mysql.Db) == 0 { + return "", dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Source.Mysql.Db is null") + } + + return "mysql", nil } - return nil + if source.SQLite != nil { + if len(source.SQLite.Db) == 0 { + return "", dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Source.SQLite.Db is null") + } + + return "sqlite", nil + } + + if source.Oss != nil { + return "", dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Source.Oss not support yet") + } + + return "", dferrors.Newf(dfcodes.ManagerConfigError, "store config error: Source must be set one of mysql, sqlite, oss") } func (cfg *RedisConfig) Valid() error { @@ -140,5 +162,11 @@ func (cfg *Config) Valid() error { return dferrors.Newf(dfcodes.ManagerConfigError, "stores config error: Stores is null") } + for _, store := range cfg.Stores { + if _, err := store.Valid(); err != nil { + return err + } + } + return nil } diff --git a/manager/configsvc/service.go b/manager/configsvc/service.go index b635f41b7..fefd45010 100644 --- a/manager/configsvc/service.go +++ b/manager/configsvc/service.go @@ -147,13 +147,25 @@ func (svc *ConfigSvc) grantKeepAliveLease() (lease.LeaseID, chan struct{}, error return leaseID, ch, nil } +func (svc *ConfigSvc) setKeepAliveLeaseID(id lease.LeaseID) { + svc.mu.Lock() + defer svc.mu.Unlock() + svc.keepAliveLeaseID = id +} + +func (svc *ConfigSvc) getKeepAliveLeaseID() lease.LeaseID { + svc.mu.Lock() + defer svc.mu.Unlock() + return svc.keepAliveLeaseID +} + func (svc *ConfigSvc) grantKeepAliveLeaseLoop() { defer svc.wg.Done() var ka chan struct{} id, ch, err := svc.grantKeepAliveLease() if err == nil { - svc.keepAliveLeaseID = id + svc.setKeepAliveLeaseID(id) ka = ch } @@ -162,12 +174,12 @@ func (svc *ConfigSvc) grantKeepAliveLeaseLoop() { case <-svc.stopC: return case <-ka: - svc.keepAliveLeaseID = lease.NoLease + svc.setKeepAliveLeaseID(lease.NoLease) case <-time.After(svc.grantKeepAliveLeaseIDTime): - if svc.keepAliveLeaseID == lease.NoLease { + if svc.getKeepAliveLeaseID() == lease.NoLease { id, ch, err := svc.grantKeepAliveLease() if err == nil { - svc.keepAliveLeaseID = id + svc.setKeepAliveLeaseID(id) ka = ch } } @@ -183,7 +195,7 @@ func (svc *ConfigSvc) checkKeepAliveLoop() { case <-svc.stopC: return case <-time.After(svc.checkKeepAliveTime): - if svc.keepAliveLeaseID != lease.NoLease { + if svc.getKeepAliveLeaseID() != lease.NoLease { svc.updateAllInstanceState() } } diff --git a/manager/server/lease_test.go b/manager/server/lease_test.go index ef3c2009f..04b0d4f38 100644 --- a/manager/server/lease_test.go +++ b/manager/server/lease_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "d7y.io/dragonfly/v2/manager/config" "d7y.io/dragonfly/v2/manager/lease" + "d7y.io/dragonfly/v2/manager/server/mocks" "d7y.io/dragonfly/v2/manager/store/client" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" @@ -186,7 +186,7 @@ func (suite *LeaseTestSuite) randPrefix() string { func (suite *LeaseTestSuite) SetupTest() { assert := assert.New(suite.T()) - cfg := config.New() + cfg := mocks.NewMockConfig() store, err := client.NewStore(cfg) assert.Nil(err) assert.NotNil(store) diff --git a/manager/server/mocks/config_mock.go b/manager/server/mocks/config_mock.go new file mode 100644 index 000000000..010dd3bb1 --- /dev/null +++ b/manager/server/mocks/config_mock.go @@ -0,0 +1,35 @@ +package mocks + +import ( + "path" + + "d7y.io/dragonfly/v2/internal/dfpath" + "d7y.io/dragonfly/v2/manager/config" +) + +func NewMockConfig() *config.Config { + return &config.Config{ + Server: &config.ServerConfig{ + Port: 8004, + }, + Configure: &config.ConfigureConfig{ + StoreName: "store1", + }, + Redis: &config.RedisConfig{ + User: "", + Password: "", + Addrs: []string{"127.0.0.1:6379"}, + }, + Stores: []*config.StoreConfig{ + { + Name: "store1", + Source: &config.StoreSource{ + SQLite: &config.SQLiteConfig{ + Db: path.Join(dfpath.WorkHome, "dragonfly_manager.db"), + }, + }, + }, + }, + HostService: &config.HostService{}, + } +} diff --git a/manager/server/server.go b/manager/server/server.go index c9456e770..e2f3d1bde 100644 --- a/manager/server/server.go +++ b/manager/server/server.go @@ -112,6 +112,4 @@ func (s *Server) Stop() { if err != nil { logger.Errorf("failed to stop manager http server: %+v", err) } - - s.httpServer = nil } diff --git a/manager/server/server_test.go b/manager/server/server_test.go index 02f6507bd..79d849a91 100644 --- a/manager/server/server_test.go +++ b/manager/server/server_test.go @@ -8,19 +8,21 @@ import ( "time" "d7y.io/dragonfly/v2/manager/apis/v2/types" - "d7y.io/dragonfly/v2/manager/config" "d7y.io/dragonfly/v2/manager/configsvc" + "d7y.io/dragonfly/v2/manager/server/mocks" "d7y.io/dragonfly/v2/manager/store" "d7y.io/dragonfly/v2/pkg/basic/dfnet" "d7y.io/dragonfly/v2/pkg/dflog/logcore" "d7y.io/dragonfly/v2/pkg/rpc/manager" "d7y.io/dragonfly/v2/pkg/rpc/manager/client" + "github.com/alicebob/miniredis/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) type ServerTestSuite struct { suite.Suite + redis *miniredis.Miniredis server *Server client client.ManagerClient } @@ -764,7 +766,11 @@ func (suite *ServerTestSuite) SetupSuite() { configsvc.KeepAliveTimeoutMax = 2 * time.Second _ = logcore.InitManager(false) - cfg := config.New() + cfg := mocks.NewMockConfig() + + suite.redis = miniredis.NewMiniRedis() + _ = suite.redis.StartAddr(cfg.Redis.Addrs[0]) + server, err := New(cfg) assert.Nil(err) assert.NotNil(server) @@ -785,6 +791,7 @@ func (suite *ServerTestSuite) SetupSuite() { } func (suite *ServerTestSuite) TearDownSuite() { + suite.redis.Close() suite.server.Stop() } diff --git a/manager/store/client/client.go b/manager/store/client/client.go index d69beb7f7..bc975663c 100644 --- a/manager/store/client/client.go +++ b/manager/store/client/client.go @@ -11,7 +11,8 @@ import ( type storeSetup func(cfg *config.StoreConfig) (store.Store, error) var storePlugins = map[string]storeSetup{ - "mysql": orm.NewOrmStore, + "mysql": orm.NewMySQLOrmStore, + "sqlite": orm.NewSQLiteOrmStore, } func NewStore(cfg *config.Config) (store.Store, error) { @@ -20,8 +21,13 @@ func NewStore(cfg *config.Config) (store.Store, error) { } for _, store := range cfg.Stores { + source, err := store.Valid() + if err != nil { + return nil, err + } + if cfg.Configure.StoreName == store.Name { - p, ok := storePlugins[store.Type] + p, ok := storePlugins[source] if ok { s, err := p(store) if err != nil { diff --git a/manager/store/orm/orm_store.go b/manager/store/orm/orm_store.go index de1129305..02caf1a27 100644 --- a/manager/store/orm/orm_store.go +++ b/manager/store/orm/orm_store.go @@ -2,15 +2,20 @@ package orm import ( "context" + "fmt" "net/url" + "os" + "path" "d7y.io/dragonfly/v2/manager/config" "d7y.io/dragonfly/v2/manager/store" "d7y.io/dragonfly/v2/pkg/dfcodes" "d7y.io/dragonfly/v2/pkg/dferrors" + "d7y.io/dragonfly/v2/pkg/util/fileutils" "github.com/iancoleman/strcase" "github.com/xo/dburl" "gorm.io/driver/mysql" + "gorm.io/driver/sqlite" "gorm.io/gorm" ) @@ -31,19 +36,24 @@ var ormTables = map[store.ResourceType]newTableSetup{ store.Lease: NewLeaseStore, } -func newOrmStore(cfg *config.StoreConfig) (*ormStore, error) { - if err := cfg.Valid(); err != nil { +func NewMySQLOrmStore(cfg *config.StoreConfig) (store.Store, error) { + source, err := cfg.Valid() + if err != nil { return nil, err } + if source != "mysql" { + return nil, dferrors.Newf(dfcodes.ManagerConfigError, "store config error: source is not mysql") + } + u, err := dburl.Parse("mysql://user:pass@localhost/dbname?") if err != nil { return nil, err } - u.Host = cfg.Mysql.Addr - u.Path = cfg.Mysql.Db - u.User = url.UserPassword(cfg.Mysql.User, cfg.Mysql.Password) + u.Host = cfg.Source.Mysql.Addr + u.Path = cfg.Source.Mysql.Db + u.User = url.UserPassword(cfg.Source.Mysql.User, cfg.Source.Mysql.Password) q := u.Query() q.Add("charset", "utf8") q.Add("parseTime", "True") @@ -79,8 +89,76 @@ func newOrmStore(cfg *config.StoreConfig) (*ormStore, error) { return orm, nil } -func NewOrmStore(cfg *config.StoreConfig) (store.Store, error) { - return newOrmStore(cfg) +func createSQLiteDB(db string) error { + dir := path.Dir(db) + if !fileutils.PathExist(dir) { + if err := fileutils.MkdirAll(dir); err != nil { + return err + } + } + + if !fileutils.PathExist(db) { + dbFile, err := os.OpenFile(db, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + return err + } + defer dbFile.Close() + } + + return nil +} + +func NewSQLiteOrmStore(cfg *config.StoreConfig) (store.Store, error) { + source, err := cfg.Valid() + if err != nil { + return nil, err + } + + if source != "sqlite" { + return nil, dferrors.Newf(dfcodes.ManagerConfigError, "store config error: source is not sqlite") + } + + if err := createSQLiteDB(cfg.Source.SQLite.Db); err != nil { + return nil, err + } + + u, err := dburl.Parse(fmt.Sprintf("sqlite://%s?", cfg.Source.SQLite.Db)) + if err != nil { + return nil, err + } + + q := u.Query() + q.Add("cache", "shared") + q.Add("mode", "memory") + u.RawQuery = q.Encode() + dsn, err := dburl.GenOpaque(u) + if err != nil { + return nil, err + } + + db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{}) + if err != nil { + return nil, err + } + + orm := &ormStore{ + db: db, + stores: make(map[store.ResourceType]store.Store), + tables: make(map[store.ResourceType]string), + } + + for t, f := range ormTables { + table := strcase.ToSnake(t.String()) + s, err := f(db, table) + if err != nil { + return nil, err + } + + orm.stores[t] = s + orm.tables[t] = table + } + + return orm, nil } func (orm *ormStore) listTables() []string {