github.com/m3db/m3@v1.5.0/src/ctl/service/r2/store/stub/store.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 stub
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/ctl/service/r2"
    29  	r2store "github.com/m3db/m3/src/ctl/service/r2/store"
    30  	"github.com/m3db/m3/src/metrics/aggregation"
    31  	"github.com/m3db/m3/src/metrics/pipeline"
    32  	"github.com/m3db/m3/src/metrics/policy"
    33  	"github.com/m3db/m3/src/metrics/rules/view"
    34  	"github.com/m3db/m3/src/metrics/rules/view/changes"
    35  	"github.com/m3db/m3/src/metrics/x/bytes"
    36  	"github.com/m3db/m3/src/x/instrument"
    37  
    38  	"github.com/pborman/uuid"
    39  )
    40  
    41  type mappingRuleHistories map[string][]view.MappingRule
    42  type rollupRuleHistories map[string][]view.RollupRule
    43  
    44  type stubData struct {
    45  	Namespaces        view.Namespaces
    46  	ErrorNamespace    string
    47  	ConflictNamespace string
    48  	RuleSets          map[string]view.RuleSet
    49  	MappingHistory    map[string]mappingRuleHistories
    50  	RollupHistory     map[string]rollupRuleHistories
    51  }
    52  
    53  var (
    54  	errNotImplemented = errors.New("not implemented")
    55  	cutoverMillis     = time.Now().UnixNano() / int64(time.Millisecond/time.Nanosecond)
    56  )
    57  
    58  // Operator contains the data necessary to implement stubbed out implementations for various r2 operations.
    59  type store struct {
    60  	data  *stubData
    61  	iOpts instrument.Options
    62  }
    63  
    64  // NewStore creates a new stub
    65  func NewStore(iOpts instrument.Options) (r2store.Store, error) {
    66  	dummyData, err := buildDummyData()
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	return &store{data: &dummyData, iOpts: iOpts}, err
    72  }
    73  
    74  func buildDummyData() (stubData, error) {
    75  	rollup1, err := pipeline.NewRollupOp(
    76  		pipeline.GroupByRollupType,
    77  		"testTarget",
    78  		[]string{"tag1", "tag2"},
    79  		aggregation.MustCompressTypes(aggregation.Min),
    80  	)
    81  	if err != nil {
    82  		return stubData{}, err
    83  	}
    84  	rollup2, err := pipeline.NewRollupOp(
    85  		pipeline.GroupByRollupType,
    86  		"testTarget",
    87  		[]string{"tag1", "tag2"},
    88  		aggregation.MustCompressTypes(aggregation.Min),
    89  	)
    90  	if err != nil {
    91  		return stubData{}, err
    92  	}
    93  	rollup3, err := pipeline.NewRollupOp(
    94  		pipeline.GroupByRollupType,
    95  		"testTarget",
    96  		[]string{"tag1", "tag2"},
    97  		aggregation.MustCompressTypes(aggregation.Min, aggregation.Max),
    98  	)
    99  	if err != nil {
   100  		return stubData{}, err
   101  	}
   102  	rollup4, err := pipeline.NewRollupOp(
   103  		pipeline.GroupByRollupType,
   104  		"testTarget",
   105  		[]string{"tag1", "tag2"},
   106  		aggregation.MustCompressTypes(aggregation.P999),
   107  	)
   108  	if err != nil {
   109  		return stubData{}, err
   110  	}
   111  	rollup5, err := pipeline.NewRollupOp(
   112  		pipeline.GroupByRollupType,
   113  		"testTarget",
   114  		[]string{"tag1", "tag2"},
   115  		aggregation.MustCompressTypes(aggregation.Min, aggregation.Max),
   116  	)
   117  	if err != nil {
   118  		return stubData{}, err
   119  	}
   120  	rollup6, err := pipeline.NewRollupOp(
   121  		pipeline.GroupByRollupType,
   122  		"testTarget",
   123  		[]string{"tag1", "tag2"},
   124  		aggregation.MustCompressTypes(aggregation.P999),
   125  	)
   126  	if err != nil {
   127  		return stubData{}, err
   128  	}
   129  	rollup7, err := pipeline.NewRollupOp(
   130  		pipeline.GroupByRollupType,
   131  		"testTarget",
   132  		[]string{"tag1", "tag2"},
   133  		aggregation.MustCompressTypes(aggregation.Min, aggregation.Max),
   134  	)
   135  	if err != nil {
   136  		return stubData{}, err
   137  	}
   138  	rollup8, err := pipeline.NewRollupOp(
   139  		pipeline.GroupByRollupType,
   140  		"testTarget",
   141  		[]string{"tag1", "tag2"},
   142  		aggregation.MustCompressTypes(aggregation.Min, aggregation.Max),
   143  	)
   144  	if err != nil {
   145  		return stubData{}, err
   146  	}
   147  	rollup9, err := pipeline.NewRollupOp(
   148  		pipeline.GroupByRollupType,
   149  		"testTarget",
   150  		[]string{"tag1", "tag2"},
   151  		aggregation.MustCompressTypes(aggregation.P999),
   152  	)
   153  	if err != nil {
   154  		return stubData{}, err
   155  	}
   156  	return stubData{
   157  		ErrorNamespace:    "errNs",
   158  		ConflictNamespace: "conflictNs",
   159  		Namespaces: view.Namespaces{
   160  			Version: 1,
   161  			Namespaces: []view.Namespace{
   162  				{
   163  					ID:                "ns1",
   164  					ForRuleSetVersion: 1,
   165  					Tombstoned:        false,
   166  				},
   167  				{
   168  					ID:                "ns2",
   169  					ForRuleSetVersion: 1,
   170  					Tombstoned:        false,
   171  				},
   172  			},
   173  		},
   174  		RuleSets: map[string]view.RuleSet{
   175  			"ns1": {
   176  				Namespace:     "ns1",
   177  				Version:       1,
   178  				CutoverMillis: cutoverMillis,
   179  				MappingRules: []view.MappingRule{
   180  					{
   181  						ID:            "mr_id1",
   182  						Name:          "mr1",
   183  						CutoverMillis: cutoverMillis,
   184  						Filter:        "tag1:val1 tag2:val2",
   185  						StoragePolicies: policy.StoragePolicies{
   186  							policy.MustParseStoragePolicy("1m:10d"),
   187  							policy.MustParseStoragePolicy("10m:30d"),
   188  						},
   189  					},
   190  					{
   191  						ID:            "mr_id2",
   192  						Name:          "mr2",
   193  						CutoverMillis: cutoverMillis,
   194  						Filter:        "tag2:val2",
   195  						StoragePolicies: policy.StoragePolicies{
   196  							policy.MustParseStoragePolicy("1m:10d"),
   197  						},
   198  					},
   199  				},
   200  				RollupRules: []view.RollupRule{
   201  					{
   202  						ID:            "rr_id1",
   203  						Name:          "rr1",
   204  						CutoverMillis: cutoverMillis,
   205  						Filter:        "tag1:val1 tag2:val2",
   206  						Targets: []view.RollupTarget{
   207  							{
   208  								Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   209  									{
   210  										Type:   pipeline.RollupOpType,
   211  										Rollup: rollup1,
   212  									},
   213  								}),
   214  								StoragePolicies: policy.StoragePolicies{
   215  									policy.MustParseStoragePolicy("1m:10d"),
   216  								},
   217  							},
   218  						},
   219  					},
   220  					{
   221  						ID:            "rr_id2",
   222  						Name:          "rr2",
   223  						CutoverMillis: cutoverMillis,
   224  						Filter:        "tag1:val1",
   225  						Targets: []view.RollupTarget{
   226  							{
   227  								Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   228  									{
   229  										Type:   pipeline.RollupOpType,
   230  										Rollup: rollup2,
   231  									},
   232  								}),
   233  								StoragePolicies: policy.StoragePolicies{
   234  									policy.MustParseStoragePolicy("1m:30d"),
   235  								},
   236  							},
   237  						},
   238  					},
   239  				},
   240  			},
   241  			"ns2": {
   242  				Namespace:     "ns2",
   243  				Version:       1,
   244  				CutoverMillis: cutoverMillis,
   245  				MappingRules:  []view.MappingRule{},
   246  				RollupRules: []view.RollupRule{
   247  					{
   248  						ID:            "rr_id3",
   249  						Name:          "rr1",
   250  						CutoverMillis: cutoverMillis,
   251  						Filter:        "tag1:val1 tag2:val2",
   252  						Targets: []view.RollupTarget{
   253  							{
   254  								Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   255  									{
   256  										Type:   pipeline.RollupOpType,
   257  										Rollup: rollup3,
   258  									},
   259  								}),
   260  								StoragePolicies: policy.StoragePolicies{
   261  									policy.MustParseStoragePolicy("1m:10d"),
   262  								},
   263  							},
   264  							{
   265  								Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   266  									{
   267  										Type:   pipeline.RollupOpType,
   268  										Rollup: rollup4,
   269  									},
   270  								}),
   271  								StoragePolicies: policy.StoragePolicies{
   272  									policy.MustParseStoragePolicy("1m:10d"),
   273  								},
   274  							},
   275  						},
   276  					},
   277  				},
   278  			},
   279  		},
   280  		MappingHistory: map[string]mappingRuleHistories{
   281  			"ns1": {
   282  				"mr_id1": []view.MappingRule{
   283  					{
   284  						ID:            "mr_id1",
   285  						Name:          "mr1",
   286  						CutoverMillis: cutoverMillis,
   287  						Filter:        "tag1:val1 tag2:val2",
   288  						StoragePolicies: policy.StoragePolicies{
   289  							policy.MustParseStoragePolicy("1m:10d"),
   290  							policy.MustParseStoragePolicy("10m:30d"),
   291  						},
   292  					},
   293  				},
   294  				"mr_id2": []view.MappingRule{
   295  					{
   296  						ID:            "mr_id2",
   297  						Name:          "mr2",
   298  						CutoverMillis: cutoverMillis,
   299  						Filter:        "tag1:val1 tag2:val2",
   300  						StoragePolicies: policy.StoragePolicies{
   301  							policy.MustParseStoragePolicy("1m:10d"),
   302  							policy.MustParseStoragePolicy("10m:30d"),
   303  						},
   304  					},
   305  				},
   306  			},
   307  			"ns2": nil,
   308  		},
   309  		RollupHistory: map[string]rollupRuleHistories{
   310  			"ns1": {
   311  				"rr_id1": []view.RollupRule{
   312  					{
   313  						ID:            "rr_id1",
   314  						Name:          "rr1",
   315  						CutoverMillis: cutoverMillis,
   316  						Filter:        "tag1:val1 tag2:val2",
   317  						Targets: []view.RollupTarget{
   318  							{
   319  								Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   320  									{
   321  										Type:   pipeline.RollupOpType,
   322  										Rollup: rollup5,
   323  									},
   324  								}),
   325  								StoragePolicies: policy.StoragePolicies{
   326  									policy.MustParseStoragePolicy("1m:10d"),
   327  								},
   328  							},
   329  							{
   330  								Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   331  									{
   332  										Type:   pipeline.RollupOpType,
   333  										Rollup: rollup6,
   334  									},
   335  								}),
   336  								StoragePolicies: policy.StoragePolicies{
   337  									policy.MustParseStoragePolicy("1m:10d"),
   338  								},
   339  							},
   340  						},
   341  					},
   342  					{
   343  						ID:            "rr_id1",
   344  						Name:          "rr1",
   345  						CutoverMillis: cutoverMillis,
   346  						Filter:        "tag1:val1",
   347  						Targets: []view.RollupTarget{
   348  							{
   349  								Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   350  									{
   351  										Type:   pipeline.RollupOpType,
   352  										Rollup: rollup7,
   353  									},
   354  								}),
   355  								StoragePolicies: policy.StoragePolicies{
   356  									policy.MustParseStoragePolicy("1m:10d"),
   357  								},
   358  							},
   359  						},
   360  					},
   361  				},
   362  			},
   363  			"ns2": {
   364  				"rr_id3": []view.RollupRule{
   365  					{
   366  						ID:            "rr_id1",
   367  						Name:          "rr1",
   368  						CutoverMillis: cutoverMillis,
   369  						Filter:        "tag1:val1 tag2:val2",
   370  						Targets: []view.RollupTarget{
   371  							{
   372  								Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   373  									{
   374  										Type:   pipeline.RollupOpType,
   375  										Rollup: rollup8,
   376  									},
   377  								}),
   378  								StoragePolicies: policy.StoragePolicies{
   379  									policy.MustParseStoragePolicy("1m:10d"),
   380  								},
   381  							},
   382  							{
   383  								Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{
   384  									{
   385  										Type:   pipeline.RollupOpType,
   386  										Rollup: rollup9,
   387  									},
   388  								}),
   389  								StoragePolicies: policy.StoragePolicies{
   390  									policy.MustParseStoragePolicy("1m:10d"),
   391  								},
   392  							},
   393  						},
   394  					},
   395  				},
   396  			},
   397  		},
   398  	}, nil
   399  }
   400  
   401  func (s *store) FetchNamespaces() (view.Namespaces, error) {
   402  	return s.data.Namespaces, nil
   403  }
   404  
   405  func (s *store) CreateNamespace(
   406  	namespaceID string,
   407  	uOpts r2store.UpdateOptions,
   408  ) (view.Namespace, error) {
   409  	switch namespaceID {
   410  	case s.data.ErrorNamespace:
   411  		return view.Namespace{}, r2.NewInternalError(fmt.Sprintf("could not create namespace: %s", namespaceID))
   412  	case s.data.ConflictNamespace:
   413  		return view.Namespace{}, r2.NewVersionError(fmt.Sprintf("namespaces version mismatch"))
   414  	default:
   415  		for _, n := range s.data.Namespaces.Namespaces {
   416  			if namespaceID == n.ID {
   417  				return view.Namespace{}, r2.NewConflictError(fmt.Sprintf("namespace %s already exists", namespaceID))
   418  			}
   419  		}
   420  
   421  		newView := view.Namespace{
   422  			ID:                namespaceID,
   423  			ForRuleSetVersion: 1,
   424  		}
   425  
   426  		s.data.Namespaces.Namespaces = append(s.data.Namespaces.Namespaces, newView)
   427  		s.data.RuleSets[namespaceID] = view.RuleSet{
   428  			Namespace:     namespaceID,
   429  			Version:       1,
   430  			CutoverMillis: time.Now().UnixNano(),
   431  		}
   432  		return newView, nil
   433  	}
   434  }
   435  
   436  func (s *store) ValidateRuleSet(rs view.RuleSet) error {
   437  	// Assumes no validation config for stub store so all rule sets are valid.
   438  	return nil
   439  }
   440  
   441  // This function is not supported. Use mocks package.
   442  func (s *store) UpdateRuleSet(
   443  	rsChanges changes.RuleSetChanges,
   444  	version int,
   445  	uOpts r2store.UpdateOptions,
   446  ) (view.RuleSet, error) {
   447  	return view.RuleSet{}, errNotImplemented
   448  }
   449  
   450  func (s *store) DeleteNamespace(namespaceID string, uOpts r2store.UpdateOptions) error {
   451  	switch namespaceID {
   452  	case s.data.ErrorNamespace:
   453  		return r2.NewInternalError("could not delete namespace")
   454  	case s.data.ConflictNamespace:
   455  		return r2.NewVersionError("namespace version mismatch")
   456  	default:
   457  		for i, n := range s.data.Namespaces.Namespaces {
   458  			if namespaceID == n.ID {
   459  				s.data.Namespaces.Namespaces = append(s.data.Namespaces.Namespaces[:i], s.data.Namespaces.Namespaces[i+1:]...)
   460  				return nil
   461  			}
   462  		}
   463  		return r2.NewConflictError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   464  	}
   465  }
   466  
   467  func (s *store) FetchRuleSetSnapshot(namespaceID string) (view.RuleSet, error) {
   468  	switch namespaceID {
   469  	case s.data.ErrorNamespace:
   470  		return view.RuleSet{}, r2.NewInternalError(fmt.Sprintf("could not fetch namespace: %s", namespaceID))
   471  	default:
   472  		for _, n := range s.data.Namespaces.Namespaces {
   473  			if namespaceID == n.ID {
   474  				rs := s.data.RuleSets[namespaceID]
   475  				return rs, nil
   476  			}
   477  		}
   478  		return view.RuleSet{}, r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   479  	}
   480  }
   481  
   482  func (s *store) FetchMappingRule(namespaceID string, mappingRuleID string) (view.MappingRule, error) {
   483  	switch namespaceID {
   484  	case s.data.ErrorNamespace:
   485  		return view.MappingRule{}, r2.NewInternalError(fmt.Sprintf("could not fetch mappingRule: %s in namespace: %s", namespaceID, mappingRuleID))
   486  	default:
   487  		rs, exists := s.data.RuleSets[namespaceID]
   488  		if !exists {
   489  			return view.MappingRule{}, r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   490  		}
   491  		for _, m := range rs.MappingRules {
   492  			if mappingRuleID == m.ID {
   493  				return m, nil
   494  			}
   495  		}
   496  		return view.MappingRule{}, r2.NewNotFoundError(fmt.Sprintf("mappingRule: %s doesn't exist in Namespace: %s", mappingRuleID, namespaceID))
   497  	}
   498  }
   499  
   500  func (s *store) CreateMappingRule(
   501  	namespaceID string,
   502  	mrv view.MappingRule,
   503  	uOpts r2store.UpdateOptions,
   504  ) (view.MappingRule, error) {
   505  	switch namespaceID {
   506  	case s.data.ErrorNamespace:
   507  		return view.MappingRule{}, r2.NewInternalError("could not create mapping rule")
   508  	case s.data.ConflictNamespace:
   509  		return view.MappingRule{}, r2.NewVersionError("namespaces version mismatch")
   510  	default:
   511  		rs, exists := s.data.RuleSets[namespaceID]
   512  		if !exists {
   513  			return view.MappingRule{}, r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   514  		}
   515  
   516  		for _, m := range rs.MappingRules {
   517  			if mrv.Name == m.Name {
   518  				return view.MappingRule{}, r2.NewConflictError(fmt.Sprintf("mapping rule: %s already exists in namespace: %s", mrv.Name, namespaceID))
   519  			}
   520  		}
   521  		newID := uuid.New()
   522  		newRule := view.MappingRule{
   523  			ID:              newID,
   524  			Name:            mrv.Name,
   525  			CutoverMillis:   time.Now().UnixNano(),
   526  			Filter:          mrv.Filter,
   527  			AggregationID:   mrv.AggregationID,
   528  			StoragePolicies: mrv.StoragePolicies,
   529  		}
   530  		rs.MappingRules = append(rs.MappingRules, newRule)
   531  		return newRule, nil
   532  	}
   533  }
   534  
   535  func (s *store) UpdateMappingRule(
   536  	namespaceID,
   537  	mappingRuleID string,
   538  	mrv view.MappingRule,
   539  	uOpts r2store.UpdateOptions,
   540  ) (view.MappingRule, error) {
   541  	switch namespaceID {
   542  	case s.data.ErrorNamespace:
   543  		return view.MappingRule{}, r2.NewInternalError("could not update mapping rule.")
   544  	case s.data.ConflictNamespace:
   545  		return view.MappingRule{}, r2.NewVersionError("namespaces version mismatch")
   546  	default:
   547  		rs, exists := s.data.RuleSets[namespaceID]
   548  		if !exists {
   549  			return view.MappingRule{}, r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   550  		}
   551  
   552  		for i, m := range rs.MappingRules {
   553  			if mappingRuleID == m.ID {
   554  				newRule := view.MappingRule{
   555  					ID:              "new",
   556  					Name:            mrv.Name,
   557  					CutoverMillis:   time.Now().UnixNano(),
   558  					Filter:          mrv.Filter,
   559  					AggregationID:   mrv.AggregationID,
   560  					StoragePolicies: mrv.StoragePolicies,
   561  				}
   562  				rs.MappingRules[i] = newRule
   563  				return newRule, nil
   564  			}
   565  		}
   566  		return view.MappingRule{}, r2.NewNotFoundError(fmt.Sprintf("mapping rule: %s doesn't exist in namespace: %s", mappingRuleID, namespaceID))
   567  	}
   568  }
   569  
   570  func (s *store) DeleteMappingRule(
   571  	namespaceID,
   572  	mappingRuleID string,
   573  	uOpts r2store.UpdateOptions,
   574  ) error {
   575  	switch namespaceID {
   576  	case s.data.ErrorNamespace:
   577  		return r2.NewInternalError("could not delete mapping rule.")
   578  	case s.data.ConflictNamespace:
   579  		return r2.NewVersionError("namespaces version mismatch")
   580  	default:
   581  		rs, exists := s.data.RuleSets[namespaceID]
   582  		if !exists {
   583  			return r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   584  		}
   585  		foundIdx := -1
   586  		for i, rule := range rs.MappingRules {
   587  			if rule.ID == mappingRuleID {
   588  				foundIdx = i
   589  				break
   590  			}
   591  		}
   592  		if foundIdx == -1 {
   593  			return r2.NewNotFoundError(fmt.Sprintf("mapping rule: %s doesn't exist in namespace: %s", mappingRuleID, namespaceID))
   594  		}
   595  		rs.MappingRules = append(rs.MappingRules[:foundIdx], rs.MappingRules[foundIdx+1:]...)
   596  		return nil
   597  	}
   598  }
   599  
   600  func (s *store) FetchMappingRuleHistory(namespaceID, mappingRuleID string) ([]view.MappingRule, error) {
   601  	switch namespaceID {
   602  	case s.data.ErrorNamespace:
   603  		return nil, r2.NewInternalError(fmt.Sprintf("Could not fetch mappingRuleID: %s in namespace: %s", namespaceID, mappingRuleID))
   604  	default:
   605  		ns, exists := s.data.MappingHistory[namespaceID]
   606  		if !exists {
   607  			return nil, r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   608  		}
   609  		hist, exists := ns[mappingRuleID]
   610  		if !exists {
   611  			return nil, r2.NewNotFoundError(fmt.Sprintf("mappingRule: %s doesn't exist in Namespace: %s", mappingRuleID, namespaceID))
   612  		}
   613  		return hist, nil
   614  	}
   615  }
   616  
   617  func (s *store) FetchRollupRule(namespaceID, rollupRuleID string) (view.RollupRule, error) {
   618  	switch namespaceID {
   619  	case s.data.ErrorNamespace:
   620  		return view.RollupRule{}, r2.NewInternalError(fmt.Sprintf("Could not fetch rollupRule: %s in namespace: %s", namespaceID, rollupRuleID))
   621  	default:
   622  		rs, exists := s.data.RuleSets[namespaceID]
   623  		if !exists {
   624  			return view.RollupRule{}, r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   625  		}
   626  		for _, r := range rs.RollupRules {
   627  			if rollupRuleID == r.ID {
   628  				return r, nil
   629  			}
   630  		}
   631  		return view.RollupRule{}, r2.NewNotFoundError(fmt.Sprintf("rollupRule: %s doesn't exist in Namespace: %s", rollupRuleID, namespaceID))
   632  	}
   633  }
   634  
   635  func (s *store) CreateRollupRule(
   636  	namespaceID string,
   637  	rrv view.RollupRule,
   638  	uOpts r2store.UpdateOptions,
   639  ) (view.RollupRule, error) {
   640  	switch namespaceID {
   641  	case s.data.ErrorNamespace:
   642  		return view.RollupRule{}, r2.NewInternalError("could not create rollup rule")
   643  	case s.data.ConflictNamespace:
   644  		return view.RollupRule{}, r2.NewVersionError("namespaces version mismatch")
   645  	default:
   646  		rs, exists := s.data.RuleSets[namespaceID]
   647  		if !exists {
   648  			return view.RollupRule{}, r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   649  		}
   650  		for _, r := range rs.RollupRules {
   651  			if rrv.Name == r.Name {
   652  				return view.RollupRule{}, r2.NewConflictError(fmt.Sprintf("rollup rule: %s already exists in namespace: %s", rrv.Name, namespaceID))
   653  			}
   654  		}
   655  		newID := uuid.New()
   656  		newRule := view.RollupRule{
   657  			ID:            newID,
   658  			Name:          rrv.Name,
   659  			CutoverMillis: time.Now().UnixNano(),
   660  			Filter:        rrv.Filter,
   661  			Targets:       rrv.Targets,
   662  		}
   663  		rs.RollupRules = append(rs.RollupRules, newRule)
   664  		return newRule, nil
   665  	}
   666  }
   667  
   668  func (s *store) UpdateRollupRule(
   669  	namespaceID,
   670  	rollupRuleID string,
   671  	rrv view.RollupRule,
   672  	uOpts r2store.UpdateOptions,
   673  ) (view.RollupRule, error) {
   674  	switch namespaceID {
   675  	case s.data.ErrorNamespace:
   676  		return view.RollupRule{}, r2.NewInternalError("could not update rollup rule.")
   677  	case s.data.ConflictNamespace:
   678  		return view.RollupRule{}, r2.NewVersionError("namespaces version mismatch")
   679  	default:
   680  		rs, exists := s.data.RuleSets[namespaceID]
   681  		if !exists {
   682  			return view.RollupRule{}, r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   683  		}
   684  
   685  		for i, m := range rs.RollupRules {
   686  			if rollupRuleID == m.ID {
   687  				newRule := view.RollupRule{
   688  					ID:            rollupRuleID,
   689  					Name:          rrv.Name,
   690  					CutoverMillis: time.Now().UnixNano(),
   691  					Filter:        rrv.Filter,
   692  					Targets:       rrv.Targets,
   693  				}
   694  				rs.RollupRules[i] = newRule
   695  				return newRule, nil
   696  			}
   697  		}
   698  		return view.RollupRule{}, r2.NewNotFoundError(fmt.Sprintf("rollup rule: %s doesn't exist in namespace: %s", rollupRuleID, namespaceID))
   699  	}
   700  }
   701  
   702  func (s *store) DeleteRollupRule(
   703  	namespaceID,
   704  	rollupRuleID string,
   705  	uOpts r2store.UpdateOptions,
   706  ) error {
   707  	switch namespaceID {
   708  	case s.data.ErrorNamespace:
   709  		return r2.NewInternalError("could not delete rollup rule.")
   710  	case s.data.ConflictNamespace:
   711  		return r2.NewVersionError("namespaces version mismatch")
   712  	default:
   713  		rs, exists := s.data.RuleSets[namespaceID]
   714  		if !exists {
   715  			return r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   716  		}
   717  
   718  		foundIdx := -1
   719  		for i, rule := range rs.RollupRules {
   720  			if rule.ID == rollupRuleID {
   721  				foundIdx = i
   722  				break
   723  			}
   724  		}
   725  		if foundIdx == -1 {
   726  			return r2.NewNotFoundError(fmt.Sprintf("rollup rule: %s doesn't exist in namespace: %s", rollupRuleID, namespaceID))
   727  		}
   728  		rs.RollupRules = append(rs.RollupRules[:foundIdx], rs.RollupRules[foundIdx+1:]...)
   729  		return nil
   730  	}
   731  }
   732  
   733  func (s *store) FetchRollupRuleHistory(namespaceID, rollupRuleID string) ([]view.RollupRule, error) {
   734  	switch namespaceID {
   735  	case s.data.ErrorNamespace:
   736  		return nil, r2.NewInternalError(fmt.Sprintf("Could not fetch rollupRule: %s in namespace: %s", namespaceID, rollupRuleID))
   737  	default:
   738  		ns, exists := s.data.RollupHistory[namespaceID]
   739  		if !exists {
   740  			return nil, r2.NewNotFoundError(fmt.Sprintf("namespace %s doesn't exist", namespaceID))
   741  		}
   742  		hist, exists := ns[rollupRuleID]
   743  		if !exists {
   744  			return nil, r2.NewNotFoundError(fmt.Sprintf("rollupRule: %s doesn't exist in Namespace: %s", rollupRuleID, namespaceID))
   745  		}
   746  		return hist, nil
   747  	}
   748  }
   749  
   750  func (s *store) Close() {}
   751  
   752  // nolint: unparam
   753  func b(str string) []byte        { return []byte(str) }
   754  func bs(strs ...string) [][]byte { return bytes.ArraysFromStringArray(strs) }