func (c *Client) SetAdminPrivilege(username string, admin bool) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	if err := data.SetAdminPrivilege(username, admin); err != nil {
		return err
	}
	if err := c.commit(data); err != nil {
		return err
	}
	return nil
}
SetAdminPrivilege sets or unsets admin privilege to the given username.
package meta
import (
	"bytes"
	crand "crypto/rand"
	"crypto/sha256"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"
	"github.com/influxdata/influxdb"
	"github.com/influxdata/influxdb/logger"
	"github.com/influxdata/influxdb/pkg/file"
	"github.com/influxdata/influxdb/services/meta"
	"github.com/influxdata/influxql"
	"go.uber.org/zap"
	"golang.org/x/crypto/bcrypt"
)
const (
	SaltBytes = 32
	metaFile = "meta.db"
	ShardGroupDeletedExpiration = -2 * 7 * 24 * time.Hour
)
type Client struct {
	logger *zap.Logger
	mu        sync.RWMutex
	closing   chan struct{}
	changed   chan struct{}
	cacheData *Data
	authCache map[string]authUser
	path string
	retentionAutoCreate bool
}
type authUser struct {
	bhash string
	salt  []byte
	hash  []byte
}
func NewClient(config *meta.Config) *Client {
	return &Client{
		cacheData: &Data{
			Data: meta.Data{
				ClusterID: 0,
				Index:     1,
			},
		},
		closing:             make(chan struct{}),
		changed:             make(chan struct{}),
		logger:              zap.NewNop(),
		authCache:           make(map[string]authUser),
		path:                config.Dir,
		retentionAutoCreate: config.RetentionAutoCreate,
	}
}
func (c *Client) Print() {
	fmt.Printf("%+v\n", c.cacheData)
}
func (c *Client) Open() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if err := c.Load(); err != nil {
		return err
	}
	if c.cacheData.Index == 1 {
		if err := snapshot(c.path, c.cacheData); err != nil {
			return err
		}
	}
	return nil
}
func (c *Client) Close() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if t, ok := http.DefaultTransport.(*http.Transport); ok {
		t.CloseIdleConnections()
	}
	select {
	case <-c.closing:
		return nil
	default:
		close(c.closing)
	}
	return nil
}
func (c *Client) ClusterID() uint64 {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.cacheData.ClusterID
}
func (c *Client) data() *Data {
	return c.cacheData
}
func (c *Client) DataNode(id uint64) (*meta.NodeInfo, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	for _, n := range c.data().DataNodes {
		if n.ID == id {
			return &n, nil
		}
	}
	return nil, ErrNodeNotFound
}
func (c *Client) DataNodes() ([]meta.NodeInfo, error) {
	return c.data().DataNodes, nil
}
func (c *Client) CreateDataNode(httpAddr, tcpAddr string) (*meta.NodeInfo, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	err := c.data().CreateDataNode(httpAddr, tcpAddr)
	if err != nil {
		return nil, err
	}
	n, err := c.DataNodeByTCPHost(tcpAddr)
	if err != nil {
		return nil, err
	}
	if err := c.commit(c.data()); err != nil {
		return nil, err
	}
	return n, nil
}
func (c *Client) DataNodeByHTTPHost(httpAddr string) (*meta.NodeInfo, error) {
	nodes, _ := c.DataNodes()
	for _, n := range nodes {
		if n.Host == httpAddr {
			return &n, nil
		}
	}
	return nil, ErrNodeNotFound
}
func (c *Client) DataNodeByTCPHost(tcpAddr string) (*meta.NodeInfo, error) {
	nodes, _ := c.DataNodes()
	for _, n := range nodes {
		if n.TCPHost == tcpAddr {
			return &n, nil
		}
	}
	return nil, ErrNodeNotFound
}
func (c *Client) DeleteDataNode(id uint64) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	err := c.cacheData.DeleteDataNode(id)
	if err != nil {
		return err
	}
	if err := c.commit(c.cacheData); err != nil {
		return err
	}
	return nil
}
func (c *Client) MetaNodes() ([]meta.NodeInfo, error) {
	return c.data().MetaNodes, nil
}
func (c *Client) MetaNodeByAddr(addr string) *meta.NodeInfo {
	for _, n := range c.data().MetaNodes {
		if n.Host == addr {
			return &n
		}
	}
	return nil
}
func (c *Client) Database(name string) *meta.DatabaseInfo {
	c.mu.RLock()
	defer c.mu.RUnlock()
	for _, d := range c.cacheData.Databases {
		if d.Name == name {
			return &d
		}
	}
	return nil
}
func (c *Client) Databases() []meta.DatabaseInfo {
	c.mu.RLock()
	defer c.mu.RUnlock()
	dbs := c.cacheData.Databases
	if dbs == nil {
		return []meta.DatabaseInfo{}
	}
	return dbs
}
func (c *Client) CreateDatabase(name string) (*meta.DatabaseInfo, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	if db := data.Database(name); db != nil {
		return db, nil
	}
	if err := data.CreateDatabase(name); err != nil {
		return nil, err
	}
	if c.retentionAutoCreate {
		rpi := meta.DefaultRetentionPolicyInfo()
		if err := data.CreateRetentionPolicy(name, rpi, true); err != nil {
			return nil, err
		}
	}
	db := data.Database(name)
	if err := c.commit(data); err != nil {
		return nil, err
	}
	return db, nil
}
func (c *Client) CreateDatabaseWithRetentionPolicy(name string, spec *meta.RetentionPolicySpec) (*meta.DatabaseInfo, error) {
	if spec == nil {
		return nil, errors.New("CreateDatabaseWithRetentionPolicy called with nil spec")
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	if spec.Duration != nil && *spec.Duration < meta.MinRetentionPolicyDuration && *spec.Duration != 0 {
		return nil, meta.ErrRetentionPolicyDurationTooLow
	}
	db := data.Database(name)
	if db == nil {
		if err := data.CreateDatabase(name); err != nil {
			return nil, err
		}
		db = data.Database(name)
	}
	rpi := spec.NewRetentionPolicyInfo()
	if len(db.RetentionPolicies) == 0 {
		if err := data.CreateRetentionPolicy(name, rpi, true); err != nil {
			return nil, err
		}
	} else if !spec.Matches(db.RetentionPolicy(rpi.Name)) {
		return nil, meta.ErrRetentionPolicyConflict
	}
	if db.DefaultRetentionPolicy != rpi.Name {
		return nil, meta.ErrRetentionPolicyConflict
	}
	if err := c.commit(data); err != nil {
		return nil, err
	}
	db = data.Database(name)
	return db, nil
}
func (c *Client) DropDatabase(name string) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	if err := data.DropDatabase(name); err != nil {
		return err
	}
	if err := c.commit(data); err != nil {
		return err
	}
	return nil
}
func (c *Client) CreateRetentionPolicy(database string, spec *meta.RetentionPolicySpec, makeDefault bool) (*meta.RetentionPolicyInfo, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	if spec.Duration != nil && *spec.Duration < meta.MinRetentionPolicyDuration && *spec.Duration != 0 {
		return nil, meta.ErrRetentionPolicyDurationTooLow
	}
	rp := spec.NewRetentionPolicyInfo()
	if err := data.CreateRetentionPolicy(database, rp, makeDefault); err != nil {
		return nil, err
	}
	if err := c.commit(data); err != nil {
		return nil, err
	}
	return rp, nil
}
func (c *Client) RetentionPolicy(database, name string) (rpi *meta.RetentionPolicyInfo, err error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	db := c.cacheData.Database(database)
	if db == nil {
		return nil, influxdb.ErrDatabaseNotFound(database)
	}
	return db.RetentionPolicy(name), nil
}
func (c *Client) DropRetentionPolicy(database, name string) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	if err := data.DropRetentionPolicy(database, name); err != nil {
		return err
	}
	if err := c.commit(data); err != nil {
		return err
	}
	return nil
}
func (c *Client) UpdateRetentionPolicy(database, name string, rpu *meta.RetentionPolicyUpdate, makeDefault bool) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	if err := data.UpdateRetentionPolicy(database, name, rpu, makeDefault); err != nil {
		return err
	}
	if err := c.commit(data); err != nil {
		return err
	}
	return nil
}
func (c *Client) Users() []meta.UserInfo {
	c.mu.RLock()
	defer c.mu.RUnlock()
	users := c.cacheData.Users
	if users == nil {
		return []meta.UserInfo{}
	}
	return users
}
func (c *Client) User(name string) (meta.User, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.user(name)
}
func (c *Client) user(name string) (meta.User, error) {
	for _, u := range c.cacheData.Users {
		if u.Name == name {
			return &u, nil
		}
	}
	return nil, meta.ErrUserNotFound
}
var bcryptCost = bcrypt.DefaultCost
func (c *Client) hashWithSalt(salt []byte, password string) []byte {
	hasher := sha256.New()
	hasher.Write(salt)
	hasher.Write([]byte(password))
	return hasher.Sum(nil)
}
func (c *Client) saltedHash(password string) (salt, hash []byte, err error) {
	salt = make([]byte, SaltBytes)
	if _, err := io.ReadFull(crand.Reader, salt); err != nil {
		return nil, nil, err
	}
	return salt, c.hashWithSalt(salt, password), nil
}
func (c *Client) CreateUser(name, password string, admin bool) (meta.User, error) {
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	if u, err := c.user(name); err != nil && u != nil {
		if err := bcrypt.CompareHashAndPassword([]byte(u.(*meta.UserInfo).Hash), []byte(password)); err != nil || u.(*meta.UserInfo).Admin != admin {
			return nil, meta.ErrUserExists
		}
		return u, nil
	}
	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
	if err != nil {
		return nil, err
	}
	if err := data.CreateUser(name, string(hash), admin); err != nil {
		return nil, err
	}
	if err := c.commit(data); err != nil {
		return nil, err
	}
	return c.user(name)
}
func (c *Client) UpdateUser(name, password string) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
	if err != nil {
		return err
	}
	if err := data.UpdateUser(name, string(hash)); err != nil {
		return err
	}
	delete(c.authCache, name)
	return c.commit(data)
}
func (c *Client) DropUser(name string) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	if err := data.DropUser(name); err != nil {
		return err
	}
	if err := c.commit(data); err != nil {
		return err
	}
	return nil
}
func (c *Client) SetPrivilege(username, database string, p influxql.Privilege) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	data := c.cacheData.Clone()
	if err := data.SetPrivilege(username, database, p); err != nil {
		return err
	}
	if err := c.commit(data); err != nil {
		return err
	}
	return nil
}