1// Copyright 2012-2015 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	"encoding/json"
9	"fmt"
10	"net/url"
11	"strings"
12
13	"gopkg.in/olivere/elastic.v2/uritemplates"
14)
15
16// ClusterHealthService allows to get the status of the cluster.
17// It is documented at http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.4/cluster-health.html.
18type ClusterHealthService struct {
19	client                  *Client
20	pretty                  bool
21	indices                 []string
22	waitForStatus           string
23	level                   string
24	local                   *bool
25	masterTimeout           string
26	timeout                 string
27	waitForActiveShards     *int
28	waitForNodes            string
29	waitForRelocatingShards *int
30}
31
32// NewClusterHealthService creates a new ClusterHealthService.
33func NewClusterHealthService(client *Client) *ClusterHealthService {
34	return &ClusterHealthService{client: client, indices: make([]string, 0)}
35}
36
37// Index limits the information returned to a specific index.
38func (s *ClusterHealthService) Index(index string) *ClusterHealthService {
39	s.indices = make([]string, 0)
40	s.indices = append(s.indices, index)
41	return s
42}
43
44// Indices limits the information returned to specific indices.
45func (s *ClusterHealthService) Indices(indices ...string) *ClusterHealthService {
46	s.indices = make([]string, 0)
47	s.indices = append(s.indices, indices...)
48	return s
49}
50
51// MasterTimeout specifies an explicit operation timeout for connection to master node.
52func (s *ClusterHealthService) MasterTimeout(masterTimeout string) *ClusterHealthService {
53	s.masterTimeout = masterTimeout
54	return s
55}
56
57// Timeout specifies an explicit operation timeout.
58func (s *ClusterHealthService) Timeout(timeout string) *ClusterHealthService {
59	s.timeout = timeout
60	return s
61}
62
63// WaitForActiveShards can be used to wait until the specified number of shards are active.
64func (s *ClusterHealthService) WaitForActiveShards(waitForActiveShards int) *ClusterHealthService {
65	s.waitForActiveShards = &waitForActiveShards
66	return s
67}
68
69// WaitForNodes can be used to wait until the specified number of nodes are available.
70func (s *ClusterHealthService) WaitForNodes(waitForNodes string) *ClusterHealthService {
71	s.waitForNodes = waitForNodes
72	return s
73}
74
75// WaitForRelocatingShards can be used to wait until the specified number of relocating shards is finished.
76func (s *ClusterHealthService) WaitForRelocatingShards(waitForRelocatingShards int) *ClusterHealthService {
77	s.waitForRelocatingShards = &waitForRelocatingShards
78	return s
79}
80
81// WaitForStatus can be used to wait until the cluster is in a specific state.
82// Valid values are: green, yellow, or red.
83func (s *ClusterHealthService) WaitForStatus(waitForStatus string) *ClusterHealthService {
84	s.waitForStatus = waitForStatus
85	return s
86}
87
88// Level specifies the level of detail for returned information.
89func (s *ClusterHealthService) Level(level string) *ClusterHealthService {
90	s.level = level
91	return s
92}
93
94// Local indicates whether to return local information. If it is true,
95// we do not retrieve the state from master node (default: false).
96func (s *ClusterHealthService) Local(local bool) *ClusterHealthService {
97	s.local = &local
98	return s
99}
100
101// buildURL builds the URL for the operation.
102func (s *ClusterHealthService) buildURL() (string, url.Values, error) {
103	// Build URL
104	path, err := uritemplates.Expand("/_cluster/health/{index}", map[string]string{
105		"index": strings.Join(s.indices, ","),
106	})
107	if err != nil {
108		return "", url.Values{}, err
109	}
110
111	// Add query string parameters
112	params := url.Values{}
113	if s.waitForRelocatingShards != nil {
114		params.Set("wait_for_relocating_shards", fmt.Sprintf("%d", *s.waitForRelocatingShards))
115	}
116	if s.waitForStatus != "" {
117		params.Set("wait_for_status", s.waitForStatus)
118	}
119	if s.level != "" {
120		params.Set("level", s.level)
121	}
122	if s.local != nil {
123		params.Set("local", fmt.Sprintf("%v", *s.local))
124	}
125	if s.masterTimeout != "" {
126		params.Set("master_timeout", s.masterTimeout)
127	}
128	if s.timeout != "" {
129		params.Set("timeout", s.timeout)
130	}
131	if s.waitForActiveShards != nil {
132		params.Set("wait_for_active_shards", fmt.Sprintf("%d", *s.waitForActiveShards))
133	}
134	if s.waitForNodes != "" {
135		params.Set("wait_for_nodes", s.waitForNodes)
136	}
137
138	return path, params, nil
139}
140
141// Validate checks if the operation is valid.
142func (s *ClusterHealthService) Validate() error {
143	return nil
144}
145
146// Do executes the operation.
147func (s *ClusterHealthService) Do() (*ClusterHealthResponse, error) {
148	// Check pre-conditions
149	if err := s.Validate(); err != nil {
150		return nil, err
151	}
152
153	// Get URL for request
154	path, params, err := s.buildURL()
155	if err != nil {
156		return nil, err
157	}
158
159	// Get HTTP response
160	res, err := s.client.PerformRequest("GET", path, params, nil)
161	if err != nil {
162		return nil, err
163	}
164
165	// Return operation response
166	resp := new(ClusterHealthResponse)
167	if err := json.Unmarshal(res.Body, resp); err != nil {
168		return nil, err
169	}
170	return resp, nil
171}
172
173// ClusterHealthResponse is the response of ClusterHealthService.Do.
174type ClusterHealthResponse struct {
175	ClusterName          string `json:"cluster_name"`
176	Status               string `json:"status"`
177	TimedOut             bool   `json:"timed_out"`
178	NumberOfNodes        int    `json:"number_of_nodes"`
179	NumberOfDataNodes    int    `json:"number_of_data_nodes"`
180	ActivePrimaryShards  int    `json:"active_primary_shards"`
181	ActiveShards         int    `json:"active_shards"`
182	RelocatingShards     int    `json:"relocating_shards"`
183	InitializingShards   int    `json:"initializing_shards"`
184	UnassignedShards     int    `json:"unassigned_shards"`
185	NumberOfPendingTasks int    `json:"number_of_pending_tasks"`
186}
187