influxdb/raft/config.go

190 lines
4.4 KiB
Go

package raft
import (
"encoding/json"
"io"
"net/url"
)
// Config represents the configuration for the log.
type Config struct {
// Cluster identifier. Used to prevent separate clusters from
// accidentally communicating with one another.
ClusterID uint64
// Index is the last log index when the configuration was updated.
Index uint64
// MaxNodeID is the largest node identifier generated for this config.
MaxNodeID uint64
// List of nodes in the cluster.
Nodes []*ConfigNode
}
// NodeByID returns a node by identifier.
func (c *Config) NodeByID(id uint64) *ConfigNode {
for _, n := range c.Nodes {
if n.ID == id {
return n
}
}
return nil
}
// NodeByURL returns a node by URL.
func (c *Config) NodeByURL(u url.URL) *ConfigNode {
for _, n := range c.Nodes {
if n.URL.String() == u.String() {
return n
}
}
return nil
}
// AddNode adds a new node to the config.
func (c *Config) AddNode(id uint64, u url.URL) error {
// Validate that the id is non-zero and the url exists.
if id == 0 {
return ErrInvalidNodeID
} else if u.Host == "" {
return ErrNodeURLRequired
}
// Validate that no other nodes in the config have the same id or URL.
for _, n := range c.Nodes {
if n.ID == id {
return ErrDuplicateNodeID
} else if n.URL.String() == u.String() {
return ErrDuplicateNodeURL
}
}
// Add the node to the config.
c.Nodes = append(c.Nodes, &ConfigNode{ID: id, URL: u})
return nil
}
// RemoveNode removes a node by id.
// Returns ErrNodeNotFound if the node does not exist.
func (c *Config) RemoveNode(id uint64) error {
for i, node := range c.Nodes {
if node.ID == id {
copy(c.Nodes[i:], c.Nodes[i+1:])
c.Nodes[len(c.Nodes)-1] = nil
c.Nodes = c.Nodes[:len(c.Nodes)-1]
return nil
}
}
return ErrNodeNotFound
}
// Clone returns a deep copy of the configuration.
func (c *Config) Clone() *Config {
other := &Config{
ClusterID: c.ClusterID,
Index: c.Index,
MaxNodeID: c.MaxNodeID,
}
other.Nodes = make([]*ConfigNode, len(c.Nodes))
for i, n := range c.Nodes {
other.Nodes[i] = n.clone()
}
return other
}
// ConfigNode represents a single machine in the raft configuration.
type ConfigNode struct {
ID uint64
URL url.URL
}
// clone returns a deep copy of the node.
func (n *ConfigNode) clone() *ConfigNode {
other := &ConfigNode{ID: n.ID}
other.URL = n.URL
return other
}
// ConfigEncoder encodes a config to a writer.
type ConfigEncoder struct {
w io.Writer
}
// NewConfigEncoder returns a new instance of ConfigEncoder attached to a writer.
func NewConfigEncoder(w io.Writer) *ConfigEncoder {
return &ConfigEncoder{w}
}
// Encode marshals the configuration to the encoder's writer.
func (enc *ConfigEncoder) Encode(c *Config) error {
// Copy properties to intermediate type.
var o configJSON
o.ClusterID = c.ClusterID
o.Index = c.Index
o.MaxNodeID = c.MaxNodeID
for _, n := range c.Nodes {
o.Nodes = append(o.Nodes, &configNodeJSON{ID: n.ID, URL: n.URL.String()})
}
// Encode intermediate type as JSON.
return json.NewEncoder(enc.w).Encode(&o)
}
// ConfigDecoder decodes a config from a reader.
type ConfigDecoder struct {
r io.Reader
}
// NewConfigDecoder returns a new instance of ConfigDecoder attached to a reader.
func NewConfigDecoder(r io.Reader) *ConfigDecoder {
return &ConfigDecoder{r}
}
// Decode marshals the configuration to the decoder's reader.
func (dec *ConfigDecoder) Decode(c *Config) error {
// Decode into intermediate type.
var o configJSON
if err := json.NewDecoder(dec.r).Decode(&o); err != nil {
return err
}
// Copy properties to config.
c.ClusterID = o.ClusterID
c.Index = o.Index
c.MaxNodeID = o.MaxNodeID
// Validate and append nodes.
for _, n := range o.Nodes {
// Parse node URL.
u, err := url.Parse(n.URL)
if err != nil {
return err
} else if n.URL == "" {
u = &url.URL{}
}
// Append node to config.
if err := c.AddNode(n.ID, *u); err != nil {
return err
}
}
return nil
}
// configJSON represents an intermediate struct used for JSON encoding.
type configJSON struct {
ClusterID uint64 `json:"clusterID,omitempty"`
Index uint64 `json:"index,omitempty"`
MaxNodeID uint64 `json:"maxNodeID,omitempty"`
Nodes []*configNodeJSON `json:"nodes,omitempty"`
}
// configNodeJSON represents the JSON serialized form of the ConfigNode type.
type configNodeJSON struct {
ID uint64 `json:"id"`
URL string `json:"url,omitempty"`
}