github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/namespace/common.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 "errors" 25 "fmt" 26 "net/http" 27 "path" 28 "time" 29 30 clusterclient "github.com/m3db/m3/src/cluster/client" 31 "github.com/m3db/m3/src/cluster/kv" 32 "github.com/m3db/m3/src/cluster/placementhandler/handleroptions" 33 nsproto "github.com/m3db/m3/src/dbnode/generated/proto/namespace" 34 "github.com/m3db/m3/src/dbnode/namespace" 35 "github.com/m3db/m3/src/query/api/v1/options" 36 "github.com/m3db/m3/src/query/storage/m3" 37 "github.com/m3db/m3/src/query/util/queryhttp" 38 "github.com/m3db/m3/src/x/instrument" 39 xhttp "github.com/m3db/m3/src/x/net/http" 40 ) 41 42 const ( 43 // M3DBServiceName is the service name for M3DB. 44 M3DBServiceName = "m3db" 45 46 // ServicesPathName is the services part of the API path. 47 ServicesPathName = "services" 48 49 // M3DBNodeNamespacesKey is the KV key that holds namespaces. 50 M3DBNodeNamespacesKey = "m3db.node.namespaces" 51 52 // NamespacePathName is the namespace part of the API path. 53 NamespacePathName = "namespace" 54 55 // SchemaPathName is the schema part of the API path. 56 SchemaPathName = "schema" 57 ) 58 59 var ( 60 // M3DBServiceNamespacePathName is the M3DB service namespace API path. 61 M3DBServiceNamespacePathName = path.Join(ServicesPathName, M3DBServiceName, NamespacePathName) 62 // M3DBServiceSchemaPathName is the M3DB service schema API path. 63 M3DBServiceSchemaPathName = path.Join(ServicesPathName, M3DBServiceName, SchemaPathName) 64 65 errNamespaceNotFound = xhttp.NewError(errors.New("unable to find a namespace with specified name"), http.StatusNotFound) 66 ) 67 68 // Handler represents a generic handler for namespace endpoints. 69 type Handler struct { 70 // This is used by other namespace Handlers 71 // nolint: structcheck 72 client clusterclient.Client 73 clusters m3.Clusters 74 instrumentOpts instrument.Options 75 } 76 77 // Metadata returns the current metadata in the given store and its version 78 func Metadata(store kv.Store) ([]namespace.Metadata, int, error) { 79 value, err := store.Get(M3DBNodeNamespacesKey) 80 if err != nil { 81 // From the perspective of namespace handlers, having had no metadata 82 // set at all is semantically the same as having an empty slice of 83 // metadata and is not a real error state. 84 if err == kv.ErrNotFound { 85 return []namespace.Metadata{}, 0, nil 86 } 87 88 return nil, -1, err 89 } 90 91 var protoRegistry nsproto.Registry 92 if err := value.Unmarshal(&protoRegistry); err != nil { 93 return nil, -1, fmt.Errorf("unable to parse value, err: %v", err) 94 } 95 96 nsMap, err := namespace.FromProto(protoRegistry) 97 if err != nil { 98 return nil, -1, err 99 } 100 101 return nsMap.Metadatas(), value.Version(), nil 102 } 103 104 type applyMiddlewareFn func( 105 svc handleroptions.ServiceNameAndDefaults, 106 w http.ResponseWriter, 107 r *http.Request, 108 ) 109 110 // RegisterRoutes registers the namespace routes. 111 func RegisterRoutes( 112 r *queryhttp.EndpointRegistry, 113 client clusterclient.Client, 114 clusters m3.Clusters, 115 defaults []handleroptions.ServiceOptionsDefault, 116 instrumentOpts instrument.Options, 117 namespaceValidator options.NamespaceValidator, 118 ) error { 119 applyMiddleware := func( 120 f func(svc handleroptions.ServiceNameAndDefaults, 121 w http.ResponseWriter, r *http.Request), 122 defaults []handleroptions.ServiceOptionsDefault, 123 ) http.Handler { 124 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 125 svc := handleroptions.ServiceNameAndDefaults{ 126 ServiceName: handleroptions.M3DBServiceName, 127 Defaults: defaults, 128 } 129 f(svc, w, r) 130 }) 131 } 132 133 // Get M3DB namespaces. 134 if err := r.Register(queryhttp.RegisterOptions{ 135 Path: M3DBGetURL, 136 Handler: applyMiddleware(NewGetHandler(client, instrumentOpts).ServeHTTP, defaults), 137 Methods: []string{GetHTTPMethod}, 138 }); err != nil { 139 return err 140 } 141 142 // Add M3DB namespaces. 143 if err := r.Register(queryhttp.RegisterOptions{ 144 Path: M3DBAddURL, 145 Handler: applyMiddleware(NewAddHandler(client, instrumentOpts, namespaceValidator).ServeHTTP, defaults), 146 Methods: []string{AddHTTPMethod}, 147 }); err != nil { 148 return err 149 } 150 151 // Update M3DB namespaces. 152 if err := r.Register(queryhttp.RegisterOptions{ 153 Path: M3DBUpdateURL, 154 Handler: applyMiddleware(NewUpdateHandler(client, instrumentOpts).ServeHTTP, defaults), 155 Methods: []string{UpdateHTTPMethod}, 156 }); err != nil { 157 return err 158 } 159 160 // Delete M3DB namespaces. 161 if err := r.Register(queryhttp.RegisterOptions{ 162 Path: M3DBDeleteURL, 163 Handler: applyMiddleware(NewDeleteHandler(client, instrumentOpts).ServeHTTP, defaults), 164 Methods: []string{DeleteHTTPMethod}, 165 }); err != nil { 166 return err 167 } 168 169 // Deploy M3DB schemas. 170 if err := r.Register(queryhttp.RegisterOptions{ 171 Path: M3DBSchemaURL, 172 Handler: applyMiddleware(NewSchemaHandler(client, instrumentOpts).ServeHTTP, defaults), 173 Methods: []string{SchemaDeployHTTPMethod}, 174 }); err != nil { 175 return err 176 } 177 178 // Reset M3DB schemas. 179 if err := r.Register(queryhttp.RegisterOptions{ 180 Path: M3DBSchemaURL, 181 Handler: applyMiddleware(NewSchemaResetHandler(client, instrumentOpts).ServeHTTP, defaults), 182 Methods: []string{DeleteHTTPMethod}, 183 }); err != nil { 184 return err 185 } 186 187 // Mark M3DB namespace as ready. 188 if err := r.Register(queryhttp.RegisterOptions{ 189 Path: M3DBReadyURL, 190 Handler: applyMiddleware(NewReadyHandler(client, clusters, instrumentOpts).ServeHTTP, defaults), 191 Methods: []string{ReadyHTTPMethod}, 192 }); err != nil { 193 return err 194 } 195 196 return nil 197 } 198 199 func validateNamespaceAggregationOptions(mds []namespace.Metadata) error { 200 resolutionRetentionMap := make(map[resolutionRetentionKey]bool, len(mds)) 201 202 for _, md := range mds { 203 aggOpts := md.Options().AggregationOptions() 204 if aggOpts == nil || len(aggOpts.Aggregations()) == 0 { 205 continue 206 } 207 208 retention := md.Options().RetentionOptions().RetentionPeriod() 209 for _, agg := range aggOpts.Aggregations() { 210 if agg.Aggregated { 211 key := resolutionRetentionKey{ 212 retention: retention, 213 resolution: agg.Attributes.Resolution, 214 } 215 216 if resolutionRetentionMap[key] { 217 return fmt.Errorf("resolution and retention combination must be unique. "+ 218 "namespace with resolution=%v retention=%v already exists", key.resolution, key.retention) 219 } 220 resolutionRetentionMap[key] = true 221 } 222 } 223 } 224 225 return nil 226 } 227 228 type resolutionRetentionKey struct { 229 resolution time.Duration 230 retention time.Duration 231 }