store.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package membership
  2. /*
  3. ArozOS Cluster - persistence
  4. All cluster state lives in its own key-value database file (cluster.db,
  5. separate from the main ao.db) so it can be wiped or moved independently.
  6. Tables:
  7. cluster "info" -> ClusterInfo
  8. nodes <nodeID> -> NodeRecord
  9. jointokens <tokenID> -> JoinToken (with secret hash)
  10. config "local" -> LocalConfig
  11. */
  12. import (
  13. "encoding/json"
  14. "sync"
  15. "imuslab.com/arozos/mod/database"
  16. )
  17. const (
  18. tableCluster = "cluster"
  19. tableNodes = "nodes"
  20. tableJoinTokens = "jointokens"
  21. tableConfig = "config"
  22. // TableIdentity is the cluster.db table used by the identity service for
  23. // its cluster-scoped records; it is wiped together with the membership.
  24. TableIdentity = "identity"
  25. )
  26. var (
  27. extraTablesMu sync.Mutex
  28. extraClusterTables = []string{}
  29. currentStore *store
  30. )
  31. // RegisterClusterTable declares a cluster.db table owned by a sibling cluster
  32. // service. It is created when the store opens (or right away when a store is
  33. // already open) and dropped together with the membership when the node
  34. // leaves the cluster.
  35. func RegisterClusterTable(name string) {
  36. extraTablesMu.Lock()
  37. defer extraTablesMu.Unlock()
  38. for _, t := range extraClusterTables {
  39. if t == name {
  40. return
  41. }
  42. }
  43. extraClusterTables = append(extraClusterTables, name)
  44. if currentStore != nil {
  45. currentStore.db.NewTable(name)
  46. }
  47. }
  48. func registeredClusterTables() []string {
  49. extraTablesMu.Lock()
  50. defer extraTablesMu.Unlock()
  51. return append([]string{}, extraClusterTables...)
  52. }
  53. // store wraps the key-value database with typed accessors.
  54. type store struct {
  55. db *database.Database
  56. }
  57. // storedJoinToken is the on-disk form, which unlike the API form keeps the hash.
  58. type storedJoinToken struct {
  59. ID string `json:"id"`
  60. SecretHash string `json:"secretHash"`
  61. Created int64 `json:"created"`
  62. Expires int64 `json:"expires"`
  63. Uses int `json:"uses"`
  64. }
  65. func newStore(dbfile string) (*store, error) {
  66. db, err := database.NewDatabase(dbfile, false)
  67. if err != nil {
  68. return nil, err
  69. }
  70. tables := append([]string{tableCluster, tableNodes, tableJoinTokens, tableConfig, TableIdentity}, registeredClusterTables()...)
  71. for _, table := range tables {
  72. if err := db.NewTable(table); err != nil {
  73. db.Close()
  74. return nil, err
  75. }
  76. }
  77. st := &store{db: db}
  78. extraTablesMu.Lock()
  79. currentStore = st
  80. extraTablesMu.Unlock()
  81. return st, nil
  82. }
  83. func (s *store) close() {
  84. extraTablesMu.Lock()
  85. if currentStore == s {
  86. currentStore = nil
  87. }
  88. extraTablesMu.Unlock()
  89. s.db.Close()
  90. }
  91. func (s *store) loadCluster() *ClusterInfo {
  92. if !s.db.KeyExists(tableCluster, "info") {
  93. return nil
  94. }
  95. var info ClusterInfo
  96. if err := s.db.Read(tableCluster, "info", &info); err != nil || info.ID == "" {
  97. return nil
  98. }
  99. return &info
  100. }
  101. func (s *store) saveCluster(info *ClusterInfo) error {
  102. return s.db.Write(tableCluster, "info", info)
  103. }
  104. func (s *store) loadConfig() LocalConfig {
  105. var cfg LocalConfig
  106. if s.db.KeyExists(tableConfig, "local") {
  107. s.db.Read(tableConfig, "local", &cfg)
  108. }
  109. return cfg
  110. }
  111. func (s *store) saveConfig(cfg LocalConfig) error {
  112. return s.db.Write(tableConfig, "local", cfg)
  113. }
  114. func (s *store) loadNodes() map[string]*NodeRecord {
  115. nodes := map[string]*NodeRecord{}
  116. entries, err := s.db.ListTable(tableNodes)
  117. if err != nil {
  118. return nodes
  119. }
  120. for _, kv := range entries {
  121. var rec NodeRecord
  122. if err := json.Unmarshal(kv[1], &rec); err != nil || rec.ID == "" {
  123. continue
  124. }
  125. nodes[rec.ID] = &rec
  126. }
  127. return nodes
  128. }
  129. func (s *store) saveNode(rec *NodeRecord) error {
  130. return s.db.Write(tableNodes, rec.ID, rec)
  131. }
  132. func (s *store) deleteNode(id string) error {
  133. return s.db.Delete(tableNodes, id)
  134. }
  135. func (s *store) loadJoinTokens() map[string]*JoinToken {
  136. tokens := map[string]*JoinToken{}
  137. entries, err := s.db.ListTable(tableJoinTokens)
  138. if err != nil {
  139. return tokens
  140. }
  141. for _, kv := range entries {
  142. var st storedJoinToken
  143. if err := json.Unmarshal(kv[1], &st); err != nil || st.ID == "" {
  144. continue
  145. }
  146. tokens[st.ID] = &JoinToken{ID: st.ID, SecretHash: st.SecretHash, Created: st.Created, Expires: st.Expires, Uses: st.Uses}
  147. }
  148. return tokens
  149. }
  150. func (s *store) saveJoinToken(t *JoinToken) error {
  151. return s.db.Write(tableJoinTokens, t.ID, storedJoinToken{ID: t.ID, SecretHash: t.SecretHash, Created: t.Created, Expires: t.Expires, Uses: t.Uses})
  152. }
  153. func (s *store) deleteJoinToken(id string) error {
  154. return s.db.Delete(tableJoinTokens, id)
  155. }
  156. // wipeCluster removes every cluster-scoped record but keeps the local config.
  157. func (s *store) wipeCluster() error {
  158. tables := append([]string{tableCluster, tableNodes, tableJoinTokens, TableIdentity}, registeredClusterTables()...)
  159. for _, table := range tables {
  160. if err := s.db.DropTable(table); err != nil {
  161. return err
  162. }
  163. if err := s.db.NewTable(table); err != nil {
  164. return err
  165. }
  166. }
  167. return nil
  168. }