From 4a02ad7525768ce3f0fc8a2822b98c8a646e425d Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Fri, 4 Nov 2016 16:52:15 -0400 Subject: [PATCH 1/7] Enforce single default source on update This ensures that there will only be on default source upon setting a new default by unsetting the default flag on all other sources. This only happens when the source to be updated has Default set to true to avoid a performance hit when updating other attributes. --- bolt/sources.go | 19 +++++++++++++++++++ bolt/sources_test.go | 26 +++++++++++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/bolt/sources.go b/bolt/sources.go index 5efc990163..13711a6ec4 100644 --- a/bolt/sources.go +++ b/bolt/sources.go @@ -103,6 +103,25 @@ func (s *SourcesStore) Update(ctx context.Context, src chronograf.Source) error return chronograf.ErrSourceNotFound } + //Unset any existing defaults if this is a new default + if src.Default { + srcs, err := s.All(ctx) + if err != nil { + return err + } + + for _, other := range srcs { + if other.Default { + other.Default = false + if v, err := internal.MarshalSource(other); err != nil { + return err + } else if err := b.Put(itob(other.ID), v); err != nil { + return err + } + } + } + } + if v, err := internal.MarshalSource(src); err != nil { return err } else if err := b.Put(itob(src.ID), v); err != nil { diff --git a/bolt/sources_test.go b/bolt/sources_test.go index 60dbc9346d..30b7620421 100644 --- a/bolt/sources_test.go +++ b/bolt/sources_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/influxdata/chronograf" + "github.com/influxdata/chronograf/bolt" ) // Ensure an SourceStore can store, retrieve, update, and delete sources. @@ -54,11 +55,8 @@ func TestSourceStore(t *testing.T) { // Update source. srcs[0].Username = "calvinklein" srcs[1].Name = "Enchantment Under the Sea Dance" - if err := s.Update(nil, srcs[0]); err != nil { - t.Fatal(err) - } else if err := s.Update(nil, srcs[1]); err != nil { - t.Fatal(err) - } + mustUpdateSource(t, s, srcs[0]) + mustUpdateSource(t, s, srcs[1]) // Confirm sources have updated. if src, err := s.Get(nil, srcs[0].ID); err != nil { @@ -72,6 +70,18 @@ func TestSourceStore(t *testing.T) { t.Fatalf("source 1 update error: got %v, expected %v", src.Name, "Enchantment Under the Sea Dance") } + // Attempt to make two default sources + srcs[0].Default = true + srcs[1].Default = true + mustUpdateSource(t, s, srcs[0]) + mustUpdateSource(t, s, srcs[1]) + + if actual, err := s.Get(nil, srcs[0].ID); err != nil { + t.Fatal(err) + } else if actual.Default == true { + t.Fatal("Able to set two default sources when only one should be permitted") + } + // Delete an source. if err := s.Delete(nil, srcs[0]); err != nil { t.Fatal(err) @@ -90,3 +100,9 @@ func TestSourceStore(t *testing.T) { t.Fatalf("After delete All returned incorrect source; got %v, expected %v", bsrcs[0], srcs[1]) } } + +func mustUpdateSource(t *testing.T, s *bolt.SourcesStore, src chronograf.Source) { + if err := s.Update(nil, src); err != nil { + t.Fatal(err) + } +} From 193765ead247fba0219dea8baec5a5b76027f098 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Mon, 7 Nov 2016 11:58:14 -0500 Subject: [PATCH 2/7] Auto-set default source on Add and Delete Previously, when adding a new default source, it was possible to create more than one default source, since the previous default would not be unset. Implementing this revealed another issue where deleting the default source would leave no default set. If the default source is deleted now, another source is chosen to be the new default. --- bolt/sources.go | 67 +++++++++++++++++++++++++++++++++++--------- bolt/sources_test.go | 40 ++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 14 deletions(-) diff --git a/bolt/sources.go b/bolt/sources.go index 13711a6ec4..3f5d98a601 100644 --- a/bolt/sources.go +++ b/bolt/sources.go @@ -50,6 +50,12 @@ func (s *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronogr } src.ID = int(seq) + if src.Default { + if err := s.resetDefaultSource(b, ctx); err != nil { + return err + } + } + if v, err := internal.MarshalSource(src); err != nil { return err } else if err := b.Put(itob(src.ID), v); err != nil { @@ -65,6 +71,32 @@ func (s *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronogr // Delete removes the Source from the SourcesStore func (s *SourcesStore) Delete(ctx context.Context, src chronograf.Source) error { + + // Check if requested source is the current default + if src, err := s.Get(ctx, src.ID); err != nil { + return err + } else if src.Default { + // Locate another source to be the new default + if srcs, err := s.All(ctx); err != nil { + return err + } else { + var other *chronograf.Source + for idx, _ := range srcs { + other = &srcs[idx] + // avoid selecting the source we're about to delete as the new default + if other.ID != src.ID { + break + } + } + + // set the other to be the default + other.Default = true + if err := s.Update(ctx, *other); err != nil { + return err + } + } + } + if err := s.client.db.Update(func(tx *bolt.Tx) error { if err := tx.Bucket(SourcesBucket).Delete(itob(src.ID)); err != nil { return err @@ -103,23 +135,10 @@ func (s *SourcesStore) Update(ctx context.Context, src chronograf.Source) error return chronograf.ErrSourceNotFound } - //Unset any existing defaults if this is a new default if src.Default { - srcs, err := s.All(ctx) - if err != nil { + if err := s.resetDefaultSource(b, ctx); err != nil { return err } - - for _, other := range srcs { - if other.Default { - other.Default = false - if v, err := internal.MarshalSource(other); err != nil { - return err - } else if err := b.Put(itob(other.ID), v); err != nil { - return err - } - } - } } if v, err := internal.MarshalSource(src); err != nil { @@ -134,3 +153,23 @@ func (s *SourcesStore) Update(ctx context.Context, src chronograf.Source) error return nil } + +// resetDefaultSource unsets the Default flag on all sources +func (s *SourcesStore) resetDefaultSource(b *bolt.Bucket, ctx context.Context) error { + srcs, err := s.All(ctx) + if err != nil { + return err + } + + for _, other := range srcs { + if other.Default { + other.Default = false + if v, err := internal.MarshalSource(other); err != nil { + return err + } else if err := b.Put(itob(other.ID), v); err != nil { + return err + } + } + } + return nil +} diff --git a/bolt/sources_test.go b/bolt/sources_test.go index 30b7620421..9d36291bc4 100644 --- a/bolt/sources_test.go +++ b/bolt/sources_test.go @@ -82,6 +82,32 @@ func TestSourceStore(t *testing.T) { t.Fatal("Able to set two default sources when only one should be permitted") } + // Attempt to add a new default source + srcs = append(srcs, chronograf.Source{ + Name: "Biff Tannen", + Type: "influx", + Username: "HELLO", + Password: "MCFLY", + URL: "anybody.in.there.local", + Default: true, + }) + + srcs[2] = mustAddSource(t, s, srcs[2]) + if srcs, err := s.All(nil); err != nil { + t.Fatal(err) + } else { + defaults := 0 + for _, src := range srcs { + if src.Default { + defaults++ + } + } + + if defaults != 1 { + t.Fatal("Able to add more than one default source") + } + } + // Delete an source. if err := s.Delete(nil, srcs[0]); err != nil { t.Fatal(err) @@ -92,6 +118,11 @@ func TestSourceStore(t *testing.T) { t.Fatalf("source delete error: got %v, expected %v", err, chronograf.ErrSourceNotFound) } + // Delete the other source we created + if err := s.Delete(nil, srcs[2]); err != nil { + t.Fatal(err) + } + if bsrcs, err := s.All(nil); err != nil { t.Fatal(err) } else if len(bsrcs) != 1 { @@ -106,3 +137,12 @@ func mustUpdateSource(t *testing.T, s *bolt.SourcesStore, src chronograf.Source) t.Fatal(err) } } + +func mustAddSource(t *testing.T, s *bolt.SourcesStore, src chronograf.Source) chronograf.Source { + if src, err := s.Add(nil, src); err != nil { + t.Fatal(err) + return src + } else { + return src + } +} From d03d47b9ad325fa9a9d70ecef6ce9c291b7ee5cd Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Mon, 7 Nov 2016 18:07:52 -0500 Subject: [PATCH 3/7] Extract Default source enforcement logic Enforcing the constraint that there be exactly one default source whenever there are sources present isn't really a concern of the traditional CRUD methods that are provided by SourcesStore. This makes this separation a little more explicit and provides a place to glue some documentation onto what would otherwise be a confusing bit of business logic in the write path of SourcesStore. --- bolt/sources.go | 55 +++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/bolt/sources.go b/bolt/sources.go index 3f5d98a601..b64ddbd4f6 100644 --- a/bolt/sources.go +++ b/bolt/sources.go @@ -72,29 +72,8 @@ func (s *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronogr // Delete removes the Source from the SourcesStore func (s *SourcesStore) Delete(ctx context.Context, src chronograf.Source) error { - // Check if requested source is the current default - if src, err := s.Get(ctx, src.ID); err != nil { + if err := s.setRandomDefault(ctx, src); err != nil { return err - } else if src.Default { - // Locate another source to be the new default - if srcs, err := s.All(ctx); err != nil { - return err - } else { - var other *chronograf.Source - for idx, _ := range srcs { - other = &srcs[idx] - // avoid selecting the source we're about to delete as the new default - if other.ID != src.ID { - break - } - } - - // set the other to be the default - other.Default = true - if err := s.Update(ctx, *other); err != nil { - return err - } - } } if err := s.client.db.Update(func(tx *bolt.Tx) error { @@ -173,3 +152,35 @@ func (s *SourcesStore) resetDefaultSource(b *bolt.Bucket, ctx context.Context) e } return nil } + +// setRandomDefault will locate a source other than the provided +// chronograf.Source and set it as the default source. If no other sources are +// available, the provided source will be set to the default source if is not +// already. It assumes that the provided chronograf.Source has been persisted. +func (s *SourcesStore) setRandomDefault(ctx context.Context, src chronograf.Source) error { + // Check if requested source is the current default + if target, err := s.Get(ctx, src.ID); err != nil { + return err + } else if target.Default { + // Locate another source to be the new default + if srcs, err := s.All(ctx); err != nil { + return err + } else { + var other *chronograf.Source + for idx, _ := range srcs { + other = &srcs[idx] + // avoid selecting the source we're about to delete as the new default + if other.ID != target.ID { + break + } + } + + // set the other to be the default + other.Default = true + if err := s.Update(ctx, *other); err != nil { + return err + } + } + } + return nil +} From 493befbdc77fe405a71452b6486b2ad12234dbaa Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Mon, 7 Nov 2016 18:13:02 -0500 Subject: [PATCH 4/7] Enforce default constraints on first/last source Whenver sources are present, there should be exactly one default source. Prior to this commit, it was possible to add the first source as a non-default source and circumvent this invariant. This unfortunately requires a bit of special-casing logic in s.Add and requires a query to fetch the number of sources currently persisted. Tests have been added to cover this as well as deleting the final source, to ensure that there aren't any odd indexing panics that might result from trying to find another candidate default source when deleting. --- bolt/sources.go | 8 ++++++++ bolt/sources_test.go | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/bolt/sources.go b/bolt/sources.go index b64ddbd4f6..61506fdfda 100644 --- a/bolt/sources.go +++ b/bolt/sources.go @@ -42,6 +42,14 @@ func (s *SourcesStore) All(ctx context.Context) ([]chronograf.Source, error) { // Add creates a new Source in the SourceStore. func (s *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronograf.Source, error) { + + // force first source added to be default + if srcs, err := s.All(ctx); err != nil { + return chronograf.Source{}, err + } else if len(srcs) == 0 { + src.Default = true + } + if err := s.client.db.Update(func(tx *bolt.Tx) error { b := tx.Bucket(SourcesBucket) seq, err := b.NextSequence() diff --git a/bolt/sources_test.go b/bolt/sources_test.go index 9d36291bc4..9693116ab5 100644 --- a/bolt/sources_test.go +++ b/bolt/sources_test.go @@ -130,6 +130,28 @@ func TestSourceStore(t *testing.T) { } else if !reflect.DeepEqual(bsrcs[0], srcs[1]) { t.Fatalf("After delete All returned incorrect source; got %v, expected %v", bsrcs[0], srcs[1]) } + + // Delete the final source + if err := s.Delete(nil, srcs[1]); err != nil { + t.Fatal(err) + } + + // Try to add one source as a non-default and ensure that it becomes a + // default + src := mustAddSource(t, s, chronograf.Source{ + Name: "Biff Tannen", + Type: "influx", + Username: "HELLO", + Password: "MCFLY", + URL: "anybody.in.there.local", + Default: false, + }) + + if actual, err := s.Get(nil, src.ID); err != nil { + t.Fatal(err) + } else if !actual.Default { + t.Fatal("Expected first source added to be default but wasn't") + } } func mustUpdateSource(t *testing.T, s *bolt.SourcesStore, src chronograf.Source) { From b8e335fbee011c4e03d073ae67d3107f52a70b58 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Tue, 8 Nov 2016 12:09:35 -0500 Subject: [PATCH 5/7] Prevent deadlocks with source default enforcement Previously, the logic to enforce on default source relied on the public-facing CRUD methods already provided by SourcesStore. This was prone to deadlocks due to the possibility of acquiring a transaction within a transaction. This extracts the logic that was performed within the transactions of each CRUD action and makes the private methods that receive a *bolt.Tx. This allows the convenience methods that enforce default source to use this private API and provide the transaction from its caller. This ensures that there is only ever one transaction acquired by each expored CRUD method. --- bolt/sources.go | 153 +++++++++++++++++++++++++++++------------------- 1 file changed, 92 insertions(+), 61 deletions(-) diff --git a/bolt/sources.go b/bolt/sources.go index 61506fdfda..b9d3e514f8 100644 --- a/bolt/sources.go +++ b/bolt/sources.go @@ -21,14 +21,9 @@ type SourcesStore struct { func (s *SourcesStore) All(ctx context.Context) ([]chronograf.Source, error) { var srcs []chronograf.Source if err := s.client.db.View(func(tx *bolt.Tx) error { - if err := tx.Bucket(SourcesBucket).ForEach(func(k, v []byte) error { - var src chronograf.Source - if err := internal.UnmarshalSource(v, &src); err != nil { - return err - } - srcs = append(srcs, src) - return nil - }); err != nil { + var err error + srcs, err = s.all(ctx, tx) + if err != nil { return err } return nil @@ -51,25 +46,7 @@ func (s *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronogr } if err := s.client.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket(SourcesBucket) - seq, err := b.NextSequence() - if err != nil { - return err - } - src.ID = int(seq) - - if src.Default { - if err := s.resetDefaultSource(b, ctx); err != nil { - return err - } - } - - if v, err := internal.MarshalSource(src); err != nil { - return err - } else if err := b.Put(itob(src.ID), v); err != nil { - return err - } - return nil + return s.add(ctx, &src, tx) }); err != nil { return chronograf.Source{}, err } @@ -79,16 +56,11 @@ func (s *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronogr // Delete removes the Source from the SourcesStore func (s *SourcesStore) Delete(ctx context.Context, src chronograf.Source) error { - - if err := s.setRandomDefault(ctx, src); err != nil { - return err - } - if err := s.client.db.Update(func(tx *bolt.Tx) error { - if err := tx.Bucket(SourcesBucket).Delete(itob(src.ID)); err != nil { + if err := s.setRandomDefault(ctx, tx, src); err != nil { return err } - return nil + return s.delete(ctx, src, tx) }); err != nil { return err } @@ -100,9 +72,9 @@ func (s *SourcesStore) Delete(ctx context.Context, src chronograf.Source) error func (s *SourcesStore) Get(ctx context.Context, id int) (chronograf.Source, error) { var src chronograf.Source if err := s.client.db.View(func(tx *bolt.Tx) error { - if v := tx.Bucket(SourcesBucket).Get(itob(id)); v == nil { - return chronograf.ErrSourceNotFound - } else if err := internal.UnmarshalSource(v, &src); err != nil { + var err error + src, err = s.get(ctx, id, tx) + if err != nil { return err } return nil @@ -116,24 +88,7 @@ func (s *SourcesStore) Get(ctx context.Context, id int) (chronograf.Source, erro // Update a Source func (s *SourcesStore) Update(ctx context.Context, src chronograf.Source) error { if err := s.client.db.Update(func(tx *bolt.Tx) error { - // Get an existing soource with the same ID. - b := tx.Bucket(SourcesBucket) - if v := b.Get(itob(src.ID)); v == nil { - return chronograf.ErrSourceNotFound - } - - if src.Default { - if err := s.resetDefaultSource(b, ctx); err != nil { - return err - } - } - - if v, err := internal.MarshalSource(src); err != nil { - return err - } else if err := b.Put(itob(src.ID), v); err != nil { - return err - } - return nil + return s.update(ctx, src, tx) }); err != nil { return err } @@ -141,9 +96,85 @@ func (s *SourcesStore) Update(ctx context.Context, src chronograf.Source) error return nil } +func (s *SourcesStore) all(ctx context.Context, tx *bolt.Tx) ([]chronograf.Source, error) { + var srcs []chronograf.Source + if err := tx.Bucket(SourcesBucket).ForEach(func(k, v []byte) error { + var src chronograf.Source + if err := internal.UnmarshalSource(v, &src); err != nil { + return err + } + srcs = append(srcs, src) + return nil + }); err != nil { + return srcs, err + } + return srcs, nil +} + +func (s *SourcesStore) add(ctx context.Context, src *chronograf.Source, tx *bolt.Tx) error { + b := tx.Bucket(SourcesBucket) + seq, err := b.NextSequence() + if err != nil { + return err + } + src.ID = int(seq) + + if src.Default { + if err := s.resetDefaultSource(tx, ctx); err != nil { + return err + } + } + + if v, err := internal.MarshalSource(*src); err != nil { + return err + } else if err := b.Put(itob(src.ID), v); err != nil { + return err + } + return nil +} + +func (s *SourcesStore) delete(ctx context.Context, src chronograf.Source, tx *bolt.Tx) error { + if err := tx.Bucket(SourcesBucket).Delete(itob(src.ID)); err != nil { + return err + } + return nil +} + +func (s *SourcesStore) get(ctx context.Context, id int, tx *bolt.Tx) (chronograf.Source, error) { + var src chronograf.Source + if v := tx.Bucket(SourcesBucket).Get(itob(id)); v == nil { + return src, chronograf.ErrSourceNotFound + } else if err := internal.UnmarshalSource(v, &src); err != nil { + return src, err + } + return src, nil +} + +func (s *SourcesStore) update(ctx context.Context, src chronograf.Source, tx *bolt.Tx) error { + // Get an existing soource with the same ID. + b := tx.Bucket(SourcesBucket) + if v := b.Get(itob(src.ID)); v == nil { + return chronograf.ErrSourceNotFound + } + + if src.Default { + if err := s.resetDefaultSource(tx, ctx); err != nil { + return err + } + } + + if v, err := internal.MarshalSource(src); err != nil { + return err + } else if err := b.Put(itob(src.ID), v); err != nil { + return err + } + return nil +} + // resetDefaultSource unsets the Default flag on all sources -func (s *SourcesStore) resetDefaultSource(b *bolt.Bucket, ctx context.Context) error { - srcs, err := s.All(ctx) +func (s *SourcesStore) resetDefaultSource(tx *bolt.Tx, ctx context.Context) error { + b := tx.Bucket(SourcesBucket) + srcs, err := s.all(ctx, tx) if err != nil { return err } @@ -165,13 +196,13 @@ func (s *SourcesStore) resetDefaultSource(b *bolt.Bucket, ctx context.Context) e // chronograf.Source and set it as the default source. If no other sources are // available, the provided source will be set to the default source if is not // already. It assumes that the provided chronograf.Source has been persisted. -func (s *SourcesStore) setRandomDefault(ctx context.Context, src chronograf.Source) error { +func (s *SourcesStore) setRandomDefault(ctx context.Context, tx *bolt.Tx, src chronograf.Source) error { // Check if requested source is the current default - if target, err := s.Get(ctx, src.ID); err != nil { + if target, err := s.get(ctx, src.ID, tx); err != nil { return err } else if target.Default { // Locate another source to be the new default - if srcs, err := s.All(ctx); err != nil { + if srcs, err := s.all(ctx, tx); err != nil { return err } else { var other *chronograf.Source @@ -185,7 +216,7 @@ func (s *SourcesStore) setRandomDefault(ctx context.Context, src chronograf.Sour // set the other to be the default other.Default = true - if err := s.Update(ctx, *other); err != nil { + if err := s.update(ctx, *other, tx); err != nil { return err } } From d4f243439988c0bb5d5ba8b4660174deca4a2c7b Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Tue, 8 Nov 2016 12:14:06 -0500 Subject: [PATCH 6/7] Make context.Context the first argument This is the convention. --- bolt/sources.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bolt/sources.go b/bolt/sources.go index b9d3e514f8..52a3ca3a0a 100644 --- a/bolt/sources.go +++ b/bolt/sources.go @@ -120,7 +120,7 @@ func (s *SourcesStore) add(ctx context.Context, src *chronograf.Source, tx *bolt src.ID = int(seq) if src.Default { - if err := s.resetDefaultSource(tx, ctx); err != nil { + if err := s.resetDefaultSource(ctx, tx); err != nil { return err } } @@ -158,7 +158,7 @@ func (s *SourcesStore) update(ctx context.Context, src chronograf.Source, tx *bo } if src.Default { - if err := s.resetDefaultSource(tx, ctx); err != nil { + if err := s.resetDefaultSource(ctx, tx); err != nil { return err } } @@ -172,7 +172,7 @@ func (s *SourcesStore) update(ctx context.Context, src chronograf.Source, tx *bo } // resetDefaultSource unsets the Default flag on all sources -func (s *SourcesStore) resetDefaultSource(tx *bolt.Tx, ctx context.Context) error { +func (s *SourcesStore) resetDefaultSource(ctx context.Context, tx *bolt.Tx) error { b := tx.Bucket(SourcesBucket) srcs, err := s.all(ctx, tx) if err != nil { From b0860ca9036571fd205fcd591ee6416d53af8c5a Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Tue, 8 Nov 2016 12:29:29 -0500 Subject: [PATCH 7/7] Make argument order of setRandomDefault consistent Most of the other private CRUD methods have a consistent argument order of a context, a source, then a transaction. setRandomDefault had two of these args transposed, so this swaps them to be consistent with the rest of the methods. --- bolt/sources.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt/sources.go b/bolt/sources.go index 52a3ca3a0a..0f548a8230 100644 --- a/bolt/sources.go +++ b/bolt/sources.go @@ -57,7 +57,7 @@ func (s *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronogr // Delete removes the Source from the SourcesStore func (s *SourcesStore) Delete(ctx context.Context, src chronograf.Source) error { if err := s.client.db.Update(func(tx *bolt.Tx) error { - if err := s.setRandomDefault(ctx, tx, src); err != nil { + if err := s.setRandomDefault(ctx, src, tx); err != nil { return err } return s.delete(ctx, src, tx) @@ -196,7 +196,7 @@ func (s *SourcesStore) resetDefaultSource(ctx context.Context, tx *bolt.Tx) erro // chronograf.Source and set it as the default source. If no other sources are // available, the provided source will be set to the default source if is not // already. It assumes that the provided chronograf.Source has been persisted. -func (s *SourcesStore) setRandomDefault(ctx context.Context, tx *bolt.Tx, src chronograf.Source) error { +func (s *SourcesStore) setRandomDefault(ctx context.Context, src chronograf.Source, tx *bolt.Tx) error { // Check if requested source is the current default if target, err := s.get(ctx, src.ID, tx); err != nil { return err