github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/namespace/delete.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  	"encoding/json"
    25  	"errors"
    26  	"fmt"
    27  	"net/http"
    28  	"path"
    29  	"strings"
    30  
    31  	clusterclient "github.com/m3db/m3/src/cluster/client"
    32  	"github.com/m3db/m3/src/cluster/placementhandler/handleroptions"
    33  	"github.com/m3db/m3/src/dbnode/namespace"
    34  	"github.com/m3db/m3/src/query/api/v1/route"
    35  	"github.com/m3db/m3/src/query/util/logging"
    36  	xerrors "github.com/m3db/m3/src/x/errors"
    37  	"github.com/m3db/m3/src/x/instrument"
    38  	xhttp "github.com/m3db/m3/src/x/net/http"
    39  
    40  	"github.com/gorilla/mux"
    41  	"go.uber.org/zap"
    42  )
    43  
    44  const (
    45  	namespaceIDVar = "id"
    46  
    47  	// DeleteHTTPMethod is the HTTP method used with this resource.
    48  	DeleteHTTPMethod = http.MethodDelete
    49  )
    50  
    51  var (
    52  	// M3DBDeleteURL is the url for the M3DB namespace delete handler.
    53  	M3DBDeleteURL = path.Join(
    54  		route.Prefix,
    55  		M3DBServiceNamespacePathName,
    56  		fmt.Sprintf("{%s}", namespaceIDVar),
    57  	)
    58  )
    59  
    60  var (
    61  	errEmptyID = xerrors.NewInvalidParamsError(errors.New("must specify namespace ID to delete"))
    62  )
    63  
    64  // DeleteHandler is the handler for namespace deletes.
    65  type DeleteHandler Handler
    66  
    67  // NewDeleteHandler returns a new instance of DeleteHandler.
    68  func NewDeleteHandler(
    69  	client clusterclient.Client,
    70  	instrumentOpts instrument.Options,
    71  ) *DeleteHandler {
    72  	return &DeleteHandler{
    73  		client:         client,
    74  		instrumentOpts: instrumentOpts,
    75  	}
    76  }
    77  
    78  func (h *DeleteHandler) ServeHTTP(
    79  	svc handleroptions.ServiceNameAndDefaults,
    80  	w http.ResponseWriter,
    81  	r *http.Request,
    82  ) {
    83  	ctx := r.Context()
    84  	logger := logging.WithContext(ctx, h.instrumentOpts)
    85  	id := strings.TrimSpace(mux.Vars(r)[namespaceIDVar])
    86  	if id == "" {
    87  		logger.Error("no namespace ID to delete", zap.Error(errEmptyID))
    88  		xhttp.WriteError(w, errEmptyID)
    89  		return
    90  	}
    91  
    92  	opts := handleroptions.NewServiceOptions(svc, r.Header, nil)
    93  	err := h.Delete(id, opts)
    94  	if err != nil {
    95  		logger.Error("unable to delete namespace", zap.Error(err))
    96  		xhttp.WriteError(w, err)
    97  		return
    98  	}
    99  
   100  	json.NewEncoder(w).Encode(struct {
   101  		Deleted bool `json:"deleted"`
   102  	}{
   103  		Deleted: true,
   104  	})
   105  }
   106  
   107  // Delete deletes a namespace.
   108  func (h *DeleteHandler) Delete(id string, opts handleroptions.ServiceOptions) error {
   109  	store, err := h.client.Store(opts.KVOverrideOptions())
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	metadatas, version, err := Metadata(store)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	mdIdx := -1
   120  	for idx, md := range metadatas {
   121  		if md.ID().String() == id {
   122  			mdIdx = idx
   123  			break
   124  		}
   125  	}
   126  
   127  	if mdIdx == -1 {
   128  		return errNamespaceNotFound
   129  	}
   130  
   131  	// If metadatas are empty, remove the key
   132  	if len(metadatas) == 1 {
   133  		if _, err = store.Delete(M3DBNodeNamespacesKey); err != nil {
   134  			return fmt.Errorf("unable to delete kv key: %v", err)
   135  		}
   136  
   137  		return nil
   138  	}
   139  
   140  	// Replace the index where we found the metadata with the last element, then truncate
   141  	metadatas[mdIdx] = metadatas[len(metadatas)-1]
   142  	metadatas = metadatas[:len(metadatas)-1]
   143  
   144  	// Update namespace map and set kv
   145  	nsMap, err := namespace.NewMap(metadatas)
   146  	if err != nil {
   147  		return fmt.Errorf("failed to delete namespace: %v", err)
   148  	}
   149  
   150  	protoRegistry, err := namespace.ToProto(nsMap)
   151  	if err != nil {
   152  		return fmt.Errorf("failed to delete namespace: %v", err)
   153  	}
   154  
   155  	_, err = store.CheckAndSet(M3DBNodeNamespacesKey, version, protoRegistry)
   156  	if err != nil {
   157  		return fmt.Errorf("failed to delete namespace: %v", err)
   158  	}
   159  
   160  	return nil
   161  }