github.com/m3db/m3@v1.5.0/src/metrics/rules/namespace.go (about)

     1  // Copyright (c) 2017 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 rules
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  
    27  	"github.com/m3db/m3/src/metrics/generated/proto/rulepb"
    28  	"github.com/m3db/m3/src/metrics/rules/view"
    29  	xerrors "github.com/m3db/m3/src/x/errors"
    30  )
    31  
    32  var (
    33  	emptyNamespaceSnapshot NamespaceSnapshot
    34  	emptyNamespace         Namespace
    35  	emptyNamespaces        Namespaces
    36  
    37  	errNamespaceSnapshotIndexOutOfRange = errors.New("namespace snapshot idx out of range")
    38  	errNilNamespaceSnapshotProto        = errors.New("nil namespace snapshot proto")
    39  	errNilNamespaceProto                = errors.New("nil namespace proto")
    40  	errNilNamespacesProto               = errors.New("nil namespaces proto")
    41  	errNilNamespaceSnapshot             = errors.New("nil namespace snapshot")
    42  	errMultipleNamespaceMatches         = errors.New("more than one namespace match found")
    43  	errNamespaceNotFound                = errors.New("namespace not found")
    44  	errNamespaceNotTombstoned           = errors.New("namespace is not tombstoned")
    45  	errNamespaceAlreadyTombstoned       = errors.New("namespace is already tombstoned")
    46  	errNoNamespaceSnapshots             = errors.New("namespace has no snapshots")
    47  
    48  	namespaceActionErrorFmt = "cannot %s namespace %s"
    49  )
    50  
    51  // NamespaceSnapshot defines a namespace snapshot for which rules are defined.
    52  type NamespaceSnapshot struct {
    53  	forRuleSetVersion  int
    54  	tombstoned         bool
    55  	lastUpdatedAtNanos int64
    56  	lastUpdatedBy      string
    57  }
    58  
    59  func newNamespaceSnapshot(snapshot *rulepb.NamespaceSnapshot) (NamespaceSnapshot, error) {
    60  	if snapshot == nil {
    61  		return emptyNamespaceSnapshot, errNilNamespaceSnapshotProto
    62  	}
    63  	return NamespaceSnapshot{
    64  		forRuleSetVersion:  int(snapshot.ForRulesetVersion),
    65  		tombstoned:         snapshot.Tombstoned,
    66  		lastUpdatedAtNanos: snapshot.LastUpdatedAtNanos,
    67  		lastUpdatedBy:      snapshot.LastUpdatedBy,
    68  	}, nil
    69  }
    70  
    71  // ForRuleSetVersion is the ruleset version this namespace change is related to.
    72  func (s NamespaceSnapshot) ForRuleSetVersion() int { return s.forRuleSetVersion }
    73  
    74  // Tombstoned determines whether the namespace has been tombstoned.
    75  func (s NamespaceSnapshot) Tombstoned() bool { return s.tombstoned }
    76  
    77  // LastUpdatedAtNanos returns the time when the namespace is last updated in nanoseconds.
    78  func (s NamespaceSnapshot) LastUpdatedAtNanos() int64 { return s.lastUpdatedAtNanos }
    79  
    80  // LastUpdatedBy returns the user who last updated the namespace.
    81  func (s NamespaceSnapshot) LastUpdatedBy() string { return s.lastUpdatedBy }
    82  
    83  // Proto returns the given Namespace in protobuf form
    84  func (s NamespaceSnapshot) Proto() *rulepb.NamespaceSnapshot {
    85  	return &rulepb.NamespaceSnapshot{
    86  		ForRulesetVersion:  int32(s.forRuleSetVersion),
    87  		Tombstoned:         s.tombstoned,
    88  		LastUpdatedAtNanos: s.lastUpdatedAtNanos,
    89  		LastUpdatedBy:      s.lastUpdatedBy,
    90  	}
    91  }
    92  
    93  // Namespace stores namespace snapshots.
    94  type Namespace struct {
    95  	name      []byte
    96  	snapshots []NamespaceSnapshot
    97  }
    98  
    99  // newNamespace creates a new namespace.
   100  func newNamespace(namespace *rulepb.Namespace) (Namespace, error) {
   101  	if namespace == nil {
   102  		return emptyNamespace, errNilNamespaceProto
   103  	}
   104  	snapshots := make([]NamespaceSnapshot, 0, len(namespace.Snapshots))
   105  	for _, snapshot := range namespace.Snapshots {
   106  		s, err := newNamespaceSnapshot(snapshot)
   107  		if err != nil {
   108  			return emptyNamespace, err
   109  		}
   110  		snapshots = append(snapshots, s)
   111  	}
   112  	return Namespace{
   113  		name:      []byte(namespace.Name),
   114  		snapshots: snapshots,
   115  	}, nil
   116  }
   117  
   118  // NamespaceView returns the view representation of a namespace object.
   119  func (n Namespace) NamespaceView(snapshotIdx int) (view.Namespace, error) {
   120  	if snapshotIdx < 0 || snapshotIdx >= len(n.snapshots) {
   121  		return view.Namespace{}, errNamespaceSnapshotIndexOutOfRange
   122  	}
   123  	s := n.snapshots[snapshotIdx]
   124  	return view.Namespace{
   125  		ID:                  string(n.name),
   126  		ForRuleSetVersion:   s.forRuleSetVersion,
   127  		Tombstoned:          s.tombstoned,
   128  		LastUpdatedBy:       s.lastUpdatedBy,
   129  		LastUpdatedAtMillis: s.lastUpdatedAtNanos / nanosPerMilli,
   130  	}, nil
   131  }
   132  
   133  func (n Namespace) clone() Namespace {
   134  	name := make([]byte, len(n.name))
   135  	copy(name, n.name)
   136  	snapshots := make([]NamespaceSnapshot, len(n.snapshots))
   137  	copy(snapshots, n.snapshots)
   138  	return Namespace{
   139  		name:      name,
   140  		snapshots: snapshots,
   141  	}
   142  }
   143  
   144  // Name is the name of the namespace.
   145  func (n Namespace) Name() []byte { return n.name }
   146  
   147  // Snapshots return the namespace snapshots.
   148  func (n Namespace) Snapshots() []NamespaceSnapshot { return n.snapshots }
   149  
   150  // Proto returns the given Namespace in protobuf form
   151  func (n Namespace) Proto() (*rulepb.Namespace, error) {
   152  	if n.snapshots == nil {
   153  		return nil, errNilNamespaceSnapshot
   154  	}
   155  
   156  	res := &rulepb.Namespace{
   157  		Name: string(n.name),
   158  	}
   159  
   160  	snapshots := make([]*rulepb.NamespaceSnapshot, len(n.snapshots))
   161  	for i, s := range n.snapshots {
   162  		snapshots[i] = s.Proto()
   163  	}
   164  	res.Snapshots = snapshots
   165  
   166  	return res, nil
   167  }
   168  
   169  func (n *Namespace) markTombstoned(tombstonedRSVersion int, meta UpdateMetadata) error {
   170  	if n.Tombstoned() {
   171  		return errNamespaceAlreadyTombstoned
   172  	}
   173  	snapshot := NamespaceSnapshot{
   174  		forRuleSetVersion:  tombstonedRSVersion,
   175  		tombstoned:         true,
   176  		lastUpdatedAtNanos: meta.updatedAtNanos,
   177  		lastUpdatedBy:      meta.updatedBy,
   178  	}
   179  	n.snapshots = append(n.snapshots, snapshot)
   180  	return nil
   181  }
   182  
   183  func (n *Namespace) revive(meta UpdateMetadata) error {
   184  	if !n.Tombstoned() {
   185  		return errNamespaceNotTombstoned
   186  	}
   187  	if len(n.snapshots) == 0 {
   188  		return errNoNamespaceSnapshots
   189  	}
   190  
   191  	tombstonedRuleSetVersion := n.snapshots[len(n.snapshots)-1].forRuleSetVersion
   192  	// NB: The revived ruleset version is one after the tombstoned ruleset version.
   193  	snapshot := NamespaceSnapshot{
   194  		forRuleSetVersion:  tombstonedRuleSetVersion + 1,
   195  		tombstoned:         false,
   196  		lastUpdatedAtNanos: meta.updatedAtNanos,
   197  		lastUpdatedBy:      meta.updatedBy,
   198  	}
   199  	n.snapshots = append(n.snapshots, snapshot)
   200  	return nil
   201  }
   202  
   203  // Tombstoned returns the tombstoned state for a given namespace.
   204  func (n Namespace) Tombstoned() bool {
   205  	if len(n.snapshots) == 0 {
   206  		return true
   207  	}
   208  	return n.snapshots[len(n.snapshots)-1].tombstoned
   209  }
   210  
   211  // Namespaces store the list of namespaces for which rules are defined.
   212  type Namespaces struct {
   213  	version    int
   214  	namespaces []Namespace
   215  }
   216  
   217  // NewNamespaces creates new namespaces.
   218  func NewNamespaces(version int, namespaces *rulepb.Namespaces) (Namespaces, error) {
   219  	if namespaces == nil {
   220  		return emptyNamespaces, errNilNamespacesProto
   221  	}
   222  	nss := make([]Namespace, 0, len(namespaces.Namespaces))
   223  	for _, namespace := range namespaces.Namespaces {
   224  		ns, err := newNamespace(namespace)
   225  		if err != nil {
   226  			return emptyNamespaces, err
   227  		}
   228  		nss = append(nss, ns)
   229  	}
   230  	return Namespaces{
   231  		version:    version,
   232  		namespaces: nss,
   233  	}, nil
   234  }
   235  
   236  // NamespacesView returns a view representation of a given Namespaces object.
   237  func (nss Namespaces) NamespacesView() (view.Namespaces, error) {
   238  	namespaces := make([]view.Namespace, len(nss.namespaces))
   239  	for i, n := range nss.namespaces {
   240  		ns, err := n.NamespaceView(len(n.snapshots) - 1)
   241  		if err != nil {
   242  			return view.Namespaces{}, err
   243  		}
   244  		namespaces[i] = ns
   245  	}
   246  	return view.Namespaces{
   247  		Version:    nss.version,
   248  		Namespaces: namespaces,
   249  	}, nil
   250  }
   251  
   252  // Clone creates a deep copy of this Namespaces object.
   253  func (nss Namespaces) Clone() Namespaces {
   254  	namespaces := make([]Namespace, len(nss.namespaces))
   255  	for i, n := range nss.namespaces {
   256  		namespaces[i] = n.clone()
   257  	}
   258  	return Namespaces{
   259  		version:    nss.version,
   260  		namespaces: namespaces,
   261  	}
   262  }
   263  
   264  // Version returns the namespaces version.
   265  func (nss Namespaces) Version() int { return nss.version }
   266  
   267  // Namespaces returns the list of namespaces.
   268  func (nss Namespaces) Namespaces() []Namespace { return nss.namespaces }
   269  
   270  // Proto returns the given Namespaces slice in protobuf form.
   271  func (nss Namespaces) Proto() (*rulepb.Namespaces, error) {
   272  	res := &rulepb.Namespaces{}
   273  
   274  	namespaces := make([]*rulepb.Namespace, len(nss.namespaces))
   275  	for i, n := range nss.namespaces {
   276  		namespace, err := n.Proto()
   277  		if err != nil {
   278  			return nil, err
   279  		}
   280  		namespaces[i] = namespace
   281  	}
   282  	res.Namespaces = namespaces
   283  
   284  	return res, nil
   285  }
   286  
   287  // Namespace returns a namespace with a given name.
   288  func (nss *Namespaces) Namespace(name string) (*Namespace, error) {
   289  	var res *Namespace
   290  
   291  	for i, ns := range nss.namespaces {
   292  		if string(ns.name) != name {
   293  			continue
   294  		}
   295  
   296  		if res == nil {
   297  			res = &nss.namespaces[i]
   298  		} else {
   299  			return nil, errMultipleNamespaceMatches
   300  		}
   301  	}
   302  
   303  	if res == nil {
   304  		return nil, errNamespaceNotFound
   305  	}
   306  
   307  	return res, nil
   308  }
   309  
   310  // AddNamespace adds a new namespace to the namespaces structure and persists it.
   311  // This function returns a boolean indicating whether or not the namespace was revived.
   312  // The revived flag should be used to decided if the corresponding" ruleset should also
   313  // be revived.
   314  func (nss *Namespaces) AddNamespace(nsName string, meta UpdateMetadata) (bool, error) {
   315  	existing, err := nss.Namespace(nsName)
   316  	if err != nil && err != errNamespaceNotFound {
   317  		return false, xerrors.Wrap(err, fmt.Sprintf(namespaceActionErrorFmt, "add", nsName))
   318  	}
   319  
   320  	// Brand new namespace.
   321  	if err == errNamespaceNotFound {
   322  		ns := Namespace{
   323  			name: []byte(nsName),
   324  			snapshots: []NamespaceSnapshot{
   325  				NamespaceSnapshot{
   326  					forRuleSetVersion:  1,
   327  					tombstoned:         false,
   328  					lastUpdatedAtNanos: meta.updatedAtNanos,
   329  					lastUpdatedBy:      meta.updatedBy,
   330  				},
   331  			},
   332  		}
   333  
   334  		nss.namespaces = append(nss.namespaces, ns)
   335  		return false, nil
   336  	}
   337  
   338  	// Revive the namespace.
   339  	if err = existing.revive(meta); err != nil {
   340  		return false, xerrors.Wrap(err, fmt.Sprintf(namespaceActionErrorFmt, "revive", nsName))
   341  	}
   342  
   343  	return true, nil
   344  }
   345  
   346  // DeleteNamespace tombstones the given namespace mapping it to the next ruleset version.
   347  func (nss *Namespaces) DeleteNamespace(nsName string, currRuleSetVersion int, meta UpdateMetadata) error {
   348  	existing, err := nss.Namespace(nsName)
   349  	if err != nil {
   350  		return xerrors.Wrap(err, fmt.Sprintf(namespaceActionErrorFmt, "delete", nsName))
   351  	}
   352  
   353  	if err := existing.markTombstoned(currRuleSetVersion+1, meta); err != nil {
   354  		return xerrors.Wrap(err, fmt.Sprintf(namespaceActionErrorFmt, "delete", nsName))
   355  	}
   356  
   357  	return nil
   358  }