2015-12-23 15:48:25 +00:00
|
|
|
package meta
|
|
|
|
|
|
|
|
import (
|
2015-12-30 13:15:00 +00:00
|
|
|
"bytes"
|
2016-01-04 22:37:20 +00:00
|
|
|
crand "crypto/rand"
|
|
|
|
"crypto/sha256"
|
2016-01-06 15:11:59 +00:00
|
|
|
"errors"
|
2015-12-30 13:15:00 +00:00
|
|
|
"fmt"
|
2016-01-04 22:37:20 +00:00
|
|
|
"io"
|
2015-12-30 13:15:00 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
2016-01-07 22:52:53 +00:00
|
|
|
"math/rand"
|
2015-12-30 13:15:00 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
2016-03-08 19:59:33 +00:00
|
|
|
"path/filepath"
|
2016-02-04 15:12:52 +00:00
|
|
|
"sort"
|
2015-12-30 13:15:00 +00:00
|
|
|
"sync"
|
2015-12-23 15:48:25 +00:00
|
|
|
"time"
|
|
|
|
|
2016-02-10 17:26:18 +00:00
|
|
|
"github.com/influxdata/influxdb"
|
|
|
|
"github.com/influxdata/influxdb/influxql"
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-01-06 22:34:34 +00:00
|
|
|
"golang.org/x/crypto/bcrypt"
|
2015-12-23 15:48:25 +00:00
|
|
|
)
|
|
|
|
|
2015-12-30 22:52:39 +00:00
|
|
|
const (
|
2016-01-04 22:37:20 +00:00
|
|
|
// SaltBytes is the number of bytes used for salts
|
|
|
|
SaltBytes = 32
|
2016-03-08 19:59:33 +00:00
|
|
|
|
|
|
|
metaFile = "meta.db"
|
2015-12-30 22:52:39 +00:00
|
|
|
)
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-01-11 01:43:05 +00:00
|
|
|
var (
|
|
|
|
// ErrServiceUnavailable is returned when the meta service is unavailable.
|
|
|
|
ErrServiceUnavailable = errors.New("meta service unavailable")
|
2016-01-19 20:49:32 +00:00
|
|
|
|
|
|
|
// ErrService is returned when the meta service returns an error.
|
|
|
|
ErrService = errors.New("meta service error")
|
2016-01-11 01:43:05 +00:00
|
|
|
)
|
|
|
|
|
2015-12-31 23:37:27 +00:00
|
|
|
// Client is used to execute commands on and read data from
|
|
|
|
// a meta service cluster.
|
2015-12-23 15:48:25 +00:00
|
|
|
type Client struct {
|
2015-12-30 13:15:00 +00:00
|
|
|
logger *log.Logger
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
mu sync.RWMutex
|
|
|
|
closing chan struct{}
|
|
|
|
changed chan struct{}
|
|
|
|
cacheData *Data
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-01-04 22:37:20 +00:00
|
|
|
// Authentication cache.
|
|
|
|
authCache map[string]authUser
|
2016-03-08 19:59:33 +00:00
|
|
|
|
|
|
|
path string
|
|
|
|
|
2016-07-26 19:39:08 +00:00
|
|
|
retentionAutoCreate bool
|
|
|
|
defaultRetentionPolicyName string
|
2016-02-11 22:50:26 +00:00
|
|
|
}
|
2016-01-04 22:37:20 +00:00
|
|
|
|
2016-02-11 22:50:26 +00:00
|
|
|
type authUser struct {
|
|
|
|
bhash string
|
|
|
|
salt []byte
|
|
|
|
hash []byte
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2015-12-31 23:37:27 +00:00
|
|
|
// NewClient returns a new *Client.
|
2016-03-08 19:59:33 +00:00
|
|
|
func NewClient(config *Config) *Client {
|
2016-02-12 22:10:02 +00:00
|
|
|
return &Client{
|
2016-03-08 19:59:33 +00:00
|
|
|
cacheData: &Data{
|
2016-06-23 06:52:58 +00:00
|
|
|
ClusterID: uint64(rand.Int63()),
|
2016-03-08 19:59:33 +00:00
|
|
|
Index: 1,
|
|
|
|
},
|
2016-07-26 19:39:08 +00:00
|
|
|
closing: make(chan struct{}),
|
|
|
|
changed: make(chan struct{}),
|
|
|
|
logger: log.New(ioutil.Discard, "[metaclient] ", log.LstdFlags),
|
|
|
|
authCache: make(map[string]authUser, 0),
|
|
|
|
path: config.Dir,
|
|
|
|
retentionAutoCreate: config.RetentionAutoCreate,
|
|
|
|
defaultRetentionPolicyName: config.DefaultRetentionPolicyName,
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-31 23:37:27 +00:00
|
|
|
// Open a connection to a meta service cluster.
|
2015-12-30 13:15:00 +00:00
|
|
|
func (c *Client) Open() error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2016-03-30 16:39:57 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
// Try to load from disk
|
|
|
|
if err := c.Load(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
// If this is a brand new instance, persist to disk immediatly.
|
|
|
|
if c.cacheData.Index == 1 {
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := snapshot(c.path, c.cacheData); err != nil {
|
2016-03-08 19:59:33 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2015-12-30 13:15:00 +00:00
|
|
|
|
|
|
|
return nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2015-12-31 23:37:27 +00:00
|
|
|
// Close the meta service cluster connection.
|
2015-12-30 13:15:00 +00:00
|
|
|
func (c *Client) Close() error {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
2016-01-20 03:33:07 +00:00
|
|
|
if t, ok := http.DefaultTransport.(*http.Transport); ok {
|
|
|
|
t.CloseIdleConnections()
|
|
|
|
}
|
|
|
|
|
2016-01-02 20:12:54 +00:00
|
|
|
select {
|
|
|
|
case <-c.closing:
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
close(c.closing)
|
|
|
|
}
|
2015-12-30 13:15:00 +00:00
|
|
|
|
|
|
|
return nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2016-01-09 04:29:48 +00:00
|
|
|
// AcquireLease attempts to acquire the specified lease.
|
2016-03-08 19:59:33 +00:00
|
|
|
// TODO corylanou remove this for single node
|
|
|
|
func (c *Client) AcquireLease(name string) (*Lease, error) {
|
|
|
|
l := Lease{
|
|
|
|
Name: name,
|
|
|
|
Expiration: time.Now().Add(DefaultLeaseDuration),
|
2016-01-09 04:29:48 +00:00
|
|
|
}
|
2016-03-08 19:59:33 +00:00
|
|
|
return &l, nil
|
2016-01-09 04:29:48 +00:00
|
|
|
}
|
|
|
|
|
2016-01-08 15:27:01 +00:00
|
|
|
func (c *Client) data() *Data {
|
2016-01-08 13:00:24 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
2016-03-08 19:59:33 +00:00
|
|
|
data := c.cacheData.Clone()
|
|
|
|
return data
|
2016-01-08 13:00:24 +00:00
|
|
|
}
|
|
|
|
|
2015-12-31 23:37:27 +00:00
|
|
|
// ClusterID returns the ID of the cluster it's connected to.
|
2016-01-07 22:52:53 +00:00
|
|
|
func (c *Client) ClusterID() uint64 {
|
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
|
2016-01-08 19:25:07 +00:00
|
|
|
return c.cacheData.ClusterID
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 21:17:52 +00:00
|
|
|
// Database returns info for the requested database.
|
2016-04-30 22:04:38 +00:00
|
|
|
func (c *Client) Database(name string) *DatabaseInfo {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
c.mu.RUnlock()
|
|
|
|
|
|
|
|
for _, d := range data.Databases {
|
2015-12-30 13:15:00 +00:00
|
|
|
if d.Name == name {
|
2016-04-30 22:04:38 +00:00
|
|
|
return &d
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-30 22:04:38 +00:00
|
|
|
return nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 21:17:52 +00:00
|
|
|
// Databases returns a list of all database infos.
|
2016-04-30 22:04:38 +00:00
|
|
|
func (c *Client) Databases() []DatabaseInfo {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
c.mu.RUnlock()
|
|
|
|
|
|
|
|
dbs := data.Databases
|
2016-01-08 15:27:01 +00:00
|
|
|
if dbs == nil {
|
2016-04-30 22:04:38 +00:00
|
|
|
return []DatabaseInfo{}
|
2015-12-30 21:17:52 +00:00
|
|
|
}
|
2016-04-30 22:04:38 +00:00
|
|
|
return dbs
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2016-01-06 22:34:34 +00:00
|
|
|
// CreateDatabase creates a database or returns it if it already exists
|
2015-12-31 23:37:27 +00:00
|
|
|
func (c *Client) CreateDatabase(name string) (*DatabaseInfo, error) {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
|
|
|
if db := data.Database(name); db != nil {
|
2016-01-06 22:34:34 +00:00
|
|
|
return db, nil
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.CreateDatabase(name); err != nil {
|
|
|
|
return nil, err
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
// create default retention policy
|
|
|
|
if c.retentionAutoCreate {
|
|
|
|
if err := data.CreateRetentionPolicy(name, &RetentionPolicyInfo{
|
2016-07-26 19:39:08 +00:00
|
|
|
Name: c.defaultRetentionPolicyName,
|
2016-03-08 19:59:33 +00:00
|
|
|
ReplicaN: 1,
|
|
|
|
}); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-07-26 19:39:08 +00:00
|
|
|
if err := data.SetDefaultRetentionPolicy(name, c.defaultRetentionPolicyName); err != nil {
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
db := data.Database(name)
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return db, nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 21:17:52 +00:00
|
|
|
// CreateDatabaseWithRetentionPolicy creates a database with the specified retention policy.
|
2015-12-31 23:37:27 +00:00
|
|
|
func (c *Client) CreateDatabaseWithRetentionPolicy(name string, rpi *RetentionPolicyInfo) (*DatabaseInfo, error) {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
2015-12-30 21:17:52 +00:00
|
|
|
if rpi.Duration < MinRetentionPolicyDuration && rpi.Duration != 0 {
|
|
|
|
return nil, ErrRetentionPolicyDurationTooLow
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if db := data.Database(name); db != nil {
|
2016-02-16 17:03:43 +00:00
|
|
|
// Check if the retention policy already exists. If it does and matches
|
|
|
|
// the desired retention policy, exit with no error.
|
|
|
|
if rp := db.RetentionPolicy(rpi.Name); rp != nil {
|
2016-04-04 17:17:40 +00:00
|
|
|
// Normalise ShardDuration before comparing to any existing retention policies.
|
|
|
|
rpi.ShardGroupDuration = normalisedShardDuration(rpi.ShardGroupDuration, rpi.Duration)
|
2016-03-31 16:59:11 +00:00
|
|
|
if rp.ReplicaN != rpi.ReplicaN || rp.Duration != rpi.Duration || rp.ShardGroupDuration != rpi.ShardGroupDuration {
|
2016-02-16 17:03:43 +00:00
|
|
|
return nil, ErrRetentionPolicyConflict
|
|
|
|
}
|
|
|
|
return db, nil
|
|
|
|
}
|
2016-01-06 22:34:34 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.CreateDatabase(name); err != nil {
|
|
|
|
return nil, err
|
2015-12-30 21:17:52 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.CreateRetentionPolicy(name, rpi); err != nil {
|
2016-01-06 22:34:34 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.SetDefaultRetentionPolicy(name, rpi.Name); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
db := data.Database(name)
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return db, nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 21:17:52 +00:00
|
|
|
// DropDatabase deletes a database.
|
2015-12-30 13:15:00 +00:00
|
|
|
func (c *Client) DropDatabase(name string) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
|
|
|
if err := data.DropDatabase(name); err != nil {
|
|
|
|
return err
|
2015-12-30 21:17:52 +00:00
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 21:17:52 +00:00
|
|
|
// CreateRetentionPolicy creates a retention policy on the specified database.
|
2015-12-31 23:37:27 +00:00
|
|
|
func (c *Client) CreateRetentionPolicy(database string, rpi *RetentionPolicyInfo) (*RetentionPolicyInfo, error) {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
2015-12-30 21:17:52 +00:00
|
|
|
if rpi.Duration < MinRetentionPolicyDuration && rpi.Duration != 0 {
|
|
|
|
return nil, ErrRetentionPolicyDurationTooLow
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.CreateRetentionPolicy(database, rpi); err != nil {
|
|
|
|
return nil, err
|
2015-12-30 21:17:52 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
rp, err := data.RetentionPolicy(database, rpi.Name)
|
|
|
|
if err != nil {
|
2015-12-30 21:17:52 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return rp, nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2016-07-26 19:39:08 +00:00
|
|
|
// RetentionAutoCreateName returns the name used for auto generated retention policies.
|
|
|
|
func (c *Client) RetentionAutoCreateName() string {
|
|
|
|
return c.defaultRetentionPolicyName
|
|
|
|
}
|
|
|
|
|
2015-12-30 21:17:52 +00:00
|
|
|
// RetentionPolicy returns the requested retention policy info.
|
|
|
|
func (c *Client) RetentionPolicy(database, name string) (rpi *RetentionPolicyInfo, err error) {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
c.mu.RUnlock()
|
2015-12-30 21:17:52 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
db := data.Database(database)
|
2016-02-02 18:08:44 +00:00
|
|
|
if db == nil {
|
2016-02-15 13:00:58 +00:00
|
|
|
return nil, influxdb.ErrDatabaseNotFound(database)
|
2016-02-02 18:08:44 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 21:17:52 +00:00
|
|
|
return db.RetentionPolicy(name), nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 21:17:52 +00:00
|
|
|
// DropRetentionPolicy drops a retention policy from a database.
|
2015-12-31 23:37:27 +00:00
|
|
|
func (c *Client) DropRetentionPolicy(database, name string) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
|
|
|
if err := data.DropRetentionPolicy(database, name); err != nil {
|
|
|
|
return err
|
2015-12-30 21:17:52 +00:00
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2015-12-31 23:37:27 +00:00
|
|
|
// SetDefaultRetentionPolicy sets a database's default retention policy.
|
2015-12-23 15:48:25 +00:00
|
|
|
func (c *Client) SetDefaultRetentionPolicy(database, name string) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
|
|
|
if err := data.SetDefaultRetentionPolicy(database, name); err != nil {
|
|
|
|
return err
|
2016-01-01 04:57:46 +00:00
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2015-12-31 23:37:27 +00:00
|
|
|
// UpdateRetentionPolicy updates a retention policy.
|
2015-12-30 13:15:00 +00:00
|
|
|
func (c *Client) UpdateRetentionPolicy(database, name string, rpu *RetentionPolicyUpdate) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2016-01-08 13:00:24 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
data := c.cacheData.Clone()
|
2016-01-08 13:00:24 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.UpdateRetentionPolicy(database, name, rpu); err != nil {
|
|
|
|
return err
|
2016-01-08 13:00:24 +00:00
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2016-01-04 22:37:20 +00:00
|
|
|
func (c *Client) Users() []UserInfo {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
c.mu.RUnlock()
|
|
|
|
|
|
|
|
users := data.Users
|
2016-01-04 22:37:20 +00:00
|
|
|
|
2016-01-08 15:27:01 +00:00
|
|
|
if users == nil {
|
2016-01-04 22:37:20 +00:00
|
|
|
return []UserInfo{}
|
|
|
|
}
|
2016-01-08 15:27:01 +00:00
|
|
|
return users
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) User(name string) (*UserInfo, error) {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
c.mu.RUnlock()
|
|
|
|
|
|
|
|
for _, u := range data.Users {
|
2016-01-04 22:37:20 +00:00
|
|
|
if u.Name == name {
|
|
|
|
return &u, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, ErrUserNotFound
|
|
|
|
}
|
|
|
|
|
2016-02-11 22:50:26 +00:00
|
|
|
// bcryptCost is the cost associated with generating password with bcrypt.
|
2016-01-04 22:37:20 +00:00
|
|
|
// This setting is lowered during testing to improve test suite performance.
|
2016-02-11 22:50:26 +00:00
|
|
|
var bcryptCost = bcrypt.DefaultCost
|
2016-01-04 22:37:20 +00:00
|
|
|
|
|
|
|
// hashWithSalt returns a salted hash of password using salt
|
2016-02-11 22:50:26 +00:00
|
|
|
func (c *Client) hashWithSalt(salt []byte, password string) []byte {
|
2016-01-04 22:37:20 +00:00
|
|
|
hasher := sha256.New()
|
2016-02-11 22:50:26 +00:00
|
|
|
hasher.Write(salt)
|
|
|
|
hasher.Write([]byte(password))
|
|
|
|
return hasher.Sum(nil)
|
2016-01-04 22:37:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// saltedHash returns a salt and salted hash of password
|
|
|
|
func (c *Client) saltedHash(password string) (salt, hash []byte, err error) {
|
|
|
|
salt = make([]byte, SaltBytes)
|
2016-02-11 22:50:26 +00:00
|
|
|
if _, err := io.ReadFull(crand.Reader, salt); err != nil {
|
|
|
|
return nil, nil, err
|
2016-01-04 22:37:20 +00:00
|
|
|
}
|
|
|
|
|
2016-02-11 22:50:26 +00:00
|
|
|
return salt, c.hashWithSalt(salt, password), nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 13:15:00 +00:00
|
|
|
func (c *Client) CreateUser(name, password string, admin bool) (*UserInfo, error) {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
2016-03-14 22:40:25 +00:00
|
|
|
// See if the user already exists.
|
|
|
|
if u := data.User(name); u != nil {
|
|
|
|
if err := bcrypt.CompareHashAndPassword([]byte(u.Hash), []byte(password)); err != nil || u.Admin != admin {
|
|
|
|
return nil, ErrUserExists
|
|
|
|
}
|
|
|
|
return u, nil
|
|
|
|
}
|
|
|
|
|
2016-01-04 22:37:20 +00:00
|
|
|
// Hash the password before serializing it.
|
2016-02-11 22:50:26 +00:00
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
|
2016-01-04 22:37:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.CreateUser(name, string(hash), admin); err != nil {
|
2016-01-04 22:37:20 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2016-03-08 19:59:33 +00:00
|
|
|
|
|
|
|
u := data.User(name)
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return u, nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) UpdateUser(name, password string) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
2016-01-04 22:37:20 +00:00
|
|
|
// Hash the password before serializing it.
|
2016-02-11 22:50:26 +00:00
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
|
2016-01-04 22:37:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.UpdateUser(name, string(hash)); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(c.authCache, name)
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) DropUser(name string) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
|
|
|
if err := data.DropUser(name); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) SetPrivilege(username, database string, p influxql.Privilege) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
|
|
|
if err := data.SetPrivilege(username, database, p); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) SetAdminPrivilege(username string, admin bool) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
|
|
|
if err := data.SetAdminPrivilege(username, admin); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) UserPrivileges(username string) (map[string]influxql.Privilege, error) {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
c.mu.RUnlock()
|
|
|
|
|
|
|
|
p, err := data.UserPrivileges(username)
|
2016-01-04 22:37:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return p, nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) UserPrivilege(username, database string) (*influxql.Privilege, error) {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
c.mu.RUnlock()
|
|
|
|
|
|
|
|
p, err := data.UserPrivilege(username, database)
|
2016-01-04 22:37:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return p, nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2016-01-04 22:37:20 +00:00
|
|
|
func (c *Client) AdminUserExists() bool {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
c.mu.RUnlock()
|
|
|
|
|
|
|
|
for _, u := range data.Users {
|
2016-01-04 22:37:20 +00:00
|
|
|
if u.Admin {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) Authenticate(username, password string) (*UserInfo, error) {
|
2016-01-04 22:37:20 +00:00
|
|
|
// Find user.
|
2016-06-02 05:17:31 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
userInfo := c.cacheData.User(username)
|
|
|
|
c.mu.RUnlock()
|
2016-02-11 22:50:26 +00:00
|
|
|
if userInfo == nil {
|
2016-01-04 22:37:20 +00:00
|
|
|
return nil, ErrUserNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the local auth cache first.
|
2016-06-02 05:17:31 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
au, ok := c.authCache[username]
|
|
|
|
c.mu.RUnlock()
|
|
|
|
if ok {
|
2016-01-04 22:37:20 +00:00
|
|
|
// verify the password using the cached salt and hash
|
2016-02-11 22:50:26 +00:00
|
|
|
if bytes.Equal(c.hashWithSalt(au.salt, password), au.hash) {
|
|
|
|
return userInfo, nil
|
2016-01-04 22:37:20 +00:00
|
|
|
}
|
|
|
|
|
2016-02-11 22:50:26 +00:00
|
|
|
// fall through to requiring a full bcrypt hash for invalid passwords
|
2016-01-04 22:37:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Compare password with user hash.
|
2016-02-11 22:50:26 +00:00
|
|
|
if err := bcrypt.CompareHashAndPassword([]byte(userInfo.Hash), []byte(password)); err != nil {
|
2016-01-04 22:37:20 +00:00
|
|
|
return nil, ErrAuthenticate
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate a salt and hash of the password for the cache
|
|
|
|
salt, hashed, err := c.saltedHash(password)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-06-02 05:17:31 +00:00
|
|
|
c.mu.Lock()
|
2016-02-11 22:50:26 +00:00
|
|
|
c.authCache[username] = authUser{salt: salt, hash: hashed, bhash: userInfo.Hash}
|
2016-06-02 05:17:31 +00:00
|
|
|
c.mu.Unlock()
|
2016-02-11 22:50:26 +00:00
|
|
|
return userInfo, nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2016-01-04 22:37:20 +00:00
|
|
|
func (c *Client) UserCount() int {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
|
|
|
|
return len(c.cacheData.Users)
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2016-02-04 15:12:52 +00:00
|
|
|
// ShardIDs returns a list of all shard ids.
|
|
|
|
func (c *Client) ShardIDs() []uint64 {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
|
2016-02-04 15:12:52 +00:00
|
|
|
var a []uint64
|
2016-03-08 19:59:33 +00:00
|
|
|
for _, dbi := range c.cacheData.Databases {
|
2016-02-04 15:12:52 +00:00
|
|
|
for _, rpi := range dbi.RetentionPolicies {
|
|
|
|
for _, sgi := range rpi.ShardGroups {
|
|
|
|
for _, si := range sgi.Shards {
|
|
|
|
a = append(a, si.ID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sort.Sort(uint64Slice(a))
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
2016-01-06 15:11:59 +00:00
|
|
|
// ShardGroupsByTimeRange returns a list of all shard groups on a database and policy that may contain data
|
|
|
|
// for the specified time range. Shard groups are sorted by start time.
|
2015-12-23 15:48:25 +00:00
|
|
|
func (c *Client) ShardGroupsByTimeRange(database, policy string, min, max time.Time) (a []ShardGroupInfo, err error) {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
|
2016-01-06 15:11:59 +00:00
|
|
|
// Find retention policy.
|
2016-03-08 19:59:33 +00:00
|
|
|
rpi, err := c.cacheData.RetentionPolicy(database, policy)
|
2016-01-06 15:11:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if rpi == nil {
|
|
|
|
return nil, influxdb.ErrRetentionPolicyNotFound(policy)
|
|
|
|
}
|
|
|
|
groups := make([]ShardGroupInfo, 0, len(rpi.ShardGroups))
|
|
|
|
for _, g := range rpi.ShardGroups {
|
|
|
|
if g.Deleted() || !g.Overlaps(min, max) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
groups = append(groups, g)
|
|
|
|
}
|
|
|
|
return groups, nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2016-02-19 20:38:02 +00:00
|
|
|
// ShardsByTimeRange returns a slice of shards that may contain data in the time range.
|
|
|
|
func (c *Client) ShardsByTimeRange(sources influxql.Sources, tmin, tmax time.Time) (a []ShardInfo, err error) {
|
|
|
|
m := make(map[*ShardInfo]struct{})
|
2015-11-04 21:06:06 +00:00
|
|
|
for _, src := range sources {
|
|
|
|
mm, ok := src.(*influxql.Measurement)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("invalid source type: %#v", src)
|
|
|
|
}
|
|
|
|
|
|
|
|
groups, err := c.ShardGroupsByTimeRange(mm.Database, mm.RetentionPolicy, tmin, tmax)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, g := range groups {
|
2016-02-19 20:38:02 +00:00
|
|
|
for i := range g.Shards {
|
|
|
|
m[&g.Shards[i]] = struct{}{}
|
2015-11-04 21:06:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-19 20:38:02 +00:00
|
|
|
a = make([]ShardInfo, 0, len(m))
|
|
|
|
for sh := range m {
|
|
|
|
a = append(a, *sh)
|
2015-11-04 21:06:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
2016-03-11 15:53:15 +00:00
|
|
|
// DropShard deletes a shard by ID.
|
|
|
|
func (c *Client) DropShard(id uint64) error {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
data.DropShard(id)
|
|
|
|
return c.commit(data)
|
|
|
|
}
|
|
|
|
|
2016-01-06 15:11:59 +00:00
|
|
|
// CreateShardGroup creates a shard group on a database and policy for a given timestamp.
|
|
|
|
func (c *Client) CreateShardGroup(database, policy string, timestamp time.Time) (*ShardGroupInfo, error) {
|
2016-05-09 22:10:38 +00:00
|
|
|
// Check under a read-lock
|
|
|
|
c.mu.RLock()
|
|
|
|
if sg, _ := c.cacheData.ShardGroupByTimestamp(database, policy, timestamp); sg != nil {
|
|
|
|
c.mu.RUnlock()
|
|
|
|
return sg, nil
|
|
|
|
}
|
|
|
|
c.mu.RUnlock()
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
2016-05-09 22:10:38 +00:00
|
|
|
// Check again under the write lock
|
2016-03-08 19:59:33 +00:00
|
|
|
data := c.cacheData.Clone()
|
2016-04-07 15:46:54 +00:00
|
|
|
if sg, _ := data.ShardGroupByTimestamp(database, policy, timestamp); sg != nil {
|
|
|
|
return sg, nil
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
sgi, err := createShardGroup(data, database, policy, timestamp)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2016-01-08 13:00:24 +00:00
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return sgi, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func createShardGroup(data *Data, database, policy string, timestamp time.Time) (*ShardGroupInfo, error) {
|
2016-04-07 15:46:54 +00:00
|
|
|
// It is the responsibility of the caller to check if it exists before calling this method.
|
2016-03-08 19:59:33 +00:00
|
|
|
if sg, _ := data.ShardGroupByTimestamp(database, policy, timestamp); sg != nil {
|
2016-04-07 15:46:54 +00:00
|
|
|
return nil, ErrShardGroupExists
|
2016-01-06 15:11:59 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.CreateShardGroup(database, policy, timestamp); err != nil {
|
2016-01-06 15:11:59 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
rpi, err := data.RetentionPolicy(database, policy)
|
2016-01-06 15:11:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if rpi == nil {
|
|
|
|
return nil, errors.New("retention policy deleted after shard group created")
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
sgi := rpi.ShardGroupByTimestamp(timestamp)
|
|
|
|
return sgi, nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2016-01-06 15:11:59 +00:00
|
|
|
// DeleteShardGroup removes a shard group from a database and retention policy by id.
|
2015-12-23 15:48:25 +00:00
|
|
|
func (c *Client) DeleteShardGroup(database, policy string, id uint64) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
|
|
|
if err := data.DeleteShardGroup(database, policy, id); err != nil {
|
|
|
|
return err
|
2016-01-06 15:11:59 +00:00
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2016-01-06 15:11:59 +00:00
|
|
|
// PrecreateShardGroups creates shard groups whose endtime is before the 'to' time passed in, but
|
|
|
|
// is yet to expire before 'from'. This is to avoid the need for these shards to be created when data
|
|
|
|
// for the corresponding time range arrives. Shard creation involves Raft consensus, and precreation
|
|
|
|
// avoids taking the hit at write-time.
|
2015-12-23 15:48:25 +00:00
|
|
|
func (c *Client) PrecreateShardGroups(from, to time.Time) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
data := c.cacheData.Clone()
|
2016-04-07 15:46:54 +00:00
|
|
|
var changed bool
|
2016-03-08 19:59:33 +00:00
|
|
|
|
|
|
|
for _, di := range data.Databases {
|
2016-01-06 15:11:59 +00:00
|
|
|
for _, rp := range di.RetentionPolicies {
|
|
|
|
if len(rp.ShardGroups) == 0 {
|
|
|
|
// No data was ever written to this group, or all groups have been deleted.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
g := rp.ShardGroups[len(rp.ShardGroups)-1] // Get the last group in time.
|
|
|
|
if !g.Deleted() && g.EndTime.Before(to) && g.EndTime.After(from) {
|
|
|
|
// Group is not deleted, will end before the future time, but is still yet to expire.
|
|
|
|
// This last check is important, so the system doesn't create shards groups wholly
|
|
|
|
// in the past.
|
|
|
|
|
|
|
|
// Create successive shard group.
|
|
|
|
nextShardGroupTime := g.EndTime.Add(1 * time.Nanosecond)
|
2016-04-07 15:46:54 +00:00
|
|
|
// if it already exists, continue
|
|
|
|
if sg, _ := data.ShardGroupByTimestamp(di.Name, rp.Name, nextShardGroupTime); sg != nil {
|
|
|
|
c.logger.Printf("shard group %d exists for database %s, retention policy %s", sg.ID, di.Name, rp.Name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
newGroup, err := createShardGroup(data, di.Name, rp.Name, nextShardGroupTime)
|
|
|
|
if err != nil {
|
2016-01-06 15:11:59 +00:00
|
|
|
c.logger.Printf("failed to precreate successive shard group for group %d: %s", g.ID, err.Error())
|
2016-04-07 15:46:54 +00:00
|
|
|
continue
|
2016-01-06 15:11:59 +00:00
|
|
|
}
|
2016-04-07 15:46:54 +00:00
|
|
|
changed = true
|
|
|
|
c.logger.Printf("new shard group %d successfully precreated for database %s, retention policy %s", newGroup.ID, di.Name, rp.Name)
|
2016-01-06 15:11:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-08 19:59:33 +00:00
|
|
|
|
2016-04-07 15:46:54 +00:00
|
|
|
if changed {
|
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-03-14 15:51:12 +00:00
|
|
|
}
|
|
|
|
|
2015-12-23 15:48:25 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-01-06 15:11:59 +00:00
|
|
|
// ShardOwner returns the owning shard group info for a specific shard.
|
|
|
|
func (c *Client) ShardOwner(shardID uint64) (database, policy string, sgi *ShardGroupInfo) {
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
c.mu.RUnlock()
|
|
|
|
|
|
|
|
for _, dbi := range data.Databases {
|
2016-01-06 15:11:59 +00:00
|
|
|
for _, rpi := range dbi.RetentionPolicies {
|
|
|
|
for _, g := range rpi.ShardGroups {
|
|
|
|
if g.Deleted() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, sh := range g.Shards {
|
|
|
|
if sh.ID == shardID {
|
|
|
|
database = dbi.Name
|
|
|
|
policy = rpi.Name
|
|
|
|
sgi = &g
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
func (c *Client) CreateContinuousQuery(database, name, query string) error {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
data := c.cacheData.Clone()
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.CreateContinuousQuery(database, name, query); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
|
|
|
}
|
2016-01-25 04:56:06 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
func (c *Client) DropContinuousQuery(database, name string) error {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2016-01-25 04:56:06 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
data := c.cacheData.Clone()
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.DropContinuousQuery(database, name); err != nil {
|
|
|
|
return nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
2016-02-19 21:16:35 +00:00
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
func (c *Client) CreateSubscription(database, rp, name, mode string, destinations []string) error {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
data := c.cacheData.Clone()
|
2016-01-09 04:29:48 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := data.CreateSubscription(database, rp, name, mode, destinations); err != nil {
|
|
|
|
return err
|
2016-01-09 04:29:48 +00:00
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
func (c *Client) DropSubscription(database, rp, name string) error {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
data := c.cacheData.Clone()
|
|
|
|
|
|
|
|
if err := data.DropSubscription(database, rp, name); err != nil {
|
|
|
|
return err
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if err := c.commit(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2015-12-30 13:15:00 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
func (c *Client) SetData(data *Data) error {
|
|
|
|
c.mu.Lock()
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
// reset the index so the commit will fire a change event
|
|
|
|
c.cacheData.Index = 0
|
2015-12-23 15:48:25 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
// increment the index to force the changed channel to fire
|
|
|
|
d := data.Clone()
|
|
|
|
d.Index++
|
2016-03-14 15:51:12 +00:00
|
|
|
|
|
|
|
if err := c.commit(d); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-12-23 15:48:25 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
c.mu.Unlock()
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
return nil
|
2016-01-20 23:50:42 +00:00
|
|
|
}
|
|
|
|
|
2016-04-07 15:46:54 +00:00
|
|
|
func (c *Client) Data() Data {
|
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
d := c.cacheData.Clone()
|
|
|
|
return *d
|
|
|
|
}
|
|
|
|
|
2015-12-30 13:15:00 +00:00
|
|
|
// WaitForDataChanged will return a channel that will get closed when
|
|
|
|
// the metastore data has changed
|
|
|
|
func (c *Client) WaitForDataChanged() chan struct{} {
|
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
return c.changed
|
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
// commit assumes it is under a full lock
|
2016-03-14 15:51:12 +00:00
|
|
|
func (c *Client) commit(data *Data) error {
|
2016-03-08 19:59:33 +00:00
|
|
|
data.Index++
|
2016-03-14 15:51:12 +00:00
|
|
|
|
|
|
|
// try to write to disk before updating in memory
|
|
|
|
if err := snapshot(c.path, data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// update in memory
|
2016-03-08 19:59:33 +00:00
|
|
|
c.cacheData = data
|
2016-03-14 15:51:12 +00:00
|
|
|
|
|
|
|
// close channels to signal changes
|
2016-03-08 19:59:33 +00:00
|
|
|
close(c.changed)
|
|
|
|
c.changed = make(chan struct{})
|
2016-03-14 15:51:12 +00:00
|
|
|
|
|
|
|
return nil
|
2016-03-08 19:59:33 +00:00
|
|
|
}
|
|
|
|
|
2015-12-23 15:48:25 +00:00
|
|
|
func (c *Client) MarshalBinary() ([]byte, error) {
|
2016-01-20 23:50:42 +00:00
|
|
|
c.mu.RLock()
|
|
|
|
defer c.mu.RUnlock()
|
|
|
|
return c.cacheData.MarshalBinary()
|
|
|
|
}
|
|
|
|
|
2016-04-20 20:07:08 +00:00
|
|
|
// SetLogOutput sets the writer to which all logs are written. It must not be
|
|
|
|
// called after Open is called.
|
|
|
|
func (c *Client) SetLogOutput(w io.Writer) {
|
2016-01-20 23:50:42 +00:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2016-04-20 20:07:08 +00:00
|
|
|
c.logger = log.New(w, "[metaclient] ", log.LstdFlags)
|
2015-12-23 15:48:25 +00:00
|
|
|
}
|
2015-12-30 13:15:00 +00:00
|
|
|
|
2016-02-11 22:50:26 +00:00
|
|
|
func (c *Client) updateAuthCache() {
|
|
|
|
// copy cached user info for still-present users
|
|
|
|
newCache := make(map[string]authUser, len(c.authCache))
|
|
|
|
|
|
|
|
for _, userInfo := range c.cacheData.Users {
|
|
|
|
if cached, ok := c.authCache[userInfo.Name]; ok {
|
|
|
|
if cached.bhash == userInfo.Hash {
|
|
|
|
newCache[userInfo.Name] = cached
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.authCache = newCache
|
|
|
|
}
|
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
// snapshot will save the current meta data to disk
|
|
|
|
func snapshot(path string, data *Data) error {
|
|
|
|
file := filepath.Join(path, metaFile)
|
2016-03-08 19:59:33 +00:00
|
|
|
tmpFile := file + "tmp"
|
2016-02-10 18:16:54 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
f, err := os.Create(tmpFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
2016-02-10 18:16:54 +00:00
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
var d []byte
|
|
|
|
if b, err := data.MarshalBinary(); err != nil {
|
2016-03-08 19:59:33 +00:00
|
|
|
return err
|
|
|
|
} else {
|
2016-03-14 15:51:12 +00:00
|
|
|
d = b
|
2016-03-08 19:59:33 +00:00
|
|
|
}
|
2016-02-10 18:16:54 +00:00
|
|
|
|
2016-03-14 15:51:12 +00:00
|
|
|
if _, err := f.Write(d); err != nil {
|
2016-03-08 19:59:33 +00:00
|
|
|
return err
|
2016-02-10 18:16:54 +00:00
|
|
|
}
|
|
|
|
|
2016-03-30 16:21:03 +00:00
|
|
|
if err = f.Sync(); err != nil {
|
2016-03-08 19:59:33 +00:00
|
|
|
return err
|
2016-02-10 18:16:54 +00:00
|
|
|
}
|
2016-03-08 19:59:33 +00:00
|
|
|
|
2016-04-05 17:49:43 +00:00
|
|
|
//close file handle before renaming to support Windows
|
|
|
|
if err = f.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-03-18 16:15:29 +00:00
|
|
|
return renameFile(tmpFile, file)
|
2016-02-10 18:16:54 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
// Load will save the current meta data from disk
|
|
|
|
func (c *Client) Load() error {
|
|
|
|
file := filepath.Join(c.path, metaFile)
|
|
|
|
|
|
|
|
f, err := os.Open(file)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil
|
2016-02-10 18:16:54 +00:00
|
|
|
}
|
2016-03-08 19:59:33 +00:00
|
|
|
return err
|
2016-02-10 18:16:54 +00:00
|
|
|
}
|
2016-03-08 19:59:33 +00:00
|
|
|
defer f.Close()
|
2016-02-10 18:16:54 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
data, err := ioutil.ReadAll(f)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-12-30 22:52:39 +00:00
|
|
|
|
2016-03-08 19:59:33 +00:00
|
|
|
if err := c.cacheData.UnmarshalBinary(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
2015-12-30 22:52:39 +00:00
|
|
|
}
|
2016-02-04 15:12:52 +00:00
|
|
|
|
|
|
|
type uint64Slice []uint64
|
|
|
|
|
|
|
|
func (a uint64Slice) Len() int { return len(a) }
|
|
|
|
func (a uint64Slice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
func (a uint64Slice) Less(i, j int) bool { return a[i] < a[j] }
|