Skip to content
This repository has been archived by the owner on Feb 28, 2019. It is now read-only.

Commit

Permalink
Merge 733cec1 into bbbd8a4
Browse files Browse the repository at this point in the history
  • Loading branch information
xichen2020 committed Jan 11, 2018
2 parents bbbd8a4 + 733cec1 commit 48238e5
Show file tree
Hide file tree
Showing 6 changed files with 428 additions and 428 deletions.
197 changes: 197 additions & 0 deletions service/r2/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package r2

import (
"github.com/m3db/m3metrics/policy"
"github.com/m3db/m3metrics/rules"
)

type namespaceJSON struct {
ID string `json:"id" validate:"required"`
ForRuleSetVersion int `json:"forRuleSetVersion"`
}

func newNamespaceJSON(nv *rules.NamespaceView) namespaceJSON {
return namespaceJSON{
ID: nv.Name,
ForRuleSetVersion: nv.ForRuleSetVersion,
}
}

type namespacesJSON struct {
Version int `json:"version"`
Namespaces []namespaceJSON `json:"namespaces"`
}

func newNamespacesJSON(nss *rules.NamespacesView) namespacesJSON {
views := make([]namespaceJSON, len(nss.Namespaces))
for i, namespace := range nss.Namespaces {
views[i] = newNamespaceJSON(namespace)
}
return namespacesJSON{
Version: nss.Version,
Namespaces: views,
}
}

type mappingRuleJSON struct {
ID string `json:"id,omitempty"`
Name string `json:"name" validate:"required"`
CutoverMillis int64 `json:"cutoverMillis,omitempty"`
Filter string `json:"filter" validate:"required"`
Policies []policy.Policy `json:"policies" validate:"required"`
LastUpdatedBy string `json:"lastUpdatedBy"`
LastUpdatedAtMillis int64 `json:"lastUpdatedAtMillis"`
}

func (m mappingRuleJSON) mappingRuleView() *rules.MappingRuleView {
return &rules.MappingRuleView{
ID: m.ID,
Name: m.Name,
Filter: m.Filter,
Policies: m.Policies,
}
}

func newMappingRuleJSON(mrv *rules.MappingRuleView) mappingRuleJSON {
return mappingRuleJSON{
ID: mrv.ID,
Name: mrv.Name,
Filter: mrv.Filter,
Policies: mrv.Policies,
CutoverMillis: mrv.CutoverNanos / nanosPerMilli,
LastUpdatedBy: mrv.LastUpdatedBy,
LastUpdatedAtMillis: mrv.LastUpdatedAtNanos / nanosPerMilli,
}
}

type mappingRuleHistoryJSON struct {
MappingRules []mappingRuleJSON `json:"mappingRules"`
}

func newMappingRuleHistoryJSON(hist []*rules.MappingRuleView) mappingRuleHistoryJSON {
views := make([]mappingRuleJSON, len(hist))
for i, mappingRule := range hist {
views[i] = newMappingRuleJSON(mappingRule)
}
return mappingRuleHistoryJSON{MappingRules: views}
}

type rollupTargetJSON struct {
Name string `json:"name" validate:"required"`
Tags []string `json:"tags" validate:"required"`
Policies []policy.Policy `json:"policies" validate:"required"`
}

func (t rollupTargetJSON) rollupTargetView() rules.RollupTargetView {
return rules.RollupTargetView{
Name: t.Name,
Tags: t.Tags,
Policies: t.Policies,
}
}

func newRollupTargetJSON(t rules.RollupTargetView) rollupTargetJSON {
return rollupTargetJSON{
Name: t.Name,
Tags: t.Tags,
Policies: t.Policies,
}
}

type rollupRuleJSON struct {
ID string `json:"id,omitempty"`
Name string `json:"name" validate:"required"`
Filter string `json:"filter" validate:"required"`
Targets []rollupTargetJSON `json:"targets" validate:"required,dive,required"`
CutoverMillis int64 `json:"cutoverMillis,omitempty"`
LastUpdatedBy string `json:"lastUpdatedBy"`
LastUpdatedAtMillis int64 `json:"lastUpdatedAtMillis"`
}

func newRollupRuleJSON(rrv *rules.RollupRuleView) rollupRuleJSON {
targets := make([]rollupTargetJSON, len(rrv.Targets))
for i, t := range rrv.Targets {
targets[i] = newRollupTargetJSON(t)
}
return rollupRuleJSON{
ID: rrv.ID,
Name: rrv.Name,
Filter: rrv.Filter,
Targets: targets,
CutoverMillis: rrv.CutoverNanos / nanosPerMilli,
LastUpdatedBy: rrv.LastUpdatedBy,
LastUpdatedAtMillis: rrv.LastUpdatedAtNanos / nanosPerMilli,
}
}

