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