github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/namespace/add.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package namespace 22 23 import ( 24 "bytes" 25 "fmt" 26 "net/http" 27 "path" 28 29 clusterclient "github.com/m3db/m3/src/cluster/client" 30 "github.com/m3db/m3/src/cluster/placementhandler/handleroptions" 31 nsproto "github.com/m3db/m3/src/dbnode/generated/proto/namespace" 32 "github.com/m3db/m3/src/dbnode/namespace" 33 "github.com/m3db/m3/src/query/api/v1/options" 34 "github.com/m3db/m3/src/query/api/v1/route" 35 "github.com/m3db/m3/src/query/api/v1/validators" 36 "github.com/m3db/m3/src/query/generated/proto/admin" 37 "github.com/m3db/m3/src/query/util/logging" 38 xerrors "github.com/m3db/m3/src/x/errors" 39 "github.com/m3db/m3/src/x/instrument" 40 xhttp "github.com/m3db/m3/src/x/net/http" 41 42 "github.com/gogo/protobuf/jsonpb" 43 "go.uber.org/zap" 44 ) 45 46 var ( 47 // M3DBAddURL is the url for the M3DB namespace add handler. 48 M3DBAddURL = path.Join(route.Prefix, M3DBServiceNamespacePathName) 49 50 // AddHTTPMethod is the HTTP method used with this resource. 51 AddHTTPMethod = http.MethodPost 52 ) 53 54 // AddHandler is the handler for namespace adds. 55 type AddHandler struct { 56 Handler 57 58 validator options.NamespaceValidator 59 } 60 61 // NewAddHandler returns a new instance of AddHandler. 62 func NewAddHandler( 63 client clusterclient.Client, 64 instrumentOpts instrument.Options, 65 validator options.NamespaceValidator, 66 ) *AddHandler { 67 return &AddHandler{ 68 Handler: Handler{ 69 client: client, 70 instrumentOpts: instrumentOpts, 71 }, 72 validator: validator, 73 } 74 } 75 76 func (h *AddHandler) ServeHTTP( 77 svc handleroptions.ServiceNameAndDefaults, 78 w http.ResponseWriter, 79 r *http.Request, 80 ) { 81 ctx := r.Context() 82 logger := logging.WithContext(ctx, h.instrumentOpts) 83 84 md, rErr := h.parseRequest(r) 85 if rErr != nil { 86 logger.Error("unable to parse request", zap.Error(rErr)) 87 xhttp.WriteError(w, rErr) 88 return 89 } 90 91 opts := handleroptions.NewServiceOptions(svc, r.Header, nil) 92 nsRegistry, err := h.Add(md, opts) 93 if err != nil { 94 if err == validators.ErrNamespaceExists { 95 logger.Error("namespace already exists", zap.Error(err)) 96 xhttp.WriteError(w, xhttp.NewError(err, http.StatusConflict)) 97 return 98 } 99 100 logger.Error("unable to add namespace", zap.Error(err)) 101 xhttp.WriteError(w, err) 102 return 103 } 104 105 resp := &admin.NamespaceGetResponse{ 106 Registry: &nsRegistry, 107 } 108 109 xhttp.WriteProtoMsgJSONResponse(w, resp, logger) 110 } 111 112 func (h *AddHandler) parseRequest(r *http.Request) (*admin.NamespaceAddRequest, error) { 113 defer r.Body.Close() // nolint:errcheck 114 rBody, err := xhttp.DurationToNanosBytes(r.Body) 115 if err != nil { 116 return nil, xerrors.NewInvalidParamsError(err) 117 } 118 119 addReq := new(admin.NamespaceAddRequest) 120 if err := jsonpb.Unmarshal(bytes.NewReader(rBody), addReq); err != nil { 121 return nil, xerrors.NewInvalidParamsError(err) 122 } 123 124 return addReq, nil 125 } 126 127 // Add adds a namespace. 128 func (h *AddHandler) Add( 129 addReq *admin.NamespaceAddRequest, 130 opts handleroptions.ServiceOptions, 131 ) (nsproto.Registry, error) { 132 var emptyReg nsproto.Registry 133 134 md, err := namespace.ToMetadata(addReq.Name, addReq.Options) 135 if err != nil { 136 return emptyReg, xerrors.NewInvalidParamsError(fmt.Errorf("bad namespace metadata: %v", err)) 137 } 138 139 store, err := h.client.Store(opts.KVOverrideOptions()) 140 if err != nil { 141 return emptyReg, err 142 } 143 144 currentMetadata, version, err := Metadata(store) 145 if err != nil { 146 return emptyReg, err 147 } 148 149 if err := h.validator.ValidateNewNamespace(md, currentMetadata); err != nil { 150 if err == validators.ErrNamespaceExists { 151 return emptyReg, err 152 } 153 return emptyReg, xerrors.NewInvalidParamsError(err) 154 } 155 156 newMDs := append(currentMetadata, md) 157 if err = validateNamespaceAggregationOptions(newMDs); err != nil { 158 return emptyReg, xerrors.NewInvalidParamsError(err) 159 } 160 161 nsMap, err := namespace.NewMap(newMDs) 162 if err != nil { 163 return emptyReg, xerrors.NewInvalidParamsError(err) 164 } 165 166 protoRegistry, err := namespace.ToProto(nsMap) 167 if err != nil { 168 return emptyReg, fmt.Errorf("error constructing namespace protobuf: %v", err) 169 } 170 171 _, err = store.CheckAndSet(M3DBNodeNamespacesKey, version, protoRegistry) 172 if err != nil { 173 return emptyReg, fmt.Errorf("failed to add namespace: %v", err) 174 } 175 176 return *protoRegistry, nil 177 }