1// Copyright 2012-present Oliver Eilhard. All rights reserved. 2// Use of this source code is governed by a MIT-license. 3// See http://olivere.mit-license.org/license.txt for details. 4 5package elastic 6 7import ( 8 "context" 9 "fmt" 10 "net/url" 11 "strings" 12 13 "github.com/olivere/elastic/uritemplates" 14) 15 16// ClusterStateService allows to get a comprehensive state information of the whole cluster. 17// 18// See https://www.elastic.co/guide/en/elasticsearch/reference/6.7/cluster-state.html 19// for details. 20type ClusterStateService struct { 21 client *Client 22 pretty bool 23 indices []string 24 metrics []string 25 allowNoIndices *bool 26 expandWildcards string 27 flatSettings *bool 28 ignoreUnavailable *bool 29 local *bool 30 masterTimeout string 31} 32 33// NewClusterStateService creates a new ClusterStateService. 34func NewClusterStateService(client *Client) *ClusterStateService { 35 return &ClusterStateService{ 36 client: client, 37 indices: make([]string, 0), 38 metrics: make([]string, 0), 39 } 40} 41 42// Index is a list of index names. Use _all or an empty string to 43// perform the operation on all indices. 44func (s *ClusterStateService) Index(indices ...string) *ClusterStateService { 45 s.indices = append(s.indices, indices...) 46 return s 47} 48 49// Metric limits the information returned to the specified metric. 50// It can be one of: version, master_node, nodes, routing_table, metadata, 51// blocks, or customs. 52func (s *ClusterStateService) Metric(metrics ...string) *ClusterStateService { 53 s.metrics = append(s.metrics, metrics...) 54 return s 55} 56 57// AllowNoIndices indicates whether to ignore if a wildcard indices 58// expression resolves into no concrete indices. 59// (This includes `_all` string or when no indices have been specified). 60func (s *ClusterStateService) AllowNoIndices(allowNoIndices bool) *ClusterStateService { 61 s.allowNoIndices = &allowNoIndices 62 return s 63} 64 65// ExpandWildcards indicates whether to expand wildcard expression to 66// concrete indices that are open, closed or both.. 67func (s *ClusterStateService) ExpandWildcards(expandWildcards string) *ClusterStateService { 68 s.expandWildcards = expandWildcards 69 return s 70} 71 72// FlatSettings, when set, returns settings in flat format (default: false). 73func (s *ClusterStateService) FlatSettings(flatSettings bool) *ClusterStateService { 74 s.flatSettings = &flatSettings 75 return s 76} 77 78// IgnoreUnavailable indicates whether specified concrete indices should be 79// ignored when unavailable (missing or closed). 80func (s *ClusterStateService) IgnoreUnavailable(ignoreUnavailable bool) *ClusterStateService { 81 s.ignoreUnavailable = &ignoreUnavailable 82 return s 83} 84 85// Local indicates whether to return local information. When set, it does not 86// retrieve the state from master node (default: false). 87func (s *ClusterStateService) Local(local bool) *ClusterStateService { 88 s.local = &local 89 return s 90} 91 92// MasterTimeout specifies timeout for connection to master. 93func (s *ClusterStateService) MasterTimeout(masterTimeout string) *ClusterStateService { 94 s.masterTimeout = masterTimeout 95 return s 96} 97 98// Pretty indicates that the JSON response be indented and human readable. 99func (s *ClusterStateService) Pretty(pretty bool) *ClusterStateService { 100 s.pretty = pretty 101 return s 102} 103 104// buildURL builds the URL for the operation. 105func (s *ClusterStateService) buildURL() (string, url.Values, error) { 106 // Build URL 107 metrics := strings.Join(s.metrics, ",") 108 if metrics == "" { 109 metrics = "_all" 110 } 111 indices := strings.Join(s.indices, ",") 112 if indices == "" { 113 indices = "_all" 114 } 115 path, err := uritemplates.Expand("/_cluster/state/{metrics}/{indices}", map[string]string{ 116 "metrics": metrics, 117 "indices": indices, 118 }) 119 if err != nil { 120 return "", url.Values{}, err 121 } 122 123 // Add query string parameters 124 params := url.Values{} 125 if s.pretty { 126 params.Set("pretty", "true") 127 } 128 if s.allowNoIndices != nil { 129 params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices)) 130 } 131 if s.expandWildcards != "" { 132 params.Set("expand_wildcards", s.expandWildcards) 133 } 134 if s.flatSettings != nil { 135 params.Set("flat_settings", fmt.Sprintf("%v", *s.flatSettings)) 136 } 137 if s.ignoreUnavailable != nil { 138 params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable)) 139 } 140 if s.local != nil { 141 params.Set("local", fmt.Sprintf("%v", *s.local)) 142 } 143 if s.masterTimeout != "" { 144 params.Set("master_timeout", s.masterTimeout) 145 } 146 return path, params, nil 147} 148 149// Validate checks if the operation is valid. 150func (s *ClusterStateService) Validate() error { 151 return nil 152} 153 154// Do executes the operation. 155func (s *ClusterStateService) Do(ctx context.Context) (*ClusterStateResponse, error) { 156 // Check pre-conditions 157 if err := s.Validate(); err != nil { 158 return nil, err 159 } 160 161 // Get URL for request 162 path, params, err := s.buildURL() 163 if err != nil { 164 return nil, err 165 } 166 167 // Get HTTP response 168 res, err := s.client.PerformRequest(ctx, PerformRequestOptions{ 169 Method: "GET", 170 Path: path, 171 Params: params, 172 }) 173 if err != nil { 174 return nil, err 175 } 176 177 // Return operation response 178 ret := new(ClusterStateResponse) 179 if err := s.client.decoder.Decode(res.Body, ret); err != nil { 180 return nil, err 181 } 182 return ret, nil 183} 184 185// ClusterStateResponse is the response of ClusterStateService.Do. 186type ClusterStateResponse struct { 187 ClusterName string `json:"cluster_name"` 188 Version int64 `json:"version"` 189 StateUUID string `json:"state_uuid"` 190 MasterNode string `json:"master_node"` 191 Blocks map[string]*clusterBlocks `json:"blocks"` 192 Nodes map[string]*discoveryNode `json:"nodes"` 193 Metadata *clusterStateMetadata `json:"metadata"` 194 RoutingTable *clusterStateRoutingTable `json:"routing_table"` 195 RoutingNodes *clusterStateRoutingNode `json:"routing_nodes"` 196 Customs map[string]interface{} `json:"customs"` 197} 198 199type clusterBlocks struct { 200 Global map[string]*clusterBlock `json:"global"` // id -> cluster block 201 Indices map[string]*clusterBlock `json:"indices"` // index name -> cluster block 202} 203 204type clusterBlock struct { 205 Description string `json:"description"` 206 Retryable bool `json:"retryable"` 207 DisableStatePersistence bool `json:"disable_state_persistence"` 208 Levels []string `json:"levels"` 209} 210 211type clusterStateMetadata struct { 212 ClusterUUID string `json:"cluster_uuid"` 213 Templates map[string]*indexTemplateMetaData `json:"templates"` // template name -> index template metadata 214 Indices map[string]*indexMetaData `json:"indices"` // index name _> meta data 215 RoutingTable struct { 216 Indices map[string]*indexRoutingTable `json:"indices"` // index name -> routing table 217 } `json:"routing_table"` 218 RoutingNodes struct { 219 Unassigned []*shardRouting `json:"unassigned"` 220 Nodes []*shardRouting `json:"nodes"` 221 } `json:"routing_nodes"` 222 Customs map[string]interface{} `json:"customs"` 223} 224 225type discoveryNode struct { 226 Name string `json:"name"` // server name, e.g. "es1" 227 TransportAddress string `json:"transport_address"` // e.g. inet[/1.2.3.4:9300] 228 Attributes map[string]interface{} `json:"attributes"` // e.g. { "data": true, "master": true } 229} 230 231type clusterStateRoutingTable struct { 232 Indices map[string]interface{} `json:"indices"` 233} 234 235type clusterStateRoutingNode struct { 236 Unassigned []*shardRouting `json:"unassigned"` 237 // Node Id -> shardRouting 238 Nodes map[string][]*shardRouting `json:"nodes"` 239} 240 241type indexTemplateMetaData struct { 242 IndexPatterns []string `json:"index_patterns"` // e.g. ["store-*"] 243 Order int `json:"order"` 244 Settings map[string]interface{} `json:"settings"` // index settings 245 Mappings map[string]interface{} `json:"mappings"` // type name -> mapping 246} 247 248type indexMetaData struct { 249 State string `json:"state"` 250 Settings map[string]interface{} `json:"settings"` 251 Mappings map[string]interface{} `json:"mappings"` 252 Aliases []string `json:"aliases"` // e.g. [ "alias1", "alias2" ] 253} 254 255type indexRoutingTable struct { 256 Shards map[string]*shardRouting `json:"shards"` 257} 258 259type shardRouting struct { 260 State string `json:"state"` 261 Primary bool `json:"primary"` 262 Node string `json:"node"` 263 RelocatingNode string `json:"relocating_node"` 264 Shard int `json:"shard"` 265 Index string `json:"index"` 266 Version int64 `json:"version"` 267 RestoreSource *RestoreSource `json:"restore_source"` 268 AllocationId *allocationId `json:"allocation_id"` 269 UnassignedInfo *unassignedInfo `json:"unassigned_info"` 270} 271 272type RestoreSource struct { 273 Repository string `json:"repository"` 274 Snapshot string `json:"snapshot"` 275 Version string `json:"version"` 276 Index string `json:"index"` 277} 278 279type allocationId struct { 280 Id string `json:"id"` 281 RelocationId string `json:"relocation_id"` 282} 283 284type unassignedInfo struct { 285 Reason string `json:"reason"` 286 At string `json:"at"` 287 Details string `json:"details"` 288} 289