func (r rollupRuleJSON) rollupRuleView() *rules.RollupRuleView {
targets := make([]rules.RollupTargetView, len(r.Targets))
for i, t := range r.Targets {
targets[i] = t.rollupTargetView()
}

return &rules.RollupRuleView{
ID: r.ID,
Name: r.Name,
Filter: r.Filter,
Targets: targets,
}
}

type rollupRuleHistoryJSON struct {
RollupRules []rollupRuleJSON `json:"rollupRules"`
}

func newRollupRuleHistoryJSON(hist []*rules.RollupRuleView) rollupRuleHistoryJSON {
views := make([]rollupRuleJSON, len(hist))
for i, rollupRule := range hist {
views[i] = newRollupRuleJSON(rollupRule)
}
return rollupRuleHistoryJSON{RollupRules: views}
}

type ruleSetJSON struct {
Namespace string `json:"id"`
Version int `json:"version"`
CutoverMillis int64 `json:"cutoverMillis"`
MappingRules []mappingRuleJSON `json:"mappingRules"`
RollupRules []rollupRuleJSON `json:"rollupRules"`
}

func newRuleSetJSON(latest *rules.RuleSetSnapshot) ruleSetJSON {
var mrJSON []mappingRuleJSON
for _, m := range latest.MappingRules {
mrJSON = append(mrJSON, newMappingRuleJSON(m))
}
var rrJSON []rollupRuleJSON
for _, r := range latest.RollupRules {
rrJSON = append(rrJSON, newRollupRuleJSON(r))
}
return ruleSetJSON{
Namespace: latest.Namespace,
Version: latest.Version,
CutoverMillis: latest.CutoverNanos / nanosPerMilli,
MappingRules: mrJSON,
RollupRules: rrJSON,
}
}
72 changes: 72 additions & 0 deletions service/r2/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package r2

import (
"fmt"
"net/http"

"github.com/m3db/m3ctl/auth"
"github.com/m3db/m3x/log"
)

type r2HandlerFunc func(http.ResponseWriter, *http.Request) error

type r2Handler struct {
logger log.Logger
auth auth.HTTPAuthService
}

func (h r2Handler) wrap(fn r2HandlerFunc) http.Handler {
f := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
h.handleError(w, err)
}
})
return h.auth.NewAuthHandler(f, writeAPIResponse)
}

func (h r2Handler) handleError(w http.ResponseWriter, opError error) {
h.logger.Errorf(opError.Error())

var err error
switch opError.(type) {
case conflictError:
err = writeAPIResponse(w, http.StatusConflict, opError.Error())
case badInputError:
err = writeAPIResponse(w, http.StatusBadRequest, opError.Error())
case versionError:
err = writeAPIResponse(w, http.StatusConflict, opError.Error())
case notFoundError:
err = writeAPIResponse(w, http.StatusNotFound, opError.Error())
case authError:
err = writeAPIResponse(w, http.StatusUnauthorized, opError.Error())
default:
err = writeAPIResponse(w, http.StatusInternalServerError, opError.Error())
}

// Getting here means that the error handling failed. Trying to convey what was supposed to happen.
if err != nil {
msg := fmt.Sprintf("Could not generate error response for: %s", opError.Error())
h.logger.Errorf(msg)
http.Error(w, msg, http.StatusInternalServerError)
}
}
83 changes: 83 additions & 0 deletions service/r2/io.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package r2

import (
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
"strings"

validator "gopkg.in/go-playground/validator.v9"
)

// TODO(dgromov): Make this return a list of validation errors
func parseRequest(s interface{}, body io.ReadCloser) error {
if err := json.NewDecoder(body).Decode(s); err != nil {
return NewBadInputError(fmt.Sprintf("Malformed Json: %s", err.Error()))
}

// Invoking the validation explictely to have control over the format of the error output.
validate := validator.New()
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
parts := strings.SplitN(fld.Tag.Get("json"), ",", 2)
if len(parts) > 0 {
return parts[0]
}
return fld.Name
})

var required []string
if err := validate.Struct(s); err != nil {
for _, e := range err.(validator.ValidationErrors) {
if e.ActualTag() == "required" {
required = append(required, e.Namespace())
}
}
}

if len(required) > 0 {
return NewBadInputError(fmt.Sprintf("Required: [%v]", strings.Join(required, ", ")))
}
return nil
}

func writeAPIResponse(w http.ResponseWriter, code int, msg string) error {
j, err := json.Marshal(apiResponse{Code: code, Message: msg})
if err != nil {
return err
}
return sendResponse(w, j, code)
}

func sendResponse(w http.ResponseWriter, data []byte, status int) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_, err := w.Write(data)
return err
}

type apiResponse struct {
Code int `json:"code"`
Message string `json:"message"`
}
Loading

0 comments on commit 48238e5

Please sign in to comment.