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}
83
84type Standalone interface {
85	Datastore() datastore.Datastore              // The Datastore used by all Query Nodes in the cluster
86	AccountingStore() accounting.AccountingStore // The AccountingStore used by all Query Nodes in the cluster
87	ConfigurationStore() ConfigurationStore      // The ConfigurationStore used by all Query Nodes in the cluster
88	Version() Version                            // Logical version of the software that the QueryNodes in the cluster are running
89}
90
91// QueryNode is the configuration for a single instance of a Query Engine.
92type QueryNode interface {
93	Cluster() Cluster          // The Cluster that this QueryNode belongs to
94	Name() string              // Name of this QueryNode (unique within the cluster)
95	QueryEndpoint() string     // Endpoint for serving N1QL queries
96	ClusterEndpoint() string   // Endpoint for serving admin commands
97	QuerySecure() string       // Endpoint for serving secure N1QL queries
98	ClusterSecure() string     // Endpoint for serving secure admin commands
99	Standalone() Standalone    // The QueryNode's configuration when unclustered
100	Options() QueryNodeOptions // The command line options the query node was started with
101}
102
103type QueryNodeOptions interface {
104	Datastore() string       // Datastore address
105	Configstore() string     // Configstore address
106	Accountingstore() string // Accountingstore address
107	Namespace() string       //default namespace
108	Readonly() bool          // Read-only mode
109	Signature() bool         // Whether to provide Signature
110	Metrics() bool           // Whether to provide Metrics
111	RequestCap() int         // Max number of queued requests
112	Threads() int            // Thread count
113	OrderLimit() int         // Max LIMIT for ORDER BY clauses
114	UpdateLimit() int        // Max LIMIT for data modification statements
115	Http() string            // HTTP service address
116	Https() string           // HTTPS service address
117	Certfile() string        // HTTPS certificate file
118	Keyfile() string         // HTTPS private key file
119	Logger() string          // Name of Logger implementation
120	Debug() bool             // Debug mode
121	Cluster() string         // Name of the cluster to join
122}
123
124// ConfigurationManager is the interface for managing cluster lifecycles -
125// addition and removal of clusters from the ConfigurationStore.
126type ConfigurationManager interface {
127	// The ConfigurationStore that this ConfigurationManager is managing
128	ConfigurationStore() ConfigurationStore
129
130	// Add a cluster to the configuration
131	// Possible reasons for error:
132	//	- Configuration contains a Cluster with the same identity
133	// Returns updated Cluster if no error (Cluster is now part of the ConfigurationStore)
134	AddCluster(c Cluster) (Cluster, errors.Error)
135
136	// Remove a cluster from the configuration
137	// Possible reasons for error:
138	//	- Cluster is not empty (contains one or more QueryNodes)
139	// Returns true if no error (Cluster is no longer in the ConfigurationStore)
140	RemoveCluster(c Cluster) (bool, errors.Error)
141
142	// Remove the named cluster from the configuration
143	// Possible reasons for error:
144	//	- Configuration does not have a cluster with the given id
145	//	- Cluster is not empty (contains one or more QueryNodes)
146	RemoveClusterByName(name string) (bool, errors.Error)
147
148	// The clusters in the configuration
149	GetClusters() ([]Cluster, errors.Error)
150}
151
152// ClusterManager is the interface the actions that can be done to a Cluster;
153// it is intended to support the lifecycle of QueryNodes - addition and removal of
154// QueryNodes from a Cluster.
155type ClusterManager interface {
156	// The Cluster that this ClusterManager is managing
157	Cluster() Cluster
158
159	// Add the given QueryNode to the Cluster
160	// Possible reasons for error:
161	//	- Cluster contains a QueryNode with the same identity
162	//	- Version incompatibility
163	//	- Given QueryNode is using a different Datastore
164	//	- Given QueryNode is not in standalone mode
165	// Returns the updated QueryNode if no error (cluster mode, connected to Cluster)
166	AddQueryNode(n QueryNode) (QueryNode, errors.Error)
167
168	// Remove the given QueryNode from the Cluster
169	// Possible reasons for error:
170	//	- Cluster does not contain the given QueryNode
171	//	- Given QueryNode is running in standalone mode
172	// Returns the updated QueryNode if no error (standalone mode, no cluster id)
173	RemoveQueryNode(n QueryNode) (QueryNode, errors.Error)
174
175	// Remove the QueryNode with the given id from the Cluster
176	// Possible reasons for error:
177	//	-- Cluster does not contain a QueryNode with the given id
178	// Returns the updated QueryNode if no error (standalone mode, no cluster id)
179	RemoveQueryNodeByName(name string) (QueryNode, errors.Error)
180
181	// Return the QueryNodes in the Cluster
182	GetQueryNodes() ([]QueryNode, errors.Error)
183}
184
185// Standard Version implementation - this can be used by all configstore implementations
186type StdVersion struct {
187	VersionString string
188}
189
190func NewVersion(version string) *StdVersion {
191	return &StdVersion{
192		VersionString: version,
193	}
194}
195
196func (st *StdVersion) String() string {
197	return st.VersionString
198}
199
200func (st *StdVersion) Compatible(v Version) bool {
201	return v.String() == st.String()
202}
203
204// Standard QueryNodeOptions implementation - this can be used by all configstore implementations
205type ClOptions struct {
206	DatastoreURL string `json:"datastore"`
207	CfgstoreURL  string `json:"configstore"`
208	AcctstoreURL string `json:"acctstore"`
209	NamespaceDef string `json:"namespace"`
210	ReadMode     bool   `json:"readonly"`
211	SignReqd     bool   `json:"readonly"`
212	MetricsReqd  bool   `json:"metrics"`
213	ReqCap       int    `json:"requestcap"`
214	NumThreads   int    `json:"threads"`
215	OrdLimit     int    `json:"orderlimit"`
216	UpdLimit     int    `json:"updatelimit"`
217	HttpAddr     string `json:"http"`
218	HttpsAddr    string `json:"https"`
219	LoggerImpl   string `json:"logger"`
220	DebugFlag    bool   `json:"debug"`
221	ClusterName  string `json:"cluster"`
222	CertFile     string `json:"certfile"`
223	KeyFile      string `json:"keyfile"`
224}
225
226func (c *ClOptions) Datastore() string {
227	return c.DatastoreURL
228}
229
230func (c *ClOptions) Logger() string {
231	return c.LoggerImpl
232}
233
234func (c *ClOptions) Debug() bool {
235	return c.DebugFlag
236}
237
238func (c *ClOptions) Cluster() string {
239	return c.ClusterName
240}
241
242func (c *ClOptions) Configstore() string {
243	return c.CfgstoreURL
244}
245
246func (c *ClOptions) Accountingstore() string {
247	return c.AcctstoreURL
248}
249
250func (c *ClOptions) Namespace() string {
251	return c.NamespaceDef
252}
253
254func (c *ClOptions) Readonly() bool {
255	return c.ReadMode
256}
257
258func (c *ClOptions) Signature() bool {
259	return c.SignReqd
260}
261
262func (c *ClOptions) Metrics() bool {
263	return c.MetricsReqd
264}
265
266func (c *ClOptions) RequestCap() int {
267	return c.ReqCap
268}
269
270func (c *ClOptions) Threads() int {
271	return c.NumThreads
272}
273
274func (c *ClOptions) OrderLimit() int {
275	return c.OrdLimit
276}
277
278func (c *ClOptions) UpdateLimit() int {
279	return c.UpdLimit
280}
281
282func (c *ClOptions) Http() string {
283	return c.HttpAddr
284}
285
286func (c *ClOptions) Https() string {
287	return c.HttpsAddr
288}
289
290func (c *ClOptions) Certfile() string {
291	return c.CertFile
292}
293
294func (c *ClOptions) Keyfile() string {
295	return c.KeyFile
296}
297
298func NewOptions(datastoreURL string, cfgstoreURL string, acctstoreURL string, namespace string,
299	readOnly bool, signature bool, metrics bool, reqCap int, threads int, ordLim int, updLim int,
300	http string, https string, loggerImpl string, debugFlag bool, clustName string, certFile string,
301	keyFile string) *ClOptions {
302	return &ClOptions{
303		DatastoreURL: datastoreURL,
304		CfgstoreURL:  cfgstoreURL,
305		AcctstoreURL: acctstoreURL,
306		NamespaceDef: namespace,
307		ReadMode:     readOnly,
308		SignReqd:     signature,
309		MetricsReqd:  metrics,
310		ReqCap:       reqCap,
311		NumThreads:   threads,
312		OrdLimit:     ordLim,
313		UpdLimit:     updLim,
314		HttpAddr:     http,
315		HttpsAddr:    https,
316		LoggerImpl:   loggerImpl,
317		DebugFlag:    debugFlag,
318		ClusterName:  clustName,
319		CertFile:     certFile,
320		KeyFile:      keyFile,
321	}
322}
323
324// Standard Standalone implementation - this can be used by all configstore implementations
325type StdStandalone struct {
326	configStore ConfigurationStore         `json:"-"`
327	dataStore   datastore.Datastore        `json:"-"`
328	acctStore   accounting.AccountingStore `json:"-"`
329	Vers        Version                    `json:"version"`
330}
331
332func NewStandalone(version Version, cs ConfigurationStore, ds datastore.Datastore, as accounting.AccountingStore) *StdStandalone {
333	return &StdStandalone{
334		configStore: cs,
335		dataStore:   ds,
336		acctStore:   as,
337		Vers:        version,
338	}
339}
340
341func (st *StdStandalone) Datastore() datastore.Datastore {
342	return st.dataStore
343}
344
345func (st *StdStandalone) AccountingStore() accounting.AccountingStore {
346	return st.acctStore
347}
348
349func (st *StdStandalone) ConfigurationStore() ConfigurationStore {
350	return st.configStore
351}
352
353func (st *StdStandalone) Version() Version {
354	return st.Vers
355}
356