diff --git a/Gopkg.lock b/Gopkg.lock index 4367ea9..e2f49da 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -19,6 +19,13 @@ revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f" version = "v0.4.7" +[[projects]] + name = "github.com/boltdb/bolt" + packages = ["."] + revision = "48ea1b39c25fc1bab3506fbc712ecbaa842c4d2d" + source = "https://github.com/coreos/bbolt.git" + version = "v1.3.1-coreos.6" + [[projects]] branch = "master" name = "github.com/cuigh/auxo" @@ -208,7 +215,10 @@ [[projects]] branch = "master" name = "golang.org/x/sys" - packages = ["windows"] + packages = [ + "unix", + "windows" + ] revision = "2f57af4873d00d535c5c9028850aa2152e6a5566" [[projects]] @@ -226,6 +236,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "dea823c7646b8b71101e0443b1c0c36a723989261f733ad4cb44c6e9b0d3942d" + inputs-digest = "fe9c254e0c7e739370b6380ed53816426b8245a1610db2cfc612aec63aa01701" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 20df757..34fb13e 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -39,4 +39,9 @@ required = ["github.com/docker/distribution"] [[constraint]] name = "github.com/docker/distribution" - branch = "master" \ No newline at end of file + branch = "master" + +[[constraint]] + name = "github.com/boltdb/bolt" + source = "https://github.com/coreos/bbolt.git" + version = "1.3.1-coreos.6" diff --git a/README.md b/README.md index dea7032..babc230 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Only these options can be set by environment variables for now. | Name | Value | | --------------- | -----------------------------| -| DB_TYPE | mongo | +| DB_TYPE | mongo,bolt | | DB_ADDRESS | localhost:27017/swirl | | DOCKER_ENDPOINT | tcp://docker-proxy:2375 | | AUTH_TIMEOUT | 4h | @@ -86,6 +86,8 @@ Docker support mounting configuration file through swarm from v17.06, so you can ## Deployment +Swirl support two storage engines now: mongo and bolt. **bolt** is suitable for develepment environment, **Swirl** can only deploy one replica if you use **bolt** storage engine. + ### Stand alone Just copy the swirl binary and config/assets/views directories to the host, and run it. @@ -96,6 +98,19 @@ Just copy the swirl binary and config/assets/views directories to the host, and ### Docker +* Use **bolt** storage engine + +```bash +docker run -d -p 8001:8001 \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v /data/swirl:/data/swirl \ + -e DB_TYPE=bolt \ + --name=swirl \ + cuigh/swirl +``` + +* Use **MongoDB** storage engine + ```bash docker run -d -p 8001:8001 \ --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \ @@ -107,6 +122,21 @@ docker run -d -p 8001:8001 \ ### Docker swarm +* Use **bolt** storage engine + +```bash +docker service create \ + --name=swirl \ + --publish=8001:8001/tcp \ + --env DB_TYPE=bolt \ + --constraint=node.role==manager \ + --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \ + --mount=type=bind,src=/data/swirl,dst=/data/swirl \ + cuigh/swirl +``` + +* Use **MongoDB** storage engine + ```bash docker service create \ --name=swirl \ diff --git a/dao/bolt/bolt.go b/dao/bolt/bolt.go new file mode 100644 index 0000000..0ae28f4 --- /dev/null +++ b/dao/bolt/bolt.go @@ -0,0 +1,152 @@ +package bolt + +import ( + "encoding/binary" + "encoding/json" + "path/filepath" + "strings" + + "github.com/boltdb/bolt" + "github.com/cuigh/auxo/errors" + "github.com/cuigh/auxo/log" +) + +type Value []byte + +func (v Value) Unmarshal(i interface{}) error { + return json.Unmarshal([]byte(v), i) +} + +// Dao implements dao.Interface interface. +type Dao struct { + logger log.Logger + db *bolt.DB +} + +// New creates a Dao instance. +func New(addr string) (*Dao, error) { + if addr == "" { + addr = "/data/bolt" + } + + db, err := bolt.Open(filepath.Join(addr, "swirl.db"), 0600, nil) + if err != nil { + return nil, errors.Wrap(err, "failed to open bolt database") + } + + d := &Dao{ + logger: log.Get("bolt"), + db: db, + } + return d, nil +} + +func (d *Dao) Init() { + d.db.Update(func(tx *bolt.Tx) error { + tx.CreateBucketIfNotExists([]byte("chart")) + tx.CreateBucketIfNotExists([]byte("dashboard")) + tx.CreateBucketIfNotExists([]byte("event")) + tx.CreateBucketIfNotExists([]byte("perm")) + tx.CreateBucketIfNotExists([]byte("registry")) + tx.CreateBucketIfNotExists([]byte("role")) + tx.CreateBucketIfNotExists([]byte("session")) + tx.CreateBucketIfNotExists([]byte("setting")) + tx.CreateBucketIfNotExists([]byte("stack")) + tx.CreateBucketIfNotExists([]byte("template")) + tx.CreateBucketIfNotExists([]byte("user")) + return nil + }) +} + +func (d *Dao) Close() { + d.db.Close() +} + +func (d *Dao) update(bucket, key string, value interface{}) error { + return d.db.Update(func(tx *bolt.Tx) error { + buf, err := json.Marshal(value) + if err != nil { + return err + } + + b := tx.Bucket([]byte(bucket)) + return b.Put([]byte(key), buf) + }) +} + +func (d *Dao) delete(bucket, key string) error { + return d.db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(bucket)) + return b.Delete([]byte(key)) + }) +} + +func (d *Dao) get(bucket, key string) (val Value, err error) { + err = d.db.View(func(tx *bolt.Tx) error { + if b := tx.Bucket([]byte(bucket)); b != nil { + v := b.Get([]byte(key)) + if v != nil { + val = Value(v) + } + } + return nil + }) + return +} + +func (d *Dao) count(bucket string) (count int, err error) { + err = d.db.View(func(tx *bolt.Tx) error { + if b := tx.Bucket([]byte(bucket)); b != nil { + count = b.Stats().KeyN + } + return nil + }) + return +} + +func (d *Dao) each(bucket string, fn func(v Value) error) (err error) { + return d.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(bucket)) + return b.ForEach(func(k, v []byte) error { + return fn(Value(v)) + }) + }) +} + +func (d *Dao) slice(bucket string, fn func(v Value) error, keys ...string) (err error) { + return d.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(bucket)) + for _, key := range keys { + if data := b.Get([]byte(key)); data != nil { + if err = fn(Value(data)); err != nil { + return err + } + } + } + return nil + }) +} + +func (d *Dao) batch(bucket string, fn func(b *bolt.Bucket) error) (err error) { + return d.db.Batch(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(bucket)) + return fn(b) + }) +} + +func matchAny(s string, list ...string) bool { + s = strings.ToLower(s) + for _, v := range list { + if strings.Contains(strings.ToLower(v), s) { + return true + } + } + return false +} + +// itob returns an 8-byte big endian representation of v. +func itob(i uint64) []byte { + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, i) + return b +} diff --git a/dao/bolt/chart.go b/dao/bolt/chart.go new file mode 100644 index 0000000..75faa08 --- /dev/null +++ b/dao/bolt/chart.go @@ -0,0 +1,73 @@ +package bolt + +import ( + "github.com/cuigh/swirl/model" +) + +func (d *Dao) ChartList() (charts []*model.Chart, err error) { + err = d.each("chart", func(v Value) error { + chart := &model.Chart{} + err = v.Unmarshal(chart) + if err == nil { + charts = append(charts, chart) + } + return err + }) + return +} + +func (d *Dao) ChartCreate(chart *model.Chart) (err error) { + return d.update("chart", chart.Name, chart) +} + +func (d *Dao) ChartGet(name string) (chart *model.Chart, err error) { + var v Value + v, err = d.get("chart", name) + if err == nil { + if v != nil { + chart = &model.Chart{} + err = v.Unmarshal(chart) + } + } + return +} + +func (d *Dao) ChartBatch(names ...string) (charts []*model.Chart, err error) { + err = d.slice("chart", func(v Value) error { + chart := &model.Chart{} + err = v.Unmarshal(chart) + if err == nil { + charts = append(charts, chart) + } + return err + }, names...) + return +} + +func (d *Dao) ChartUpdate(chart *model.Chart) (err error) { + return d.update("chart", chart.Name, chart) +} + +func (d *Dao) ChartDelete(name string) (err error) { + return d.delete("chart", name) +} + +func (d *Dao) DashboardGet(name, key string) (dashboard *model.ChartDashboard, err error) { + dashboard = &model.ChartDashboard{ + Name: name, + Key: key, + } + + var v Value + v, err = d.get("dashboard", dashboard.ID()) + if err == nil { + if v != nil { + err = v.Unmarshal(dashboard) + } + } + return +} + +func (d *Dao) DashboardUpdate(dashboard *model.ChartDashboard) (err error) { + return d.update("dashboard", dashboard.ID(), dashboard) +} diff --git a/dao/bolt/event.go b/dao/bolt/event.go new file mode 100644 index 0000000..596447e --- /dev/null +++ b/dao/bolt/event.go @@ -0,0 +1,50 @@ +package bolt + +import ( + "sort" + + "github.com/cuigh/swirl/misc" + "github.com/cuigh/swirl/model" +) + +func (d *Dao) EventList(args *model.EventListArgs) (events []*model.Event, count int, err error) { + err = d.each("event", func(v Value) error { + event := &model.Event{} + err = v.Unmarshal(event) + if err != nil { + return err + } + + match := true + if args.Name != "" { + match = matchAny(args.Name, event.Name) + } + if match && args.Type != "" { + match = string(event.Type) == args.Type + } + + if match { + events = append(events, event) + } + return nil + }) + if err == nil { + count = len(events) + sort.Slice(events, func(i, j int) bool { + return events[i].Time.After(events[j].Time) + }) + start, end := misc.Page(count, args.PageIndex, args.PageSize) + events = events[start:end] + } + return +} + +func (d *Dao) EventCreate(event *model.Event) (err error) { + // TODO: + return d.update("event", event.ID, event) + //return nil + //d.do(func(db *database) { + // err = db.C("event").Insert(event) + //}) + //return +} diff --git a/dao/bolt/perm.go b/dao/bolt/perm.go new file mode 100644 index 0000000..9c29b9b --- /dev/null +++ b/dao/bolt/perm.go @@ -0,0 +1,28 @@ +package bolt + +import ( + "github.com/cuigh/swirl/model" +) + +func (d *Dao) PermGet(resType, resID string) (p *model.Perm, err error) { + key := resType + "." + resID + var v Value + v, err = d.get("perm", key) + if err == nil { + if v != nil { + p = &model.Perm{} + err = v.Unmarshal(p) + } + } + return +} + +func (d *Dao) PermUpdate(perm *model.Perm) (err error) { + key := perm.ResType + "." + perm.ResID + return d.update("perm", key, perm) +} + +func (d *Dao) PermDelete(resType, resID string) (err error) { + key := resType + "." + resID + return d.delete("perm", key) +} diff --git a/dao/bolt/registry.go b/dao/bolt/registry.go new file mode 100644 index 0000000..682121a --- /dev/null +++ b/dao/bolt/registry.go @@ -0,0 +1,72 @@ +package bolt + +import ( + "encoding/json" + "time" + + "github.com/boltdb/bolt" + "github.com/cuigh/auxo/errors" + "github.com/cuigh/swirl/model" +) + +func (d *Dao) RegistryCreate(registry *model.Registry) (err error) { + return d.update("registry", registry.ID, registry) +} + +func (d *Dao) RegistryUpdate(registry *model.Registry) (err error) { + return d.batch("registry", func(b *bolt.Bucket) error { + data := b.Get([]byte(registry.ID)) + if data == nil { + return errors.New("registry not found: " + registry.ID) + } + + r := &model.Registry{} + err = json.Unmarshal(data, r) + if err != nil { + return err + } + + r.Name = registry.Name + r.URL = registry.URL + r.Username = registry.Username + if registry.Password != "" { + r.Password = registry.Password + } + r.UpdatedAt = time.Now() + data, err = json.Marshal(r) + if err != nil { + return err + } + + return b.Put([]byte(registry.ID), data) + }) +} + +func (d *Dao) RegistryList() (registries []*model.Registry, err error) { + err = d.each("registry", func(v Value) error { + r := &model.Registry{} + err = v.Unmarshal(r) + if err != nil { + return err + } + registries = append(registries, r) + return nil + }) + return +} + +func (d *Dao) RegistryGet(id string) (registry *model.Registry, err error) { + var v Value + v, err = d.get("registry", id) + if err == nil { + if v != nil { + registry = &model.Registry{} + err = v.Unmarshal(registry) + } + } + return +} + +func (d *Dao) RegistryDelete(id string) (err error) { + return d.delete("registry", id) +} diff --git a/dao/bolt/role.go b/dao/bolt/role.go new file mode 100644 index 0000000..03bb63b --- /dev/null +++ b/dao/bolt/role.go @@ -0,0 +1,68 @@ +package bolt + +import ( + "encoding/json" + "time" + + "github.com/boltdb/bolt" + "github.com/cuigh/auxo/errors" + "github.com/cuigh/swirl/model" +) + +func (d *Dao) RoleList() (roles []*model.Role, err error) { + err = d.each("role", func(v Value) error { + role := &model.Role{} + err = v.Unmarshal(role) + if err == nil { + roles = append(roles, role) + } + return err + }) + return +} + +func (d *Dao) RoleCreate(role *model.Role) (err error) { + return d.update("role", role.ID, role) +} + +func (d *Dao) RoleGet(id string) (role *model.Role, err error) { + var v Value + v, err = d.get("role", id) + if err == nil { + if v != nil { + role = &model.Role{} + err = v.Unmarshal(role) + } + } + return +} + +func (d *Dao) RoleUpdate(role *model.Role) (err error) { + return d.batch("role", func(b *bolt.Bucket) error { + data := b.Get([]byte(role.ID)) + if data == nil { + return errors.New("role not found: " + role.ID) + } + + r := &model.Role{} + err = json.Unmarshal(data, r) + if err != nil { + return err + } + + r.Name = role.Name + r.Description = role.Description + r.Perms = role.Perms + r.UpdatedAt = time.Now() + data, err = json.Marshal(r) + if err != nil { + return err + } + + return b.Put([]byte(role.ID), data) + }) +} + +func (d *Dao) RoleDelete(id string) (err error) { + return d.delete("role", id) +} diff --git a/dao/bolt/setting.go b/dao/bolt/setting.go new file mode 100644 index 0000000..b279605 --- /dev/null +++ b/dao/bolt/setting.go @@ -0,0 +1,23 @@ +package bolt + +import ( + "github.com/cuigh/swirl/model" +) + +const settingID = "0" + +func (d *Dao) SettingGet() (setting *model.Setting, err error) { + var v Value + v, err = d.get("setting", settingID) + if err == nil { + setting = &model.Setting{} + if v != nil { + err = v.Unmarshal(setting) + } + } + return +} + +func (d *Dao) SettingUpdate(setting *model.Setting) (err error) { + return d.update("setting", settingID, setting) +} diff --git a/dao/bolt/stack.go b/dao/bolt/stack.go new file mode 100644 index 0000000..1f12373 --- /dev/null +++ b/dao/bolt/stack.go @@ -0,0 +1,141 @@ +package bolt + +import ( + "encoding/json" + "time" + + "github.com/boltdb/bolt" + "github.com/cuigh/auxo/errors" + "github.com/cuigh/swirl/model" +) + +//func (d *Dao) ArchiveList(args *model.ArchiveListArgs) (archives []*model.Archive, count int, err error) { +// d.do(func(db *database) { +// var query bson.M +// if args.Name != "" { +// query = bson.M{"name": args.Name} +// } +// q := db.C("archive").Find(query) +// +// count, err = q.Count() +// if err != nil { +// return +// } +// +// archives = []*model.Archive{} +// err = q.Skip(args.PageSize * (args.PageIndex - 1)).Limit(args.PageSize).All(&archives) +// }) +// return +//} +// +//func (d *Dao) ArchiveCreate(archive *model.Archive) (err error) { +// archive.ID = misc.NewID() +// archive.CreatedAt = time.Now() +// archive.UpdatedAt = archive.CreatedAt +// +// d.do(func(db *database) { +// err = db.C("archive").Insert(archive) +// }) +// return +//} +// +//func (d *Dao) ArchiveGet(id string) (archive *model.Archive, err error) { +// d.do(func(db *database) { +// archive = &model.Archive{} +// err = db.C("archive").FindId(id).One(archive) +// if err == mgo.ErrNotFound { +// archive, err = nil, nil +// } else if err != nil { +// archive = nil +// } +// }) +// return +//} +// +//func (d *Dao) ArchiveUpdate(archive *model.Archive) (err error) { +// d.do(func(db *database) { +// update := bson.M{ +// "$set": bson.M{ +// "name": archive.Name, +// "content": archive.Content, +// "updated_by": archive.UpdatedBy, +// "updated_at": time.Now(), +// }, +// } +// err = db.C("archive").UpdateId(archive.ID, update) +// }) +// return +//} +// +//func (d *Dao) ArchiveDelete(id string) (err error) { +// d.do(func(db *database) { +// err = db.C("archive").RemoveId(id) +// }) +// return +//} + +//=============================== + +func (d *Dao) StackList() (stacks []*model.Stack, err error) { + err = d.each("stack", func(v Value) error { + stack := &model.Stack{} + err = v.Unmarshal(stack) + if err == nil { + stacks = append(stacks, stack) + } + return err + }) + return +} + +func (d *Dao) StackCreate(stack *model.Stack) (err error) { + stack.CreatedAt = time.Now() + stack.UpdatedAt = stack.CreatedAt + return d.update("stack", stack.Name, stack) +} + +func (d *Dao) StackGet(name string) (stack *model.Stack, err error) { + var v Value + v, err = d.get("stack", name) + if err == nil { + if v != nil { + stack = &model.Stack{} + err = v.Unmarshal(stack) + } + } + return +} + +func (d *Dao) StackUpdate(stack *model.Stack) (err error) { + return d.batch("stack", func(b *bolt.Bucket) error { + data := b.Get([]byte(stack.Name)) + if data == nil { + return errors.New("stack not found: " + stack.Name) + } + + s := &model.Stack{} + err = json.Unmarshal(data, s) + if err != nil { + return err + } + + s.Content = stack.Content + s.UpdatedBy = stack.UpdatedBy + s.UpdatedAt = time.Now() + data, err = json.Marshal(s) + if err != nil { + return err + } + + return b.Put([]byte(stack.Name), data) + }) +} + +func (d *Dao) StackDelete(name string) (err error) { + return d.delete("stack", name) +} + +// StackMigrate migrates stacks from old archive collection. +func (d *Dao) StackMigrate() { + // bolt storage engine was implemented at version 0.7.8, so migration is not required. +} diff --git a/dao/bolt/template.go b/dao/bolt/template.go new file mode 100644 index 0000000..af4c173 --- /dev/null +++ b/dao/bolt/template.go @@ -0,0 +1,80 @@ +package bolt + +import ( + "encoding/json" + "time" + + "github.com/boltdb/bolt" + "github.com/cuigh/auxo/errors" + "github.com/cuigh/swirl/misc" + "github.com/cuigh/swirl/model" +) + +func (d *Dao) TemplateList(args *model.TemplateListArgs) (tpls []*model.Template, count int, err error) { + err = d.each("template", func(v Value) error { + t := &model.Template{} + err = v.Unmarshal(t) + if err != nil { + return err + } + + if matchAny(args.Name, t.Name) { + tpls = append(tpls, t) + } + return nil + }) + if err == nil { + count = len(tpls) + start, end := misc.Page(count, args.PageIndex, args.PageSize) + tpls = tpls[start:end] + } + return +} + +func (d *Dao) TemplateCreate(tpl *model.Template) (err error) { + tpl.CreatedAt = time.Now() + tpl.UpdatedAt = tpl.CreatedAt + return d.update("template", tpl.ID, tpl) +} + +func (d *Dao) TemplateGet(id string) (tpl *model.Template, err error) { + var v Value + v, err = d.get("template", id) + if err == nil { + if v != nil { + tpl = &model.Template{} + err = v.Unmarshal(tpl) + } + } + return +} + +func (d *Dao) TemplateUpdate(tpl *model.Template) (err error) { + return d.batch("template", func(b *bolt.Bucket) error { + data := b.Get([]byte(tpl.ID)) + if data == nil { + return errors.New("template not found: " + tpl.ID) + } + + t := &model.Template{} + err = json.Unmarshal(data, t) + if err != nil { + return err + } + + t.Name = tpl.Name + t.Content = tpl.Content + t.UpdatedBy = tpl.UpdatedBy + t.UpdatedAt = time.Now() + data, err = json.Marshal(t) + if err != nil { + return err + } + + return b.Put([]byte(tpl.ID), data) + }) +} + +func (d *Dao) TemplateDelete(id string) (err error) { + return d.delete("template", id) +} diff --git a/dao/bolt/user.go b/dao/bolt/user.go new file mode 100644 index 0000000..1c62bee --- /dev/null +++ b/dao/bolt/user.go @@ -0,0 +1,173 @@ +package bolt + +import ( + "encoding/json" + "time" + + "github.com/boltdb/bolt" + "github.com/cuigh/auxo/errors" + "github.com/cuigh/swirl/misc" + "github.com/cuigh/swirl/model" +) + +func (d *Dao) UserCount() (count int, err error) { + return d.count("user") +} + +func (d *Dao) UserCreate(user *model.User) (err error) { + return d.update("user", user.ID, user) +} + +func (d *Dao) UserUpdate(user *model.User) (err error) { + return d.userUpdate(user.ID, func(u *model.User) { + u.Name = user.Name + u.Email = user.Email + u.Admin = user.Admin + u.Type = user.Type + u.Roles = user.Roles + }) +} + +func (d *Dao) UserBlock(id string, blocked bool) (err error) { + return d.userUpdate(id, func(u *model.User) { + if blocked { + u.Status = model.UserStatusBlocked + } else { + u.Status = model.UserStatusActive + } + }) +} + +func (d *Dao) UserDelete(id string) (err error) { + return d.delete("user", id) +} + +func (d *Dao) UserList(args *model.UserListArgs) (users []*model.User, count int, err error) { + err = d.each("user", func(v Value) error { + user := &model.User{} + err = v.Unmarshal(user) + if err != nil { + return err + } + + match := true + if args.Query != "" { + match = matchAny(args.Query, user.LoginName, user.Name, user.Email) + } + if match { + switch args.Filter { + case "admins": + match = user.Admin + case "active": + match = user.Status == model.UserStatusActive + case "blocked": + match = user.Status == model.UserStatusBlocked + } + } + + if match { + users = append(users, user) + } + return nil + }) + if err == nil { + count = len(users) + start, end := misc.Page(count, args.PageIndex, args.PageSize) + users = users[start:end] + } + return +} + +func (d *Dao) UserGetByID(id string) (user *model.User, err error) { + var v Value + v, err = d.get("user", id) + if err == nil { + if v != nil { + user = &model.User{} + err = v.Unmarshal(user) + } + } + return +} + +func (d *Dao) UserGetByName(loginName string) (user *model.User, err error) { + err = d.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte("user")) + c := b.Cursor() + for k, v := c.First(); k != nil; k, v = c.Next() { + u := &model.User{} + err = json.Unmarshal(v, u) + if err != nil { + return err + } + if u.LoginName == loginName { + user = u + return nil + } + } + return nil + }) + return +} + +func (d *Dao) ProfileUpdateInfo(user *model.User) (err error) { + return d.userUpdate(user.ID, func(u *model.User) { + u.Name = user.Name + u.Email = user.Email + }) +} + +func (d *Dao) ProfileUpdatePassword(id, pwd, salt string) (err error) { + return d.userUpdate(id, func(u *model.User) { + u.Password = pwd + u.Salt = salt + }) +} + +func (d *Dao) userUpdate(id string, decorator func(u *model.User)) (err error) { + return d.batch("user", func(b *bolt.Bucket) error { + data := b.Get([]byte(id)) + if data == nil { + return errors.New("user not found: " + id) + } + + u := &model.User{} + err = json.Unmarshal(data, u) + if err != nil { + return err + } + + decorator(u) + u.UpdatedAt = time.Now() + data, err = json.Marshal(u) + if err != nil { + return err + } + + return b.Put([]byte(id), data) + }) +} + +func (d *Dao) SessionGet(token string) (session *model.Session, err error) { + err = d.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte("session")) + c := b.Cursor() + for k, v := c.First(); k != nil; k, v = c.Next() { + s := &model.Session{} + err = json.Unmarshal(v, s) + if err != nil { + return err + } + if s.Token == token { + session = s + return nil + } + } + return nil + }) + return +} + +func (d *Dao) SessionUpdate(session *model.Session) (err error) { + return d.update("session", session.UserID, session) +} diff --git a/dao/dao.go b/dao/dao.go index aa0f10b..c48d2b7 100644 --- a/dao/dao.go +++ b/dao/dao.go @@ -3,6 +3,7 @@ package dao import ( "github.com/cuigh/auxo/errors" "github.com/cuigh/auxo/util/lazy" + "github.com/cuigh/swirl/dao/bolt" "github.com/cuigh/swirl/dao/mongo" "github.com/cuigh/swirl/misc" "github.com/cuigh/swirl/model" @@ -14,6 +15,9 @@ var ( // Interface is the interface that wraps all dao methods. type Interface interface { + Init() + Close() + RoleGet(id string) (*model.Role, error) RoleList() (roles []*model.Role, err error) RoleCreate(role *model.Role) error @@ -41,18 +45,12 @@ type Interface interface { RegistryList() (registries []*model.Registry, err error) RegistryDelete(id string) error - ArchiveList(args *model.ArchiveListArgs) (archives []*model.Archive, count int, err error) - ArchiveGet(id string) (*model.Archive, error) - ArchiveCreate(archive *model.Archive) error - ArchiveUpdate(archive *model.Archive) error - ArchiveDelete(id string) error - StackList() (stacks []*model.Stack, err error) StackGet(name string) (*model.Stack, error) StackCreate(stack *model.Stack) error StackUpdate(stack *model.Stack) error StackDelete(name string) error - // StackMigrate migrates stacks from old archive collection. This method will removed after v0.8. + // StackMigrate migrates stacks from old archive collection. This method will be removed after v0.8. StackMigrate() TemplateList(args *model.TemplateListArgs) (tpls []*model.Template, count int, err error) @@ -92,11 +90,18 @@ func Get() (Interface, error) { } func create() (d interface{}, err error) { + var i Interface switch misc.Options.DBType { case "", "mongo": - return mongo.New(misc.Options.DBAddress) + i, err = mongo.New(misc.Options.DBAddress) + case "bolt": + i, err = bolt.New(misc.Options.DBAddress) default: err = errors.New("Unknown database type: " + misc.Options.DBType) } - return + + if err == nil { + i.Init() + } + return i, err } diff --git a/dao/mongo/mongo.go b/dao/mongo/mongo.go index 4468714..e5e9139 100644 --- a/dao/mongo/mongo.go +++ b/dao/mongo/mongo.go @@ -72,11 +72,10 @@ func New(addr string) (*Dao, error) { session: s, logger: log.Get("mongo"), } - d.createIndexes() return d, nil } -func (d *Dao) createIndexes() { +func (d *Dao) Init() { db := d.db() defer db.Close() @@ -91,6 +90,10 @@ func (d *Dao) createIndexes() { } } +func (d *Dao) Close() { + d.session.Close() +} + func (d *Dao) db() *database { return &database{ db: d.session.Copy().DB(""), diff --git a/dao/mongo/stack.go b/dao/mongo/stack.go index 8e791ae..2e87893 100644 --- a/dao/mongo/stack.go +++ b/dao/mongo/stack.go @@ -5,79 +5,11 @@ import ( "github.com/cuigh/auxo/app" "github.com/cuigh/auxo/log" - "github.com/cuigh/swirl/misc" "github.com/cuigh/swirl/model" "github.com/globalsign/mgo" "github.com/globalsign/mgo/bson" ) -func (d *Dao) ArchiveList(args *model.ArchiveListArgs) (archives []*model.Archive, count int, err error) { - d.do(func(db *database) { - var query bson.M - if args.Name != "" { - query = bson.M{"name": args.Name} - } - q := db.C("archive").Find(query) - - count, err = q.Count() - if err != nil { - return - } - - archives = []*model.Archive{} - err = q.Skip(args.PageSize * (args.PageIndex - 1)).Limit(args.PageSize).All(&archives) - }) - return -} - -func (d *Dao) ArchiveCreate(archive *model.Archive) (err error) { - archive.ID = misc.NewID() - archive.CreatedAt = time.Now() - archive.UpdatedAt = archive.CreatedAt - - d.do(func(db *database) { - err = db.C("archive").Insert(archive) - }) - return -} - -func (d *Dao) ArchiveGet(id string) (archive *model.Archive, err error) { - d.do(func(db *database) { - archive = &model.Archive{} - err = db.C("archive").FindId(id).One(archive) - if err == mgo.ErrNotFound { - archive, err = nil, nil - } else if err != nil { - archive = nil - } - }) - return -} - -func (d *Dao) ArchiveUpdate(archive *model.Archive) (err error) { - d.do(func(db *database) { - update := bson.M{ - "$set": bson.M{ - "name": archive.Name, - "content": archive.Content, - "updated_by": archive.UpdatedBy, - "updated_at": time.Now(), - }, - } - err = db.C("archive").UpdateId(archive.ID, update) - }) - return -} - -func (d *Dao) ArchiveDelete(id string) (err error) { - d.do(func(db *database) { - err = db.C("archive").RemoveId(id) - }) - return -} - -//=============================== - func (d *Dao) StackList() (stacks []*model.Stack, err error) { d.do(func(db *database) { stacks = []*model.Stack{} diff --git a/main.go b/main.go index 2a2431b..85f590d 100644 --- a/main.go +++ b/main.go @@ -33,19 +33,20 @@ func main() { app.Action = func(ctx *app.Context) { err := config.UnmarshalOption("swirl", &misc.Options) if err != nil { - log.Get(app.Name).Error("Load options failed: ", err) + log.Get(app.Name).Error("Failed to load options: ", err) os.Exit(1) } setting, err := biz.Setting.Get() if err != nil { - log.Get(app.Name).Error("Load setting failed: ", err) + log.Get(app.Name).Error("Failed to load settings: ", err) os.Exit(1) } biz.Stack.Migrate() - - scaler.Start() + if setting.Metrics.Prometheus != "" { + scaler.Start() + } app.Run(server(setting)) } app.Flags.Register(flag.All)