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// Metric Cluster API support - Fetch, Create, Update, Delete, and Search 6// See: https://login.circonus.com/resources/api/calls/metric_cluster 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// MetricQuery object 20type MetricQuery struct { 21 Query string `json:"query"` 22 Type string `json:"type"` 23} 24 25// MetricCluster defines a metric cluster. See https://login.circonus.com/resources/api/calls/metric_cluster for more information. 26type MetricCluster struct { 27 CID string `json:"_cid,omitempty"` // string 28 Description string `json:"description"` // string 29 MatchingMetrics []string `json:"_matching_metrics,omitempty"` // [] len >= 1 (result info only, if query has extras - cannot be set) 30 MatchingUUIDMetrics map[string][]string `json:"_matching_uuid_metrics,omitempty"` // [] len >= 1 (result info only, if query has extras - cannot be set) 31 Name string `json:"name"` // string 32 Queries []MetricQuery `json:"queries"` // [] len >= 1 33 Tags []string `json:"tags"` // [] len >= 0 34} 35 36// NewMetricCluster returns a new MetricCluster (with defaults, if applicable) 37func NewMetricCluster() *MetricCluster { 38 return &MetricCluster{} 39} 40 41// FetchMetricCluster retrieves metric cluster with passed cid. 42func (a *API) FetchMetricCluster(cid CIDType, extras string) (*MetricCluster, error) { 43 if cid == nil || *cid == "" { 44 return nil, fmt.Errorf("Invalid metric cluster CID [none]") 45 } 46 47 clusterCID := string(*cid) 48 49 matched, err := regexp.MatchString(config.MetricClusterCIDRegex, clusterCID) 50 if err != nil { 51 return nil, err 52 } 53 if !matched { 54 return nil, fmt.Errorf("Invalid metric cluster CID [%s]", clusterCID) 55 } 56 57 reqURL := url.URL{ 58 Path: clusterCID, 59 } 60 61 extra := "" 62 switch extras { 63 case "metrics": 64 extra = "_matching_metrics" 65 case "uuids": 66 extra = "_matching_uuid_metrics" 67 } 68 69 if extra != "" { 70 q := url.Values{} 71 q.Set("extra", extra) 72 reqURL.RawQuery = q.Encode() 73 } 74 75 result, err := a.Get(reqURL.String()) 76 if err != nil { 77 return nil, err 78 } 79 80 if a.Debug { 81 a.Log.Printf("[DEBUG] fetch metric cluster, received JSON: %s", string(result)) 82 } 83 84 cluster := &MetricCluster{} 85 if err := json.Unmarshal(result, cluster); err != nil { 86 return nil, err 87 } 88 89 return cluster, nil 90} 91 92// FetchMetricClusters retrieves all metric clusters available to API Token. 93func (a *API) FetchMetricClusters(extras string) (*[]MetricCluster, error) { 94 reqURL := url.URL{ 95 Path: config.MetricClusterPrefix, 96 } 97 98 extra := "" 99 switch extras { 100 case "metrics": 101 extra = "_matching_metrics" 102 case "uuids": 103 extra = "_matching_uuid_metrics" 104 } 105 106 if extra != "" { 107 q := url.Values{} 108 q.Set("extra", extra) 109 reqURL.RawQuery = q.Encode() 110 } 111 112 result, err := a.Get(reqURL.String()) 113 if err != nil { 114 return nil, err 115 } 116 117 var clusters []MetricCluster 118 if err := json.Unmarshal(result, &clusters); err != nil { 119 return nil, err 120 } 121 122 return &clusters, nil 123} 124 125// UpdateMetricCluster updates passed metric cluster. 126func (a *API) UpdateMetricCluster(cfg *MetricCluster) (*MetricCluster, error) { 127 if cfg == nil { 128 return nil, fmt.Errorf("Invalid metric cluster config [nil]") 129 } 130 131 clusterCID := string(cfg.CID) 132 133 matched, err := regexp.MatchString(config.MetricClusterCIDRegex, clusterCID) 134 if err != nil { 135 return nil, err 136 } 137 if !matched { 138 return nil, fmt.Errorf("Invalid metric cluster CID [%s]", clusterCID) 139 } 140 141 jsonCfg, err := json.Marshal(cfg) 142 if err != nil { 143 return nil, err 144 } 145 146 if a.Debug { 147 a.Log.Printf("[DEBUG] update metric cluster, sending JSON: %s", string(jsonCfg)) 148 } 149 150 result, err := a.Put(clusterCID, jsonCfg) 151 if err != nil { 152 return nil, err 153 } 154 155 cluster := &MetricCluster{} 156 if err := json.Unmarshal(result, cluster); err != nil { 157 return nil, err 158 } 159 160 return cluster, nil 161} 162 163// CreateMetricCluster creates a new metric cluster. 164func (a *API) CreateMetricCluster(cfg *MetricCluster) (*MetricCluster, error) { 165 if cfg == nil { 166 return nil, fmt.Errorf("Invalid metric cluster config [nil]") 167 } 168 169 jsonCfg, err := json.Marshal(cfg) 170 if err != nil { 171 return nil, err 172 } 173 174 if a.Debug { 175 a.Log.Printf("[DEBUG] create metric cluster, sending JSON: %s", string(jsonCfg)) 176 } 177 178 result, err := a.Post(config.MetricClusterPrefix, jsonCfg) 179 if err != nil { 180 return nil, err 181 } 182 183 cluster := &MetricCluster{} 184 if err := json.Unmarshal(result, cluster); err != nil { 185 return nil, err 186 } 187 188 return cluster, nil 189} 190 191// DeleteMetricCluster deletes passed metric cluster. 192func (a *API) DeleteMetricCluster(cfg *MetricCluster) (bool, error) { 193 if cfg == nil { 194 return false, fmt.Errorf("Invalid metric cluster config [nil]") 195 } 196 return a.DeleteMetricClusterByCID(CIDType(&cfg.CID)) 197} 198 199// DeleteMetricClusterByCID deletes metric cluster with passed cid. 200func (a *API) DeleteMetricClusterByCID(cid CIDType) (bool, error) { 201 if cid == nil || *cid == "" { 202 return false, fmt.Errorf("Invalid metric cluster CID [none]") 203 } 204 205 clusterCID := string(*cid) 206 207 matched, err := regexp.MatchString(config.MetricClusterCIDRegex, clusterCID) 208 if err != nil { 209 return false, err 210 } 211 if !matched { 212 return false, fmt.Errorf("Invalid metric cluster CID [%s]", clusterCID) 213 } 214 215 _, err = a.Delete(clusterCID) 216 if err != nil { 217 return false, err 218 } 219 220 return true, nil 221} 222 223// SearchMetricClusters returns metric clusters matching the specified 224// search query and/or filter. If nil is passed for both parameters 225// all metric clusters will be returned. 226func (a *API) SearchMetricClusters(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]MetricCluster, error) { 227 q := url.Values{} 228 229 if searchCriteria != nil && *searchCriteria != "" { 230 q.Set("search", string(*searchCriteria)) 231 } 232 233 if filterCriteria != nil && len(*filterCriteria) > 0 { 234 for filter, criteria := range *filterCriteria { 235 for _, val := range criteria { 236 q.Add(filter, val) 237 } 238 } 239 } 240 241 if q.Encode() == "" { 242 return a.FetchMetricClusters("") 243 } 244 245 reqURL := url.URL{ 246 Path: config.MetricClusterPrefix, 247 RawQuery: q.Encode(), 248 } 249 250 result, err := a.Get(reqURL.String()) 251 if err != nil { 252 return nil, fmt.Errorf("[ERROR] API call error %+v", err) 253 } 254 255 var clusters []MetricCluster 256 if err := json.Unmarshal(result, &clusters); err != nil { 257 return nil, err 258 } 259 260 return &clusters, nil 261} 262