1//  Copyright (c) 2014 Couchbase, Inc.
2//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
3//  except in compliance with the License. You may obtain a copy of the License at
4//    http://www.apache.org/licenses/LICENSE-2.0
5//  Unless required by applicable law or agreed to in writing, software distributed under the
6//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
7//  either express or implied. See the License for the specific language governing permissions
8//  and limitations under the License.
9
10/*
11
12 Package clustering provides a common clustering abstraction for cluster
13 configuration management.
14
15 The main abstractions, and their relationships, are:
16 	ConfigurationStore - used for storing all cluster configuration data.
17	Cluster - Configuration common to a cluster. A cluster is just a set of Query Nodes, so all
18		the Query Nodes belonging to the cluster will share this configuration.
19	Query Node - Configuration for a single instance of the Query Engine. Provides sufficient
20		information to uniquely identify, and interact with, a Query Engine instance.
21The logical hierarchy is as follows:
22	ConfigurationStore -> Clusters -> Query Nodes
23*/
24package clustering
25
26import (
27	"github.com/couchbase/query/accounting"
28	"github.com/couchbase/query/datastore"
29	"github.com/couchbase/query/errors"
30)
31
32type Mode string // A Query Node runs in a particular Mode
33
34const (
35	STARTING     Mode = "starting"     // Query Node is starting up
36	STANDALONE   Mode = "standalone"   // Query Node is running by itself, it is not part of a cluster
37	CLUSTERED    Mode = "clustered"    // Query Node is part of a cluster (could be a single node cluster)
38	UNCLUSTERED  Mode = "unclustered"  // Query Node is not part of a cluster. Can serve queries
39	DISCONNECTED Mode = "disconnected" // Query Node is disconnected from datastore. It cannot serve queries
40	STOPPING     Mode = "stopping"     // Query Node is in the process of shutting down
41)
42
43type Privilege int
44
45const (
46	PRIV_READ      Privilege = 1 // read operations (e.g. retrieve node configuration)
47	PRIV_SYS_ADMIN Privilege = 2 // system administrator operations (e.g. add node, reload ssl certificate)
48)
49
50// Version provides a abstraction of logical software version for Query Nodes;
51// it could represent server build version or API version
52type Version interface {
53	String() string            // Return a string representation of the version
54	Compatible(v Version) bool // Return true if the given Version is compatible with this Version
55}
56
57// ConfigurationStore represents a store for maintaining all cluster configuration data.
58type ConfigurationStore interface {
59	Id() string                                            // Id of this ConfigurationStore
60	URL() string                                           // URL to this ConfigurationStore
61	ClusterNames() ([]string, errors.Error)                // Names of the Clusters in this ConfigurationStore
62	ClusterByName(name string) (Cluster, errors.Error)     // Find a Cluster in this ConfigurationStore using the Cluster's name
63	ConfigurationManager() ConfigurationManager            // Get a ConfigurationManager for this ConfigurationStore
64	Authorize(map[string]string, []Privilege) errors.Error // Do authorization returning an error if unsuccessful
65	WhoAmI() (string, errors.Error)                        // The Id of the local node, if clustered
66	State() (Mode, errors.Error)                           // The clustring state of the local node
67	SetOptions(httpAddr, httpsAddr string) errors.Error    // Set options for the local ConfigurationStore
68}
69
70// Cluster is a named collection of Query Nodes. It is basically a single-level namespace for one or more Query Nodes.
71// It also provides configuration common to all the Query Nodes in a cluster: Datastore, AccountingStore and ConfigurationStore.
72type Cluster interface {
73	ConfigurationStoreId() string                          // Id of the ConfigurationStore that contains this Cluster
74	Name() string                                          // Name of this Cluster (unique within the ConfigurationStore)
75	QueryNodeNames() ([]string, errors.Error)              // Names of all the Query Nodes in this Cluster
76	QueryNodeByName(name string) (QueryNode, errors.Error) // Find a Query Node in this Cluster using the Query Node's id
77	Datastore() datastore.Datastore                        // The Datastore used by all Query Nodes in the cluster
78	AccountingStore() accounting.AccountingStore           // The AccountingStore used by all Query Nodes in the cluster
79	ConfigurationStore() ConfigurationStore                // The ConfigurationStore used by all Query Nodes in the cluster
80	Version() Version                                      // Logical version of the software that the QueryNodes in the cluster are running
81	ClusterManager() ClusterManager                        // Get a ClusterManager for this Cluster
82	Capability(string) bool                                // Check if cluster possesses a certain capability
83}
84
85type Standalone interface {
86	Datastore() datastore.Datastore              // The Datastore used by all Query Nodes in the cluster
87	AccountingStore() accounting.AccountingStore // The AccountingStore used by all Query Nodes in the cluster
88	ConfigurationStore() ConfigurationStore      // The ConfigurationStore used by all Query Nodes in the cluster
89	Version() Version                            // Logical version of the software that the QueryNodes in the cluster are running
90}
91
92// QueryNode is the configuration for a single instance of a Query Engine.
93type QueryNode interface {
94	Cluster() Cluster          // The Cluster that this QueryNode belongs to
95	Name() string              // Name of this QueryNode (unique within the cluster)
96	QueryEndpoint() string     // Endpoint for serving N1QL queries
97	ClusterEndpoint() string   // Endpoint for serving admin commands
98	QuerySecure() string       // Endpoint for serving secure N1QL queries
99	ClusterSecure() string     // Endpoint for serving secure admin commands
100	Standalone() Standalone    // The QueryNode's configuration when unclustered
101	Options() QueryNodeOptions // The command line options the query node was started with
102}
103
104type QueryNodeOptions interface {
105	Datastore() string       // Datastore address
106	Configstore() string     // Configstore address
107	Accountingstore() string // Accountingstore address
108	Namespace() string       //default namespace
109	Readonly() bool          // Read-only mode
110	Signature() bool         // Whether to provide Signature
111	Metrics() bool           // Whether to provide Metrics
112	RequestCap() int         // Max number of queued requests
113	Threads() int            // Thread count
114	OrderLimit() int         // Max LIMIT for ORDER BY clauses
115	UpdateLimit() int        // Max LIMIT for data modification statements
116	Http() string            // HTTP service address
117	Https() string           // HTTPS service address
118	Certfile() string        // HTTPS certificate file
119	Keyfile() string         // HTTPS private key file
120	Logger() string          // Name of Logger implementation
121	Debug() bool             // Debug mode
122	Cluster() string         // Name of the cluster to join
123}
124
125// ConfigurationManager is the interface for managing cluster lifecycles -
126// addition and removal of clusters from the ConfigurationStore.
127type ConfigurationManager interface {
128	// The ConfigurationStore that this ConfigurationManager is managing
129	ConfigurationStore() ConfigurationStore
130
131	// Add a cluster to the configuration
132	// Possible reasons for error:
133	//	- Configuration contains a Cluster with the same identity
134	// Returns updated Cluster if no error (Cluster is now part of the ConfigurationStore)
135	AddCluster(c Cluster) (Cluster, errors.Error)
136
137	// Remove a cluster from the configuration
138	// Possible reasons for error:
139	//	- Cluster is not empty (contains one or more QueryNodes)
140	// Returns true if no error (Cluster is no longer in the ConfigurationStore)
141	RemoveCluster(c Cluster) (bool, errors.Error)
142
143	// Remove the named cluster from the configuration
144	// Possible reasons for error:
145	//	- Configuration does not have a cluster with the given id
146	//	- Cluster is not empty (contains one or more QueryNodes)
147	RemoveClusterByName(name string) (bool, errors.Error)
148
149	// The clusters in the configuration
150	GetClusters() ([]Cluster, errors.Error)
151}
152
153// ClusterManager is the interface the actions that can be done to a Cluster;
154// it is intended to support the lifecycle of QueryNodes - addition and removal of
155// QueryNodes from a Cluster.
156type ClusterManager interface {
157	// The Cluster that this ClusterManager is managing
158	Cluster() Cluster
159
160	// Add the given QueryNode to the Cluster
161	// Possible reasons for error:
162	//	- Cluster contains a QueryNode with the same identity
163	//	- Version incompatibility
164	//	- Given QueryNode is using a different Datastore
165	//	- Given QueryNode is not in standalone mode
166	// Returns the updated QueryNode if no error (cluster mode, connected to Cluster)
167	AddQueryNode(n QueryNode) (QueryNode, errors.Error)
168
169	// Remove the given QueryNode from the Cluster
170	// Possible reasons for error:
171	//	- Cluster does not contain the given QueryNode
172	//	- Given QueryNode is running in standalone mode
173	// Returns the updated QueryNode if no error (standalone mode, no cluster id)
174	RemoveQueryNode(n QueryNode) (QueryNode, errors.Error)
175
176	// Remove the QueryNode with the given id from the Cluster
177	// Possible reasons for error:
178	//	-- Cluster does not contain a QueryNode with the given id
179	// Returns the updated QueryNode if no error (standalone mode, no cluster id)
180	RemoveQueryNodeByName(name string) (QueryNode, errors.Error)
181
182	// Return the QueryNodes in the Cluster
183	GetQueryNodes() ([]QueryNode, errors.Error)
184}
185
186// Standard Version implementation - this can be used by all configstore implementations
187type StdVersion struct {
188	VersionString string
189}
190
191func NewVersion(version string) *StdVersion {
192	return &StdVersion{
193		VersionString: version,
194	}
195}
196
197func (st *StdVersion) String() string {
198	return st.VersionString
199}
200
201func (st *StdVersion) Compatible(v Version) bool {
202	return v.String() == st.String()
203}
204
205// Standard QueryNodeOptions implementation - this can be used by all configstore implementations
206type ClOptions struct {
207	DatastoreURL string `json:"datastore"`
208	CfgstoreURL  string `json:"configstore"`
209	AcctstoreURL string `json:"acctstore"`
210	NamespaceDef string `json:"namespace"`
211	ReadMode     bool   `json:"readonly"`
212	SignReqd     bool   `json:"readonly"`
213	MetricsReqd  bool   `json:"metrics"`
214	ReqCap       int    `json:"requestcap"`
215	NumThreads   int    `json:"threads"`
216	OrdLimit     int    `json:"orderlimit"`
217	UpdLimit     int    `json:"updatelimit"`
218	HttpAddr     string `json:"http"`
219	HttpsAddr    string `json:"https"`
220	LoggerImpl   string `json:"logger"`
221	DebugFlag    bool   `json:"debug"`
222	ClusterName  string `json:"cluster"`
223	CertFile     string `json:"certfile"`
224	KeyFile      string `json:"keyfile"`
225}
226
227func (c *ClOptions) Datastore() string {
228	return c.DatastoreURL
229}
230
231func (c *ClOptions) Logger() string {
232	return c.LoggerImpl
233}
234
235func (c *ClOptions) Debug() bool {
236	return c.DebugFlag
237}
238
239func (c *ClOptions) Cluster() string {
240	return c.ClusterName
241}
242
243func (c *ClOptions) Configstore() string {
244	return c.CfgstoreURL
245}
246
247func (c *ClOptions) Accountingstore() string {
248	return c.AcctstoreURL
249}
250
251func (c *ClOptions) Namespace() string {
252	return c.NamespaceDef
253}
254
255func (c *ClOptions) Readonly() bool {
256	return c.ReadMode
257}
258
259func (c *ClOptions) Signature() bool {
260	return c.SignReqd
261}
262
263func (c *ClOptions) Metrics() bool {
264	return c.MetricsReqd
265}
266
267func (c *ClOptions) RequestCap() int {
268	return c.ReqCap
269}
270
271func (c *ClOptions) Threads() int {
272	return c.NumThreads
273}
274
275func (c *ClOptions) OrderLimit() int {
276	return c.OrdLimit
277}
278
279func (c *ClOptions) UpdateLimit() int {
280	return c.UpdLimit
281}
282
283func (c *ClOptions) Http() string {
284	return c.HttpAddr
285}
286
287func (c *ClOptions) Https() string {
288	return c.HttpsAddr
289}
290
291func (c *ClOptions) Certfile() string {
292	return c.CertFile
293}
294
295func (c *ClOptions) Keyfile() string {
296	return c.KeyFile
297}
298
299func NewOptions(datastoreURL string, cfgstoreURL string, acctstoreURL string, namespace string,
300	readOnly bool, signature bool, metrics bool, reqCap int, threads int, ordLim int, updLim int,
301	http string, https string, loggerImpl string, debugFlag bool, clustName string, certFile string,
302	keyFile string) *ClOptions {
303	return &ClOptions{
304		DatastoreURL: datastoreURL,
305		CfgstoreURL:  cfgstoreURL,
306		AcctstoreURL: acctstoreURL,
307		NamespaceDef: namespace,
308		ReadMode:     readOnly,
309		SignReqd:     signature,
310		MetricsReqd:  metrics,
311		ReqCap:       reqCap,
312		NumThreads:   threads,
313		OrdLimit:     ordLim,
314		UpdLimit:     updLim,
315		HttpAddr:     http,
316		HttpsAddr:    https,
317		LoggerImpl:   loggerImpl,
318		DebugFlag:    debugFlag,
319		ClusterName:  clustName,
320		CertFile:     certFile,
321		KeyFile:      keyFile,
322	}
323}
324
325// Standard Standalone implementation - this can be used by all configstore implementations
326type StdStandalone struct {
327	configStore ConfigurationStore         `json:"-"`
328	dataStore   datastore.Datastore        `json:"-"`
329	acctStore   accounting.AccountingStore `json:"-"`
330	Vers        Version                    `json:"version"`
331}
332
333func NewStandalone(version Version, cs ConfigurationStore, ds datastore.Datastore, as accounting.AccountingStore) *StdStandalone {
334	return &StdStandalone{
335		configStore: cs,
336		dataStore:   ds,
337		acctStore:   as,
338		Vers:        version,
339	}
340}
341
342func (st *StdStandalone) Datastore() datastore.Datastore {
343	return st.dataStore
344}
345
346func (st *StdStandalone) AccountingStore() accounting.AccountingStore {
347	return st.acctStore
348}
349
350func (st *StdStandalone) ConfigurationStore() ConfigurationStore {
351	return st.configStore
352}
353
354func (st *StdStandalone) Version() Version {
355	return st.Vers
356}
357