1// Copyright 2016 Circonus, Inc. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Check bundle API support - Fetch, Create, Update, Delete, and Search
6// See: https://login.circonus.com/resources/api/calls/check_bundle
7
8package api
9
10import (
11	"encoding/json"
12	"fmt"
13	"net/url"
14	"regexp"
15
16	"github.com/circonus-labs/circonus-gometrics/api/config"
17)
18
19// CheckBundleMetric individual metric configuration
20type CheckBundleMetric struct {
21	Name   string   `json:"name"`             // string
22	Result *string  `json:"result,omitempty"` // string or null, NOTE not settable - return/information value only
23	Status string   `json:"status,omitempty"` // string
24	Tags   []string `json:"tags"`             // [] len >= 0
25	Type   string   `json:"type"`             // string
26	Units  *string  `json:"units,omitempty"`  // string or null
27
28}
29
30// CheckBundleConfig contains the check type specific configuration settings
31// as k/v pairs (see https://login.circonus.com/resources/api/calls/check_bundle
32// for the specific settings available for each distinct check type)
33type CheckBundleConfig map[config.Key]string
34
35// CheckBundle defines a check bundle. See https://login.circonus.com/resources/api/calls/check_bundle for more information.
36type CheckBundle struct {
37	Brokers            []string            `json:"brokers"`                            // [] len >= 0
38	Checks             []string            `json:"_checks,omitempty"`                  // [] len >= 0
39	CheckUUIDs         []string            `json:"_check_uuids,omitempty"`             // [] len >= 0
40	CID                string              `json:"_cid,omitempty"`                     // string
41	Config             CheckBundleConfig   `json:"config"`                             // NOTE contents of config are check type specific, map len >= 0
42	Created            uint                `json:"_created,omitempty"`                 // uint
43	DisplayName        string              `json:"display_name"`                       // string
44	LastModifedBy      string              `json:"_last_modifed_by,omitempty"`         // string
45	LastModified       uint                `json:"_last_modified,omitempty"`           // uint
46	MetricLimit        int                 `json:"metric_limit,omitempty"`             // int
47	Metrics            []CheckBundleMetric `json:"metrics"`                            // [] >= 0
48	Notes              *string             `json:"notes,omitempty"`                    // string or null
49	Period             uint                `json:"period,omitempty"`                   // uint
50	ReverseConnectURLs []string            `json:"_reverse_connection_urls,omitempty"` // [] len >= 0
51	Status             string              `json:"status,omitempty"`                   // string
52	Tags               []string            `json:"tags,omitempty"`                     // [] len >= 0
53	Target             string              `json:"target"`                             // string
54	Timeout            float32             `json:"timeout,omitempty"`                  // float32
55	Type               string              `json:"type"`                               // string
56}
57
58// NewCheckBundle returns new CheckBundle (with defaults, if applicable)
59func NewCheckBundle() *CheckBundle {
60	return &CheckBundle{
61		Config:      make(CheckBundleConfig, config.DefaultConfigOptionsSize),
62		MetricLimit: config.DefaultCheckBundleMetricLimit,
63		Period:      config.DefaultCheckBundlePeriod,
64		Timeout:     config.DefaultCheckBundleTimeout,
65		Status:      config.DefaultCheckBundleStatus,
66	}
67}
68
69// FetchCheckBundle retrieves check bundle with passed cid.
70func (a *API) FetchCheckBundle(cid CIDType) (*CheckBundle, error) {
71	if cid == nil || *cid == "" {
72		return nil, fmt.Errorf("Invalid check bundle CID [none]")
73	}
74
75	bundleCID := string(*cid)
76
77	matched, err := regexp.MatchString(config.CheckBundleCIDRegex, bundleCID)
78	if err != nil {
79		return nil, err
80	}
81	if !matched {
82		return nil, fmt.Errorf("Invalid check bundle CID [%v]", bundleCID)
83	}
84
85	result, err := a.Get(bundleCID)
86	if err != nil {
87		return nil, err
88	}
89
90	if a.Debug {
91		a.Log.Printf("[DEBUG] fetch check bundle, received JSON: %s", string(result))
92	}
93
94	checkBundle := &CheckBundle{}
95	if err := json.Unmarshal(result, checkBundle); err != nil {
96		return nil, err
97	}
98
99	return checkBundle, nil
100}
101
102// FetchCheckBundles retrieves all check bundles available to the API Token.
103func (a *API) FetchCheckBundles() (*[]CheckBundle, error) {
104	result, err := a.Get(config.CheckBundlePrefix)
105	if err != nil {
106		return nil, err
107	}
108
109	var checkBundles []CheckBundle
110	if err := json.Unmarshal(result, &checkBundles); err != nil {
111		return nil, err
112	}
113
114	return &checkBundles, nil
115}
116
117// UpdateCheckBundle updates passed check bundle.
118func (a *API) UpdateCheckBundle(cfg *CheckBundle) (*CheckBundle, error) {
119	if cfg == nil {
120		return nil, fmt.Errorf("Invalid check bundle config [nil]")
121	}
122
123	bundleCID := string(cfg.CID)
124
125	matched, err := regexp.MatchString(config.CheckBundleCIDRegex, bundleCID)
126	if err != nil {
127		return nil, err
128	}
129	if !matched {
130		return nil, fmt.Errorf("Invalid check bundle CID [%s]", bundleCID)
131	}
132
133	jsonCfg, err := json.Marshal(cfg)
134	if err != nil {
135		return nil, err
136	}
137
138	if a.Debug {
139		a.Log.Printf("[DEBUG] update check bundle, sending JSON: %s", string(jsonCfg))
140	}
141
142	result, err := a.Put(bundleCID, jsonCfg)
143	if err != nil {
144		return nil, err
145	}
146
147	checkBundle := &CheckBundle{}
148	if err := json.Unmarshal(result, checkBundle); err != nil {
149		return nil, err
150	}
151
152	return checkBundle, nil
153}
154
155// CreateCheckBundle creates a new check bundle (check).
156func (a *API) CreateCheckBundle(cfg *CheckBundle) (*CheckBundle, error) {
157	if cfg == nil {
158		return nil, fmt.Errorf("Invalid check bundle config [nil]")
159	}
160
161	jsonCfg, err := json.Marshal(cfg)
162	if err != nil {
163		return nil, err
164	}
165
166	if a.Debug {
167		a.Log.Printf("[DEBUG] create check bundle, sending JSON: %s", string(jsonCfg))
168	}
169
170	result, err := a.Post(config.CheckBundlePrefix, jsonCfg)
171	if err != nil {
172		return nil, err
173	}
174
175	checkBundle := &CheckBundle{}
176	if err := json.Unmarshal(result, checkBundle); err != nil {
177		return nil, err
178	}
179
180	return checkBundle, nil
181}
182
183// DeleteCheckBundle deletes passed check bundle.
184func (a *API) DeleteCheckBundle(cfg *CheckBundle) (bool, error) {
185	if cfg == nil {
186		return false, fmt.Errorf("Invalid check bundle config [nil]")
187	}
188	return a.DeleteCheckBundleByCID(CIDType(&cfg.CID))
189}
190
191// DeleteCheckBundleByCID deletes check bundle with passed cid.
192func (a *API) DeleteCheckBundleByCID(cid CIDType) (bool, error) {
193
194	if cid == nil || *cid == "" {
195		return false, fmt.Errorf("Invalid check bundle CID [none]")
196	}
197
198	bundleCID := string(*cid)
199
200	matched, err := regexp.MatchString(config.CheckBundleCIDRegex, bundleCID)
201	if err != nil {
202		return false, err
203	}
204	if !matched {
205		return false, fmt.Errorf("Invalid check bundle CID [%v]", bundleCID)
206	}
207
208	_, err = a.Delete(bundleCID)
209	if err != nil {
210		return false, err
211	}
212
213	return true, nil
214}
215
216// SearchCheckBundles returns check bundles matching the specified
217// search query and/or filter. If nil is passed for both parameters
218// all check bundles will be returned.
219func (a *API) SearchCheckBundles(searchCriteria *SearchQueryType, filterCriteria *map[string][]string) (*[]CheckBundle, error) {
220
221	q := url.Values{}
222
223	if searchCriteria != nil && *searchCriteria != "" {
224		q.Set("search", string(*searchCriteria))
225	}
226
227	if filterCriteria != nil && len(*filterCriteria) > 0 {
228		for filter, criteria := range *filterCriteria {
229			for _, val := range criteria {
230				q.Add(filter, val)
231			}
232		}
233	}
234
235	if q.Encode() == "" {
236		return a.FetchCheckBundles()
237	}
238
239	reqURL := url.URL{
240		Path:     config.CheckBundlePrefix,
241		RawQuery: q.Encode(),
242	}
243
244	resp, err := a.Get(reqURL.String())
245	if err != nil {
246		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
247	}
248
249	var results []CheckBundle
250	if err := json.Unmarshal(resp, &results); err != nil {
251		return nil, err
252	}
253
254	return &results, nil
255}
256