Skip to content
This repository has been archived by the owner on Oct 17, 2018. It is now read-only.

Commit

Permalink
Add auditing information for namespace changes
Browse files Browse the repository at this point in the history
  • Loading branch information
xichen2020 committed Dec 31, 2017
1 parent 4ad3c7f commit b22cf2c
Show file tree
Hide file tree
Showing 11 changed files with 1,112 additions and 455 deletions.
2 changes: 2 additions & 0 deletions generated/proto/namespace.proto
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ package schema;
message NamespaceSnapshot {
int32 for_ruleset_version = 1;
bool tombstoned = 2;
int64 last_updated_at_nanos = 3;
string last_updated_by = 4;
}

message Namespace {
Expand Down
37 changes: 21 additions & 16 deletions generated/proto/schema/namespace.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion generated/proto/schema/policy.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion generated/proto/schema/rule.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions rules/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ func (mc *mappingRule) addSnapshot(
return nil
}

func (mc *mappingRule) markTombstoned(cutoverTime int64) error {
func (mc *mappingRule) markTombstoned(meta UpdateMetadata) error {
n, err := mc.Name()
if err != nil {
return err
Expand All @@ -279,8 +279,10 @@ func (mc *mappingRule) markTombstoned(cutoverTime int64) error {
}
snapshot := *mc.snapshots[len(mc.snapshots)-1]
snapshot.tombstoned = true
snapshot.cutoverNanos = cutoverTime
snapshot.cutoverNanos = meta.cutoverNanos
snapshot.policies = nil
snapshot.lastUpdatedAtNanos = meta.updatedAtNanos
snapshot.lastUpdatedBy = meta.updatedBy
mc.snapshots = append(mc.snapshots, &snapshot)
return nil
}
Expand Down
64 changes: 59 additions & 5 deletions rules/mapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,13 @@ func TestMappingRuleActiveSnapshotNotFound(t *testing.T) {
require.Nil(t, mr.ActiveSnapshot(0))
}

func TestMappingRuleActiveSnapshotFound_Second(t *testing.T) {
func TestMappingRuleActiveSnapshotFoundSecond(t *testing.T) {
mr, err := newMappingRule(testMappingRuleSchema, testTagsFilterOptions())
require.NoError(t, err)
require.Equal(t, mr.snapshots[1], mr.ActiveSnapshot(100000))
}

func TestMappingRuleActiveSnapshotFound_First(t *testing.T) {
func TestMappingRuleActiveSnapshotFoundFirst(t *testing.T) {
mr, err := newMappingRule(testMappingRuleSchema, testTagsFilterOptions())
require.NoError(t, err)
require.Equal(t, mr.snapshots[0], mr.ActiveSnapshot(20000))
Expand All @@ -165,7 +165,7 @@ func TestMappingRuleActiveRuleNotFound(t *testing.T) {
require.Equal(t, mr, mr.ActiveRule(0))
}

func TestMappingRuleActiveRuleFound_Second(t *testing.T) {
func TestMappingRuleActiveRuleFoundSecond(t *testing.T) {
mr, err := newMappingRule(testMappingRuleSchema, testTagsFilterOptions())
require.NoError(t, err)
expected := &mappingRule{
Expand All @@ -175,7 +175,7 @@ func TestMappingRuleActiveRuleFound_Second(t *testing.T) {
require.Equal(t, expected, mr.ActiveRule(100000))
}

func TestMappingRuleActiveRuleFound_First(t *testing.T) {
func TestMappingRuleActiveRuleFoundFirst(t *testing.T) {
mr, err := newMappingRule(testMappingRuleSchema, testTagsFilterOptions())
require.NoError(t, err)
require.Equal(t, mr, mr.ActiveRule(20000))
Expand Down Expand Up @@ -246,10 +246,64 @@ func TestMappingTombstonedNoSnapshot(t *testing.T) {
}

func TestMappingTombstoned(t *testing.T) {
mr, _ := newMappingRule(testMappingRuleSchema, testTagsFilterOptions())
mr, err := newMappingRule(testMappingRuleSchema, testTagsFilterOptions())
require.NoError(t, err)
require.True(t, mr.Tombstoned())
}

func TestMappingRuleMarkTombstoned(t *testing.T) {
schema := &schema.MappingRule{
Snapshots: []*schema.MappingRuleSnapshot{testMappingRuleSchema.Snapshots[0]},
}
mr, err := newMappingRule(schema, testTagsFilterOptions())
require.NoError(t, err)

expectedPolicies := []policy.Policy{
policy.NewPolicy(policy.NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), policy.MustCompressAggregationTypes(policy.P999)),
}
require.Equal(t, 1, len(mr.snapshots))
lastSnapshot := mr.snapshots[0]
require.Equal(t, "foo", lastSnapshot.name)
require.False(t, lastSnapshot.tombstoned)
require.Equal(t, int64(12345), lastSnapshot.cutoverNanos)
require.NotNil(t, lastSnapshot.filter)
require.Equal(t, "tag1:value1 tag2:value2", lastSnapshot.rawFilter)
require.Equal(t, expectedPolicies, lastSnapshot.policies)
require.Equal(t, int64(1234), lastSnapshot.lastUpdatedAtNanos)
require.Equal(t, "someone", lastSnapshot.lastUpdatedBy)

meta := UpdateMetadata{
cutoverNanos: 67890,
updatedAtNanos: 10000,
updatedBy: "someone else",
}
require.NoError(t, mr.markTombstoned(meta))
require.Equal(t, 2, len(mr.snapshots))
require.Equal(t, lastSnapshot, mr.snapshots[0])
lastSnapshot = mr.snapshots[1]
require.Equal(t, "foo", lastSnapshot.name)
require.True(t, lastSnapshot.tombstoned)
require.Equal(t, int64(67890), lastSnapshot.cutoverNanos)
require.NotNil(t, lastSnapshot.filter)
require.Equal(t, "tag1:value1 tag2:value2", lastSnapshot.rawFilter)
require.Nil(t, lastSnapshot.policies)
require.Equal(t, int64(10000), lastSnapshot.lastUpdatedAtNanos)
require.Equal(t, "someone else", lastSnapshot.lastUpdatedBy)
}

func TestMappingRuleMarkTombstonedNoSnapshots(t *testing.T) {
schema := &schema.MappingRule{}
mr, err := newMappingRule(schema, testTagsFilterOptions())
require.NoError(t, err)
require.Error(t, mr.markTombstoned(UpdateMetadata{}))
}

func TestMappingRuleMarkTombstonedAlreadyTombstoned(t *testing.T) {
mr, err := newMappingRule(testMappingRuleSchema, testTagsFilterOptions())
require.NoError(t, err)
require.Error(t, mr.markTombstoned(UpdateMetadata{}))
}

func TestMappingRuleClone(t *testing.T) {
mr, _ := newMappingRule(testMappingRuleSchema, testTagsFilterOptions())
clone := mr.clone()
Expand Down
86 changes: 57 additions & 29 deletions rules/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,40 +39,52 @@ var (
errNilNamespaceSnapshot = errors.New("nil namespace snapshot")
errMultipleNamespaceMatches = errors.New("more than one namespace match found")
errNamespaceNotFound = errors.New("namespace not found")
errNamespaceNotTombstoned = errors.New("not tombstoned")
errNamespaceTombstoned = errors.New("already tombstoned")
errNoNamespaceSnapshots = errors.New("no snapshots")
errNamespaceNotTombstoned = errors.New("namespace is not tombstoned")
errNamespaceAlreadyTombstoned = errors.New("namespace is already tombstoned")
errNoNamespaceSnapshots = errors.New("namespace has no snapshots")

namespaceActionErrorFmt = "cannot %s namespace %s. %v"
)

// NamespaceSnapshot defines a namespace snapshot for which rules are defined.
type NamespaceSnapshot struct {
forRuleSetVersion int
tombstoned bool
forRuleSetVersion int
tombstoned bool
lastUpdatedAtNanos int64
lastUpdatedBy string
}

func newNamespaceSnapshot(snapshot *schema.NamespaceSnapshot) (NamespaceSnapshot, error) {
if snapshot == nil {
return emptyNamespaceSnapshot, errNilNamespaceSnapshotSchema
}
return NamespaceSnapshot{
forRuleSetVersion: int(snapshot.ForRulesetVersion),
tombstoned: snapshot.Tombstoned,
forRuleSetVersion: int(snapshot.ForRulesetVersion),
tombstoned: snapshot.Tombstoned,
lastUpdatedAtNanos: snapshot.LastUpdatedAtNanos,
lastUpdatedBy: snapshot.LastUpdatedBy,
}, nil
}

// ForRuleSetVersion is the ruleset version this namespace change is related to.
func (s NamespaceSnapshot) ForRuleSetVersion() int { return s.forRuleSetVersion }
func (s *NamespaceSnapshot) ForRuleSetVersion() int { return s.forRuleSetVersion }

// Tombstoned determines whether the namespace has been tombstoned.
func (s NamespaceSnapshot) Tombstoned() bool { return s.tombstoned }
func (s *NamespaceSnapshot) Tombstoned() bool { return s.tombstoned }

// LastUpdatedAtNanos returns the time when the namespace is last updated in nanoseconds.
func (s *NamespaceSnapshot) LastUpdatedAtNanos() int64 { return s.lastUpdatedAtNanos }

// LastUpdatedBy returns the user who last updated the namespace.
func (s *NamespaceSnapshot) LastUpdatedBy() string { return s.lastUpdatedBy }

// Schema returns the given Namespace in protobuf form
func (s NamespaceSnapshot) Schema() *schema.NamespaceSnapshot {
func (s *NamespaceSnapshot) Schema() *schema.NamespaceSnapshot {
return &schema.NamespaceSnapshot{
ForRulesetVersion: int32(s.forRuleSetVersion),
Tombstoned: s.tombstoned,
ForRulesetVersion: int32(s.forRuleSetVersion),
Tombstoned: s.tombstoned,
LastUpdatedAtNanos: s.lastUpdatedAtNanos,
LastUpdatedBy: s.lastUpdatedBy,
}
}

Expand Down Expand Up @@ -103,9 +115,11 @@ func newNamespace(namespace *schema.Namespace) (Namespace, error) {

// NamespaceView is a human friendly representation of a namespace at a single point in time.
type NamespaceView struct {
Name string
ForRuleSetVersion int
Tombstoned bool
Name string
ForRuleSetVersion int
Tombstoned bool
LastUpdatedAtNanos int64
LastUpdatedBy string
}

// NamespaceView returns the view representation of a namespace object.
Expand All @@ -115,9 +129,11 @@ func (n Namespace) NamespaceView(snapshotIdx int) (*NamespaceView, error) {
}
s := n.snapshots[snapshotIdx]
return &NamespaceView{
Name: string(n.name),
ForRuleSetVersion: s.forRuleSetVersion,
Tombstoned: s.tombstoned,
Name: string(n.name),
ForRuleSetVersion: s.forRuleSetVersion,
Tombstoned: s.tombstoned,
LastUpdatedAtNanos: s.lastUpdatedAtNanos,
LastUpdatedBy: s.lastUpdatedBy,
}, nil
}

Expand Down Expand Up @@ -157,16 +173,21 @@ func (n Namespace) Schema() (*schema.Namespace, error) {
return res, nil
}

func (n *Namespace) markTombstoned(tombstonedRSVersion int) error {
func (n *Namespace) markTombstoned(tombstonedRSVersion int, meta UpdateMetadata) error {
if n.Tombstoned() {
return errNamespaceTombstoned
return errNamespaceAlreadyTombstoned
}
snapshot := NamespaceSnapshot{
forRuleSetVersion: tombstonedRSVersion,
tombstoned: true,
lastUpdatedAtNanos: meta.updatedAtNanos,
lastUpdatedBy: meta.updatedBy,
}
snapshot := NamespaceSnapshot{tombstoned: true, forRuleSetVersion: tombstonedRSVersion}
n.snapshots = append(n.snapshots, snapshot)
return nil
}

func (n *Namespace) revive() error {
func (n *Namespace) revive(meta UpdateMetadata) error {
if !n.Tombstoned() {
return errNamespaceNotTombstoned
}
Expand All @@ -176,7 +197,12 @@ func (n *Namespace) revive() error {

tombstonedRuleSetVersion := n.snapshots[len(n.snapshots)-1].forRuleSetVersion
// NB: The revived ruleset version is one after the tombstoned ruleset version.
snapshot := NamespaceSnapshot{tombstoned: false, forRuleSetVersion: tombstonedRuleSetVersion + 1}
snapshot := NamespaceSnapshot{
forRuleSetVersion: tombstonedRuleSetVersion + 1,
tombstoned: false,
lastUpdatedAtNanos: meta.updatedAtNanos,
lastUpdatedBy: meta.updatedBy,
}
n.snapshots = append(n.snapshots, snapshot)
return nil
}
Expand Down Expand Up @@ -298,7 +324,7 @@ func (nss *Namespaces) Namespace(name string) (*Namespace, error) {
// This function returns a boolean indicating whether or not the namespace was revived.
// The revived flag should be used to decided if the corresponding" ruleset should also
// be revived.
func (nss *Namespaces) AddNamespace(nsName string) (bool, error) {
func (nss *Namespaces) AddNamespace(nsName string, meta UpdateMetadata) (bool, error) {
existing, err := nss.Namespace(nsName)
if err != nil && err != errNamespaceNotFound {
return false, fmt.Errorf(namespaceActionErrorFmt, "add", nsName, err)
Expand All @@ -310,8 +336,10 @@ func (nss *Namespaces) AddNamespace(nsName string) (bool, error) {
name: []byte(nsName),
snapshots: []NamespaceSnapshot{
NamespaceSnapshot{
forRuleSetVersion: 1,
tombstoned: false,
forRuleSetVersion: 1,
tombstoned: false,
lastUpdatedAtNanos: meta.updatedAtNanos,
lastUpdatedBy: meta.updatedBy,
},
},
}
Expand All @@ -321,21 +349,21 @@ func (nss *Namespaces) AddNamespace(nsName string) (bool, error) {
}

// Revive the namespace.
if err = existing.revive(); err != nil {
if err = existing.revive(meta); err != nil {
return false, fmt.Errorf(namespaceActionErrorFmt, "revive", nsName, err)
}

return true, nil
}

// DeleteNamespace tombstones the given namespace mapping it to the next ruleset version.
func (nss *Namespaces) DeleteNamespace(nsName string, currRuleSetVersion int) error {
func (nss *Namespaces) DeleteNamespace(nsName string, currRuleSetVersion int, meta UpdateMetadata) error {
existing, err := nss.Namespace(nsName)
if err != nil {
return fmt.Errorf(namespaceActionErrorFmt, "delete", nsName, err)
}

if err := existing.markTombstoned(currRuleSetVersion + 1); err != nil {
if err := existing.markTombstoned(currRuleSetVersion+1, meta); err != nil {
return fmt.Errorf(namespaceActionErrorFmt, "delete", nsName, err)
}

Expand Down
Loading

0 comments on commit b22cf2c

Please sign in to comment.