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  }