Skip to content
This repository has been archived by the owner on Nov 8, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1049 from obourdon/multi-metrics
Browse files Browse the repository at this point in the history
Fix for issue #1048: Invalid API response returned by mock collector
  • Loading branch information
tjmcs authored Jul 19, 2016
2 parents cdc8db0 + 30478b7 commit 5137f6c
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 19 deletions.
2 changes: 1 addition & 1 deletion cmd/snapctl/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ var (
},
{
Name: "get",
Usage: "get details on a single metric",
Usage: "get details on metric(s)",
Action: getMetric,
Flags: []cli.Flag{
flMetricVersion,
Expand Down
2 changes: 1 addition & 1 deletion cmd/snapctl/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ var (
// metric
flMetricVersion = cli.IntFlag{
Name: "metric-version, v",
Usage: "The metric version. Default (0) is latest",
Usage: "The metric version. 0 (default) returns all. -1 returns latest.",
}
flMetricNamespace = cli.StringFlag{
Name: "metric-namespace, m",
Expand Down
43 changes: 36 additions & 7 deletions cmd/snapctl/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"time"

"github.com/codegangsta/cli"
"github.com/intelsdi-x/snap/mgmt/rest/client"
"github.com/intelsdi-x/snap/mgmt/rest/rbody"
)

Expand Down Expand Up @@ -94,13 +95,7 @@ func listMetrics(ctx *cli.Context) error {
return nil
}

func getMetric(ctx *cli.Context) error {
if !ctx.IsSet("metric-namespace") {
return newUsageError("namespace is required\n\n", ctx)
}
ns := ctx.String("metric-namespace")
ver := ctx.Int("metric-version")
metric := pClient.GetMetric(ns, ver)
func printMetric(metric *client.GetMetricResult, idx int) error {
if metric.Err != nil {
return fmt.Errorf("%v", metric.Err)
}
Expand All @@ -119,6 +114,9 @@ func getMetric(ctx *cli.Context) error {

namespace := getNamespace(metric.Metric)

if idx > 0 {
fmt.Printf("\n")
}
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
printFields(w, false, 0, "NAMESPACE", "VERSION", "UNIT", "LAST ADVERTISED TIME", "DESCRIPTION")
printFields(w, false, 0, namespace, metric.Metric.Version, metric.Metric.Unit, time.Unix(metric.Metric.LastAdvertisedTimestamp, 0).Format(time.RFC1123), metric.Metric.Description)
Expand Down Expand Up @@ -150,7 +148,38 @@ func getMetric(ctx *cli.Context) error {
printFields(w, true, 6, rule.Name, rule.Type, rule.Default, rule.Required, rule.Minimum, rule.Maximum)
}
w.Flush()
return nil
}

func getMetric(ctx *cli.Context) error {
if !ctx.IsSet("metric-namespace") {
return newUsageError("namespace is required\n\n", ctx)
}
ns := ctx.String("metric-namespace")
ver := ctx.Int("metric-version")
metric := pClient.GetMetric(ns, ver)
switch mtype := metric.(type) {
case []*client.GetMetricResult:
// Multiple metrics
var merr error
for i, m := range metric.([]*client.GetMetricResult) {
err := printMetric(m, i)
if err != nil {
merr = err
}
}
if merr != nil {
return merr
}
case *client.GetMetricResult:
// Single metric
err := printMetric(metric.(*client.GetMetricResult), 0)
if err != nil {
return err
}
default:
return fmt.Errorf("Unexpected response type %T\n", mtype)
}
return nil
}

Expand Down
24 changes: 20 additions & 4 deletions mgmt/rest/client/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (c *Client) GetMetricCatalog() *GetMetricsResult {
// It returns the corresponding metric catalog if succeeded. Otherwise, an error is returned.
func (c *Client) FetchMetrics(ns string, ver int) *GetMetricsResult {
r := &GetMetricsResult{}
q := fmt.Sprintf("/metrics%s?ver=%d", ns, ver)
q := fmt.Sprintf("/metrics?ns=%s&ver=%d", ns, ver)
resp, err := c.do("GET", q, ContentTypeJSON)
if err != nil {
return &GetMetricsResult{Err: err}
Expand All @@ -80,7 +80,7 @@ func (c *Client) FetchMetrics(ns string, ver int) *GetMetricsResult {
// GetMetricVersions retrieves all versions of a metric at a given namespace.
func (c *Client) GetMetricVersions(ns string) *GetMetricsResult {
r := &GetMetricsResult{}
q := fmt.Sprintf("/metrics%s", ns)
q := fmt.Sprintf("/metrics?ns=%s", ns)
resp, err := c.do("GET", q, ContentTypeJSON)
if err != nil {
return &GetMetricsResult{Err: err}
Expand All @@ -100,9 +100,12 @@ func (c *Client) GetMetricVersions(ns string) *GetMetricsResult {

// GetMetric retrieves a metric at a given namespace and version.
// If the version is < 1, the latest version is returned.
func (c *Client) GetMetric(ns string, ver int) *GetMetricResult {
// Returns an interface as several return types are possible
// (array of metrics).
func (c *Client) GetMetric(ns string, ver int) interface{} {
r := &GetMetricResult{}
q := fmt.Sprintf("/metrics%s?ver=%d", ns, ver)

q := fmt.Sprintf("/metrics?ns=%s&ver=%d", ns, ver)
resp, err := c.do("GET", q, ContentTypeJSON)
if err != nil {
return &GetMetricResult{Err: err}
Expand All @@ -112,6 +115,10 @@ func (c *Client) GetMetric(ns string, ver int) *GetMetricResult {
case rbody.MetricReturnedType:
mc := resp.Body.(*rbody.MetricReturned)
r.Metric = mc.Metric
case rbody.MetricsReturnedType:
mc := resp.Body.(*rbody.MetricsReturned)
r := convertMetrics(mc)
return r
case rbody.ErrorType:
r.Err = resp.Body.(*rbody.Error)
default:
Expand Down Expand Up @@ -144,3 +151,12 @@ func convertCatalog(c *rbody.MetricsReturned) []*rbody.Metric {
}
return mci
}

func convertMetrics(c *rbody.MetricsReturned) []*GetMetricResult {
mci := make([]*GetMetricResult, len(*c))
for i, _ := range *c {
r := &GetMetricResult{Metric: &(*c)[i]}
mci[i] = r
}
return mci
}
44 changes: 39 additions & 5 deletions mgmt/rest/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ package rest
import (
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
"strings"

"github.com/julienschmidt/httprouter"

Expand All @@ -32,6 +34,38 @@ import (
)

func (s *Server) getMetrics(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
ver := 0 // 0: get all metrics

// If we are provided a parameter with the name 'ns' we need to
// perform a query
q := r.URL.Query()
v := q.Get("ver")
ns_query := q.Get("ns")
if ns_query != "" {
ver = 0 // 0: get all versions
if v != "" {
var err error
ver, err = strconv.Atoi(v)
if err != nil {
respond(400, rbody.FromError(err), w)
return
}
}
// strip the leading '/' and split on the remaining '/'
ns := strings.Split(strings.TrimLeft(ns_query, "/"), "/")
if ns[len(ns)-1] == "*" {
ns = ns[:len(ns)-1]
}

mets, err := s.mm.FetchMetrics(core.NewNamespace(ns...), ver)
if err != nil {
respond(404, rbody.FromError(err), w)
return
}
respondWithMetrics(r.Host, mets, w)
return
}

mets, err := s.mm.MetricCatalog()
if err != nil {
respond(500, rbody.FromError(err), w)
Expand Down Expand Up @@ -63,7 +97,7 @@ func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, para

if ns[len(ns)-1] == "*" {
if v == "" {
ver = -1
ver = 0 //return all metrics
} else {
ver, err = strconv.Atoi(v)
if err != nil {
Expand All @@ -81,14 +115,14 @@ func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, para
return
}

// If no version was given, get all that fall at this namespace.
// If no version was given, get all version that fall at this namespace.
if v == "" {
mts, err := s.mm.GetMetricVersions(core.NewNamespace(ns...))
mets, err := s.mm.FetchMetrics(core.NewNamespace(ns...), 0)
if err != nil {
respond(404, rbody.FromError(err), w)
return
}
respondWithMetrics(r.Host, mts, w)
respondWithMetrics(r.Host, mets, w)
return
}

Expand Down Expand Up @@ -175,7 +209,7 @@ func respondWithMetrics(host string, mets []core.CatalogedMetric, w http.Respons
}

func catalogedMetricURI(host string, mt core.CatalogedMetric) string {
return fmt.Sprintf("%s://%s/v1/metrics%s?ver=%d", protocolPrefix, host, mt.Namespace().String(), mt.Version())
return fmt.Sprintf("%s://%s/v1/metrics?ns=%s&ver=%d", protocolPrefix, host, url.QueryEscape(mt.Namespace().String()), mt.Version())
}

func getDynamicElements(ns core.Namespace, indexes []int) []rbody.DynamicElement {
Expand Down
2 changes: 1 addition & 1 deletion mgmt/rest/rbody/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func NewMetricsReturned() MetricsReturned {
}

func (m MetricsReturned) ResponseBodyMessage() string {
return "Metric"
return "Metrics returned"
}

func (m MetricsReturned) ResponseBodyType() string {
Expand Down
2 changes: 2 additions & 0 deletions mgmt/rest/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ limitations under the License.
package rest

import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
Expand Down Expand Up @@ -510,6 +511,7 @@ func respond(code int, b rbody.Body, w http.ResponseWriter) {
if err != nil {
panic(err)
}
j = bytes.Replace(j, []byte("\\u0026"), []byte("&"), -1)
fmt.Fprint(w, string(j))
}

Expand Down

0 comments on commit 5137f6c

Please sign in to comment.