github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/namespace/schema.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 "fmt" 25 "net/http" 26 "path" 27 28 clusterclient "github.com/m3db/m3/src/cluster/client" 29 "github.com/m3db/m3/src/cluster/kv" 30 "github.com/m3db/m3/src/cluster/placementhandler/handleroptions" 31 "github.com/m3db/m3/src/dbnode/namespace/kvadmin" 32 "github.com/m3db/m3/src/query/api/v1/route" 33 "github.com/m3db/m3/src/query/generated/proto/admin" 34 "github.com/m3db/m3/src/query/util/logging" 35 xerrors "github.com/m3db/m3/src/x/errors" 36 "github.com/m3db/m3/src/x/instrument" 37 xhttp "github.com/m3db/m3/src/x/net/http" 38 39 "github.com/gogo/protobuf/jsonpb" 40 "go.uber.org/zap" 41 ) 42 43 var ( 44 // M3DBSchemaURL is the url for the M3DB schema handler. 45 M3DBSchemaURL = path.Join(route.Prefix, M3DBServiceSchemaPathName) 46 47 // SchemaDeployHTTPMethod is the HTTP method used to append to this resource. 48 SchemaDeployHTTPMethod = http.MethodPost 49 ) 50 51 // SchemaHandler is the handler for namespace schema upserts. 52 type SchemaHandler Handler 53 54 // For unit test purpose. 55 var newAdminService = kvadmin.NewAdminService 56 57 // NewSchemaHandler returns a new instance of SchemaHandler. 58 func NewSchemaHandler( 59 client clusterclient.Client, 60 instrumentOpts instrument.Options, 61 ) *SchemaHandler { 62 return &SchemaHandler{ 63 client: client, 64 instrumentOpts: instrumentOpts, 65 } 66 } 67 68 func (h *SchemaHandler) ServeHTTP( 69 svc handleroptions.ServiceNameAndDefaults, 70 w http.ResponseWriter, 71 r *http.Request, 72 ) { 73 ctx := r.Context() 74 logger := logging.WithContext(ctx, h.instrumentOpts) 75 76 md, rErr := h.parseRequest(r) 77 if rErr != nil { 78 logger.Error("unable to parse request", zap.Error(rErr)) 79 xhttp.WriteError(w, rErr) 80 return 81 } 82 83 opts := handleroptions.NewServiceOptions(svc, r.Header, nil) 84 resp, err := h.Add(md, opts) 85 if err != nil { 86 if err == kv.ErrNotFound || xerrors.InnerError(err) == kv.ErrNotFound { 87 logger.Error("namespaces metadata key does not exist", zap.Error(err)) 88 xhttp.WriteError(w, err) 89 return 90 } 91 if err == kvadmin.ErrNamespaceNotFound || xerrors.InnerError(err) == kvadmin.ErrNamespaceNotFound { 92 logger.Error("namespace does not exist", zap.Error(err)) 93 xhttp.WriteError(w, xhttp.NewError(err, http.StatusNotFound)) 94 return 95 } 96 97 logger.Error("unable to deploy schema to namespace", zap.Error(err)) 98 xhttp.WriteError(w, err) 99 return 100 } 101 102 xhttp.WriteProtoMsgJSONResponse(w, &resp, logger) 103 } 104 105 func (h *SchemaHandler) parseRequest(r *http.Request) (*admin.NamespaceSchemaAddRequest, error) { 106 defer r.Body.Close() 107 108 var schemaAddReq admin.NamespaceSchemaAddRequest 109 if err := jsonpb.Unmarshal(r.Body, &schemaAddReq); err != nil { 110 return nil, xerrors.NewInvalidParamsError(err) 111 } 112 return &schemaAddReq, nil 113 } 114 115 // Add adds schema to an existing namespace. 116 func (h *SchemaHandler) Add( 117 addReq *admin.NamespaceSchemaAddRequest, 118 opts handleroptions.ServiceOptions, 119 ) (admin.NamespaceSchemaAddResponse, error) { 120 var emptyRep = admin.NamespaceSchemaAddResponse{} 121 122 store, err := h.client.Store(opts.KVOverrideOptions()) 123 if err != nil { 124 return emptyRep, err 125 } 126 127 schemaAdmin := newAdminService(store, M3DBNodeNamespacesKey, nil) 128 deployID, err := schemaAdmin.DeploySchema(addReq.Name, addReq.ProtoName, addReq.MsgName, addReq.ProtoMap) 129 if err != nil { 130 return emptyRep, err 131 } 132 return admin.NamespaceSchemaAddResponse{DeployID: deployID}, nil 133 } 134 135 // SchemaResetHandler is the handler for namespace schema reset. 136 type SchemaResetHandler Handler 137 138 // NewSchemaResetHandler returns a new instance of SchemaHandler. 139 func NewSchemaResetHandler( 140 client clusterclient.Client, 141 instrumentOpts instrument.Options, 142 ) *SchemaResetHandler { 143 return &SchemaResetHandler{ 144 client: client, 145 instrumentOpts: instrumentOpts, 146 } 147 } 148 149 func (h *SchemaResetHandler) ServeHTTP( 150 svc handleroptions.ServiceNameAndDefaults, 151 w http.ResponseWriter, 152 r *http.Request, 153 ) { 154 ctx := r.Context() 155 logger := logging.WithContext(ctx, h.instrumentOpts) 156 157 md, rErr := h.parseRequest(r) 158 if rErr != nil { 159 logger.Error("unable to parse request", zap.Error(rErr)) 160 xhttp.WriteError(w, rErr) 161 return 162 } 163 164 opts := handleroptions.NewServiceOptions(svc, r.Header, nil) 165 resp, err := h.Reset(md, opts) 166 if err != nil { 167 if err == kv.ErrNotFound || xerrors.InnerError(err) == kv.ErrNotFound { 168 logger.Error("namespaces metadata key does not exist", zap.Error(err)) 169 xhttp.WriteError(w, err) 170 return 171 } 172 if err == kvadmin.ErrNamespaceNotFound || xerrors.InnerError(err) == kvadmin.ErrNamespaceNotFound { 173 logger.Error("namespace does not exist", zap.Error(err)) 174 xhttp.WriteError(w, xhttp.NewError(err, http.StatusNotFound)) 175 return 176 } 177 178 logger.Error("unable to reset schema for namespace", zap.Error(err)) 179 xhttp.WriteError(w, err) 180 return 181 } 182 183 xhttp.WriteProtoMsgJSONResponse(w, resp, logger) 184 } 185 186 func (h *SchemaResetHandler) parseRequest(r *http.Request) (*admin.NamespaceSchemaResetRequest, error) { 187 defer r.Body.Close() 188 189 var schemaResetReq admin.NamespaceSchemaResetRequest 190 if err := jsonpb.Unmarshal(r.Body, &schemaResetReq); err != nil { 191 return nil, xerrors.NewInvalidParamsError(err) 192 } 193 return &schemaResetReq, nil 194 } 195 196 // Reset resets schema for an existing namespace. 197 func (h *SchemaResetHandler) Reset( 198 addReq *admin.NamespaceSchemaResetRequest, 199 opts handleroptions.ServiceOptions, 200 ) (*admin.NamespaceSchemaResetResponse, error) { 201 var emptyRep = admin.NamespaceSchemaResetResponse{} 202 if !opts.Force { 203 err := fmt.Errorf("CAUTION! Reset schema will prevent proto-enabled namespace from loading, proceed if you know what you are doing, please retry with force set to true") 204 return &emptyRep, xerrors.NewInvalidParamsError(err) 205 } 206 207 store, err := h.client.Store(opts.KVOverrideOptions()) 208 if err != nil { 209 return &emptyRep, err 210 } 211 212 schemaAdmin := newAdminService(store, M3DBNodeNamespacesKey, nil) 213 err = schemaAdmin.ResetSchema(addReq.Name) 214 if err != nil { 215 return &emptyRep, err 216 } 217 return &emptyRep, nil 218 }