github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/secretbackends_test.go (about)

     1  // Copyright 2022 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"sort"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/core/secrets"
    15  	"github.com/juju/juju/core/watcher"
    16  	"github.com/juju/juju/state"
    17  	"github.com/juju/juju/state/testing"
    18  )
    19  
    20  type SecretBackendsSuite struct {
    21  	testing.StateSuite
    22  	storage state.SecretBackendsStorage
    23  	store   state.SecretsStore
    24  }
    25  
    26  var _ = gc.Suite(&SecretBackendsSuite{})
    27  
    28  func (s *SecretBackendsSuite) SetUpTest(c *gc.C) {
    29  	s.StateSuite.SetUpTest(c)
    30  	s.storage = state.NewSecretBackends(s.State)
    31  	s.store = state.NewSecrets(s.State)
    32  }
    33  
    34  func (s *SecretBackendsSuite) TestCreate(c *gc.C) {
    35  	now := s.Clock.Now().Round(time.Second).UTC()
    36  	next := now.Add(time.Minute).Round(time.Second).UTC()
    37  	config := map[string]interface{}{"foo.key": "bar"}
    38  	p := state.CreateSecretBackendParams{
    39  		Name:                "myvault",
    40  		BackendType:         "vault",
    41  		TokenRotateInterval: ptr(666 * time.Minute),
    42  		NextRotateTime:      ptr(next),
    43  		Config:              config,
    44  	}
    45  	id, err := s.storage.CreateSecretBackend(p)
    46  	c.Assert(id, gc.Not(gc.Equals), "")
    47  	c.Assert(err, jc.ErrorIsNil)
    48  	backend, err := s.storage.GetSecretBackend("myvault")
    49  	c.Assert(err, jc.ErrorIsNil)
    50  	c.Assert(backend.ID, gc.NotNil)
    51  	backend.ID = ""
    52  	c.Assert(backend, jc.DeepEquals, &secrets.SecretBackend{
    53  		Name:                "myvault",
    54  		BackendType:         "vault",
    55  		TokenRotateInterval: ptr(666 * time.Minute),
    56  		Config:              config,
    57  	})
    58  	name, nextTime := state.GetSecretBackendNextRotateInfo(c, s.State, id)
    59  	c.Assert(name, gc.Equals, "myvault")
    60  	c.Assert(nextTime, gc.Equals, next)
    61  
    62  	_, err = s.storage.CreateSecretBackend(p)
    63  	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
    64  
    65  	p.Name = "another"
    66  	p.ID = id
    67  	_, err = s.storage.CreateSecretBackend(p)
    68  	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
    69  }
    70  
    71  func (s *SecretBackendsSuite) TestGetNotFound(c *gc.C) {
    72  	_, err := s.storage.GetSecretBackend("myvault")
    73  	c.Check(err, jc.Satisfies, errors.IsNotFound)
    74  }
    75  
    76  func (s *SecretBackendsSuite) TestList(c *gc.C) {
    77  	now := s.Clock.Now().Round(time.Second).UTC()
    78  	next := now.Add(time.Minute).Round(time.Second).UTC()
    79  	config := map[string]interface{}{"foo.key": "bar"}
    80  	p := state.CreateSecretBackendParams{
    81  		Name:                "myvault",
    82  		BackendType:         "vault",
    83  		TokenRotateInterval: ptr(666 * time.Minute),
    84  		NextRotateTime:      ptr(next),
    85  		Config:              config,
    86  	}
    87  	_, err := s.storage.CreateSecretBackend(p)
    88  	c.Assert(err, jc.ErrorIsNil)
    89  	p2 := state.CreateSecretBackendParams{
    90  		Name:        "myk8s",
    91  		BackendType: "kubernetes",
    92  		Config:      config,
    93  	}
    94  	_, err = s.storage.CreateSecretBackend(p2)
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	backends, err := s.storage.ListSecretBackends()
    97  	c.Assert(err, jc.ErrorIsNil)
    98  	sort.Slice(backends, func(i, j int) bool {
    99  		return backends[i].Name < backends[j].Name
   100  	})
   101  
   102  	mc := jc.NewMultiChecker()
   103  	mc.AddExpr(`_.ID`, gc.NotNil)
   104  	c.Assert(backends, mc, []*secrets.SecretBackend{{
   105  		Name:        "myk8s",
   106  		BackendType: "kubernetes",
   107  		Config:      config,
   108  	}, {
   109  		Name:                "myvault",
   110  		BackendType:         "vault",
   111  		TokenRotateInterval: ptr(666 * time.Minute),
   112  		Config:              config,
   113  	}})
   114  }
   115  
   116  func (s *SecretBackendsSuite) TestRemove(c *gc.C) {
   117  	p := state.CreateSecretBackendParams{
   118  		Name:        "myvault",
   119  		BackendType: "vault",
   120  	}
   121  	_, err := s.storage.CreateSecretBackend(p)
   122  	c.Assert(err, jc.ErrorIsNil)
   123  
   124  	err = s.storage.DeleteSecretBackend("myvault", false)
   125  	c.Assert(err, jc.ErrorIsNil)
   126  	_, err = s.storage.GetSecretBackend("myvault")
   127  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   128  	err = s.storage.DeleteSecretBackend("myvault", false)
   129  	c.Assert(err, jc.ErrorIsNil)
   130  }
   131  
   132  func (s *SecretBackendsSuite) TestRemoveWithRevisionsFails(c *gc.C) {
   133  	p := state.CreateSecretBackendParams{
   134  		Name:        "myvault",
   135  		BackendType: "vault",
   136  	}
   137  	_, err := s.storage.CreateSecretBackend(p)
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	b, err := s.storage.GetSecretBackend("myvault")
   140  	c.Assert(err, jc.ErrorIsNil)
   141  
   142  	owner := s.Factory.MakeApplication(c, nil)
   143  	uri := secrets.NewURI()
   144  	sp := state.CreateSecretParams{
   145  		Version: 1,
   146  		Owner:   owner.Tag(),
   147  		UpdateSecretParams: state.UpdateSecretParams{
   148  			LeaderToken: &fakeToken{},
   149  			ValueRef: &secrets.ValueRef{
   150  				BackendID:  b.ID,
   151  				RevisionID: "rev-id",
   152  			},
   153  		},
   154  	}
   155  	secrets := state.NewSecrets(s.State)
   156  	_, err = secrets.CreateSecret(uri, sp)
   157  	c.Assert(err, jc.ErrorIsNil)
   158  
   159  	err = s.storage.DeleteSecretBackend("myvault", false)
   160  	c.Assert(err, jc.Satisfies, errors.IsNotSupported)
   161  	count, err := state.SecretBackendRefCount(s.State, b.ID)
   162  	c.Assert(err, jc.ErrorIsNil)
   163  	c.Assert(count, gc.Equals, 1)
   164  }
   165  
   166  func (s *SecretBackendsSuite) TestRemoveWithRevisionsForce(c *gc.C) {
   167  	p := state.CreateSecretBackendParams{
   168  		Name:        "myvault",
   169  		BackendType: "vault",
   170  	}
   171  	_, err := s.storage.CreateSecretBackend(p)
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	b, err := s.storage.GetSecretBackend("myvault")
   174  	c.Assert(err, jc.ErrorIsNil)
   175  
   176  	owner := s.Factory.MakeApplication(c, nil)
   177  	uri := secrets.NewURI()
   178  	sp := state.CreateSecretParams{
   179  		Version: 1,
   180  		Owner:   owner.Tag(),
   181  		UpdateSecretParams: state.UpdateSecretParams{
   182  			LeaderToken: &fakeToken{},
   183  			ValueRef: &secrets.ValueRef{
   184  				BackendID:  b.ID,
   185  				RevisionID: "rev-id",
   186  			},
   187  		},
   188  	}
   189  	secrets := state.NewSecrets(s.State)
   190  	_, err = secrets.CreateSecret(uri, sp)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  
   193  	count, err := state.SecretBackendRefCount(s.State, b.ID)
   194  	c.Assert(err, jc.ErrorIsNil)
   195  	c.Assert(count, gc.Equals, 1)
   196  
   197  	err = s.storage.DeleteSecretBackend("myvault", true)
   198  	c.Assert(err, jc.ErrorIsNil)
   199  	_, err = state.SecretBackendRefCount(s.State, b.ID)
   200  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   201  	_, err = s.storage.GetSecretBackend("myvault")
   202  	c.Check(err, jc.Satisfies, errors.IsNotFound)
   203  }
   204  
   205  func (s *SecretBackendsSuite) TestDeleteSecretUpdatesRefCount(c *gc.C) {
   206  	p := state.CreateSecretBackendParams{
   207  		Name:        "myvault",
   208  		BackendType: "vault",
   209  	}
   210  	_, err := s.storage.CreateSecretBackend(p)
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	b, err := s.storage.GetSecretBackend("myvault")
   213  	c.Assert(err, jc.ErrorIsNil)
   214  
   215  	owner := s.Factory.MakeApplication(c, nil)
   216  	uri := secrets.NewURI()
   217  	cp := state.CreateSecretParams{
   218  		Version: 1,
   219  		Owner:   owner.Tag(),
   220  		UpdateSecretParams: state.UpdateSecretParams{
   221  			LeaderToken: &fakeToken{},
   222  			ValueRef: &secrets.ValueRef{
   223  				BackendID:  b.ID,
   224  				RevisionID: "rev-id",
   225  			},
   226  		},
   227  	}
   228  	secretStore := state.NewSecrets(s.State)
   229  	_, err = secretStore.CreateSecret(uri, cp)
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	_, err = secretStore.UpdateSecret(uri, state.UpdateSecretParams{
   232  		LeaderToken: &fakeToken{},
   233  		ValueRef: &secrets.ValueRef{
   234  			BackendID:  b.ID,
   235  			RevisionID: "rev-id2",
   236  		},
   237  	})
   238  	c.Assert(err, jc.ErrorIsNil)
   239  	count, err := state.SecretBackendRefCount(s.State, b.ID)
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	c.Assert(count, gc.Equals, 2)
   242  
   243  	_, err = secretStore.DeleteSecret(uri)
   244  	c.Assert(err, jc.ErrorIsNil)
   245  
   246  	count, err = state.SecretBackendRefCount(s.State, b.ID)
   247  	c.Assert(err, jc.ErrorIsNil)
   248  	c.Assert(count, gc.Equals, 0)
   249  
   250  	err = s.storage.DeleteSecretBackend("myvault", false)
   251  	c.Assert(err, jc.ErrorIsNil)
   252  }
   253  
   254  func (s *SecretBackendsSuite) TestDeleteRevisionsUpdatesRefCount(c *gc.C) {
   255  	p := state.CreateSecretBackendParams{
   256  		Name:        "myvault",
   257  		BackendType: "vault",
   258  	}
   259  	_, err := s.storage.CreateSecretBackend(p)
   260  	c.Assert(err, jc.ErrorIsNil)
   261  	b, err := s.storage.GetSecretBackend("myvault")
   262  	c.Assert(err, jc.ErrorIsNil)
   263  
   264  	owner := s.Factory.MakeApplication(c, nil)
   265  	uri := secrets.NewURI()
   266  	cp := state.CreateSecretParams{
   267  		Version: 1,
   268  		Owner:   owner.Tag(),
   269  		UpdateSecretParams: state.UpdateSecretParams{
   270  			LeaderToken: &fakeToken{},
   271  			ValueRef: &secrets.ValueRef{
   272  				BackendID:  b.ID,
   273  				RevisionID: "rev-id",
   274  			},
   275  		},
   276  	}
   277  	secretStore := state.NewSecrets(s.State)
   278  	_, err = secretStore.CreateSecret(uri, cp)
   279  	c.Assert(err, jc.ErrorIsNil)
   280  	_, err = secretStore.UpdateSecret(uri, state.UpdateSecretParams{
   281  		LeaderToken: &fakeToken{},
   282  		ValueRef: &secrets.ValueRef{
   283  			BackendID:  b.ID,
   284  			RevisionID: "rev-id2",
   285  		},
   286  	})
   287  	c.Assert(err, jc.ErrorIsNil)
   288  	count, err := state.SecretBackendRefCount(s.State, b.ID)
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	c.Assert(count, gc.Equals, 2)
   291  
   292  	_, err = secretStore.DeleteSecret(uri, 1)
   293  	c.Assert(err, jc.ErrorIsNil)
   294  
   295  	count, err = state.SecretBackendRefCount(s.State, b.ID)
   296  	c.Assert(err, jc.ErrorIsNil)
   297  	c.Assert(count, gc.Equals, 1)
   298  
   299  	_, err = secretStore.DeleteSecret(uri, 2)
   300  	c.Assert(err, jc.ErrorIsNil)
   301  
   302  	count, err = state.SecretBackendRefCount(s.State, b.ID)
   303  	c.Assert(err, jc.ErrorIsNil)
   304  	c.Assert(count, gc.Equals, 0)
   305  
   306  	err = s.storage.DeleteSecretBackend("myvault", false)
   307  	c.Assert(err, jc.ErrorIsNil)
   308  }
   309  
   310  func (s *SecretBackendsSuite) TestUpdate(c *gc.C) {
   311  	p := state.CreateSecretBackendParams{
   312  		Name:        "myvault",
   313  		BackendType: "vault",
   314  		Config:      map[string]interface{}{"foo.key": "bar"},
   315  	}
   316  	_, err := s.storage.CreateSecretBackend(p)
   317  	c.Assert(err, jc.ErrorIsNil)
   318  	b, err := s.storage.GetSecretBackend("myvault")
   319  	c.Assert(err, jc.ErrorIsNil)
   320  
   321  	now := s.Clock.Now().Round(time.Second).UTC()
   322  	next := now.Add(time.Minute).Round(time.Second).UTC()
   323  	u := state.UpdateSecretBackendParams{
   324  		ID:                  b.ID,
   325  		TokenRotateInterval: ptr(666 * time.Second),
   326  		NextRotateTime:      ptr(next),
   327  		Config:              map[string]interface{}{"foo": "bar2"},
   328  	}
   329  	err = s.storage.UpdateSecretBackend(u)
   330  	c.Assert(err, jc.ErrorIsNil)
   331  	b, err = s.storage.GetSecretBackend("myvault")
   332  	c.Assert(err, jc.ErrorIsNil)
   333  	c.Assert(b, jc.DeepEquals, &secrets.SecretBackend{
   334  		ID:                  b.ID,
   335  		Name:                "myvault",
   336  		BackendType:         "vault",
   337  		TokenRotateInterval: ptr(666 * time.Second),
   338  		Config:              map[string]interface{}{"foo": "bar2"},
   339  	})
   340  	name, nextTime := state.GetSecretBackendNextRotateInfo(c, s.State, b.ID)
   341  	c.Assert(name, gc.Equals, "myvault")
   342  	c.Assert(nextTime, gc.Equals, next)
   343  }
   344  
   345  func (s *SecretBackendsSuite) TestUpdateName(c *gc.C) {
   346  	now := s.Clock.Now().Round(time.Second).UTC()
   347  	next := now.Add(time.Minute).Round(time.Second).UTC()
   348  	p := state.CreateSecretBackendParams{
   349  		Name:                "myvault",
   350  		BackendType:         "vault",
   351  		TokenRotateInterval: ptr(666 * time.Second),
   352  		NextRotateTime:      ptr(next),
   353  		Config:              map[string]interface{}{"foo.key": "bar"},
   354  	}
   355  	_, err := s.storage.CreateSecretBackend(p)
   356  	c.Assert(err, jc.ErrorIsNil)
   357  	b, err := s.storage.GetSecretBackend("myvault")
   358  	c.Assert(err, jc.ErrorIsNil)
   359  
   360  	u := state.UpdateSecretBackendParams{
   361  		ID:         b.ID,
   362  		NameChange: ptr("myvault2"),
   363  		Config:     map[string]interface{}{"foo": "bar2"},
   364  	}
   365  	err = s.storage.UpdateSecretBackend(u)
   366  	c.Assert(err, jc.ErrorIsNil)
   367  	b, err = s.storage.GetSecretBackend("myvault2")
   368  	c.Assert(err, jc.ErrorIsNil)
   369  	c.Assert(b, jc.DeepEquals, &secrets.SecretBackend{
   370  		ID:                  b.ID,
   371  		Name:                "myvault2",
   372  		BackendType:         "vault",
   373  		TokenRotateInterval: ptr(666 * time.Second),
   374  		Config:              map[string]interface{}{"foo": "bar2"},
   375  	})
   376  	name, nextTime := state.GetSecretBackendNextRotateInfo(c, s.State, b.ID)
   377  	c.Assert(name, gc.Equals, "myvault2")
   378  	c.Assert(nextTime, gc.Equals, next)
   379  }
   380  
   381  func (s *SecretBackendsSuite) TestUpdateNameForInUseBackend(c *gc.C) {
   382  	now := s.Clock.Now().Round(time.Second).UTC()
   383  	next := now.Add(time.Minute).Round(time.Second).UTC()
   384  	p := state.CreateSecretBackendParams{
   385  		Name:                "myvault",
   386  		BackendType:         "vault",
   387  		TokenRotateInterval: ptr(666 * time.Second),
   388  		NextRotateTime:      ptr(next),
   389  		Config:              map[string]interface{}{"foo.key": "bar"},
   390  	}
   391  	_, err := s.storage.CreateSecretBackend(p)
   392  	c.Assert(err, jc.ErrorIsNil)
   393  	b, err := s.storage.GetSecretBackend("myvault")
   394  	c.Assert(err, jc.ErrorIsNil)
   395  
   396  	owner := s.Factory.MakeApplication(c, nil)
   397  	uri := secrets.NewURI()
   398  	cp := state.CreateSecretParams{
   399  		Version: 1,
   400  		Owner:   owner.Tag(),
   401  		UpdateSecretParams: state.UpdateSecretParams{
   402  			LeaderToken: &fakeToken{},
   403  			ValueRef:    &secrets.ValueRef{BackendID: b.ID},
   404  		},
   405  	}
   406  	_, err = s.store.CreateSecret(uri, cp)
   407  	c.Assert(err, jc.ErrorIsNil)
   408  
   409  	u := state.UpdateSecretBackendParams{
   410  		ID:         b.ID,
   411  		NameChange: ptr("myvault2"),
   412  		Config:     map[string]interface{}{"foo": "bar2"},
   413  	}
   414  	err = s.storage.UpdateSecretBackend(u)
   415  	c.Assert(err, gc.ErrorMatches, `cannot rename a secret backend that is in use`)
   416  }
   417  
   418  func (s *SecretBackendsSuite) TestUpdateNameDuplicate(c *gc.C) {
   419  	p := state.CreateSecretBackendParams{
   420  		Name:        "myvault",
   421  		BackendType: "vault",
   422  		Config:      map[string]interface{}{"foo.key": "bar"},
   423  	}
   424  	_, err := s.storage.CreateSecretBackend(p)
   425  	c.Assert(err, jc.ErrorIsNil)
   426  	p.Name = "myvault2"
   427  	_, err = s.storage.CreateSecretBackend(p)
   428  	c.Assert(err, jc.ErrorIsNil)
   429  
   430  	b, err := s.storage.GetSecretBackend("myvault")
   431  	c.Assert(err, jc.ErrorIsNil)
   432  
   433  	u := state.UpdateSecretBackendParams{
   434  		ID:         b.ID,
   435  		NameChange: ptr("myvault2"),
   436  		Config:     map[string]interface{}{"foo": "bar2"},
   437  	}
   438  	err = s.storage.UpdateSecretBackend(u)
   439  	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
   440  }
   441  
   442  func (s *SecretBackendsSuite) TestUpdateResetRotationInterval(c *gc.C) {
   443  	now := s.Clock.Now().Round(time.Second).UTC()
   444  	next := now.Add(time.Minute).Round(time.Second).UTC()
   445  	p := state.CreateSecretBackendParams{
   446  		Name:                "myvault",
   447  		BackendType:         "vault",
   448  		TokenRotateInterval: ptr(666 * time.Second),
   449  		NextRotateTime:      ptr(next),
   450  		Config:              map[string]interface{}{"foo.key": "bar"},
   451  	}
   452  	_, err := s.storage.CreateSecretBackend(p)
   453  	c.Assert(err, jc.ErrorIsNil)
   454  	b, err := s.storage.GetSecretBackend("myvault")
   455  	c.Assert(err, jc.ErrorIsNil)
   456  
   457  	u := state.UpdateSecretBackendParams{
   458  		ID:                  b.ID,
   459  		TokenRotateInterval: ptr(0 * time.Second),
   460  		Config:              map[string]interface{}{"foo": "bar2"},
   461  	}
   462  	err = s.storage.UpdateSecretBackend(u)
   463  	c.Assert(err, jc.ErrorIsNil)
   464  	b, err = s.storage.GetSecretBackend("myvault")
   465  	c.Assert(err, jc.ErrorIsNil)
   466  	c.Assert(b, jc.DeepEquals, &secrets.SecretBackend{
   467  		ID:          b.ID,
   468  		Name:        "myvault",
   469  		BackendType: "vault",
   470  		Config:      map[string]interface{}{"foo": "bar2"},
   471  	})
   472  }
   473  
   474  func (s *SecretBackendsSuite) TestSecretBackendRotated(c *gc.C) {
   475  	config := map[string]interface{}{"foo.key": "bar"}
   476  	now := s.Clock.Now().Round(time.Second).UTC()
   477  	next := now.Add(time.Minute).Round(time.Second).UTC()
   478  	cp := state.CreateSecretBackendParams{
   479  		Name:                "myvault",
   480  		BackendType:         "vault",
   481  		TokenRotateInterval: ptr(666 * time.Minute),
   482  		NextRotateTime:      ptr(next),
   483  		Config:              config,
   484  	}
   485  	id, err := s.storage.CreateSecretBackend(cp)
   486  	c.Assert(err, jc.ErrorIsNil)
   487  	next2 := now.Add(time.Hour).Round(time.Second).UTC()
   488  	err = s.storage.SecretBackendRotated(id, next2)
   489  	c.Assert(err, jc.ErrorIsNil)
   490  
   491  	_, nextTime := state.GetSecretBackendNextRotateInfo(c, s.State, id)
   492  	c.Assert(nextTime, gc.Equals, next2)
   493  }
   494  
   495  func (s *SecretBackendsSuite) TestSecretBackendRotatedConcurrent(c *gc.C) {
   496  	config := map[string]interface{}{"foo.key": "bar"}
   497  	now := s.Clock.Now().Round(time.Second).UTC()
   498  	next := now.Add(time.Minute).Round(time.Second).UTC()
   499  	cp := state.CreateSecretBackendParams{
   500  		Name:                "myvault",
   501  		BackendType:         "vault",
   502  		TokenRotateInterval: ptr(666 * time.Minute),
   503  		NextRotateTime:      ptr(next),
   504  		Config:              config,
   505  	}
   506  	id, err := s.storage.CreateSecretBackend(cp)
   507  	c.Assert(err, jc.ErrorIsNil)
   508  
   509  	later := now.Add(time.Hour).Round(time.Second).UTC()
   510  	later2 := now.Add(2 * time.Hour).Round(time.Second).UTC()
   511  	state.SetBeforeHooks(c, s.State, func() {
   512  		err := s.storage.SecretBackendRotated(id, later)
   513  		c.Assert(err, jc.ErrorIsNil)
   514  	})
   515  
   516  	err = s.storage.SecretBackendRotated(id, later2)
   517  	c.Assert(err, jc.ErrorIsNil)
   518  
   519  	_, nextTime := state.GetSecretBackendNextRotateInfo(c, s.State, id)
   520  	c.Assert(nextTime, gc.Equals, later)
   521  }
   522  
   523  type SecretBackendWatcherSuite struct {
   524  	testing.StateSuite
   525  	storage state.SecretBackendsStorage
   526  }
   527  
   528  var _ = gc.Suite(&SecretBackendWatcherSuite{})
   529  
   530  func (s *SecretBackendWatcherSuite) SetUpTest(c *gc.C) {
   531  	s.StateSuite.SetUpTest(c)
   532  	s.storage = state.NewSecretBackends(s.State)
   533  }
   534  
   535  func (s *SecretBackendWatcherSuite) setupWatcher(c *gc.C) (state.SecretBackendRotateWatcher, string) {
   536  	now := s.Clock.Now().Round(time.Second).UTC()
   537  	next := now.Add(time.Minute).Round(time.Second).UTC()
   538  	cp := state.CreateSecretBackendParams{
   539  		Name:                "myvault",
   540  		BackendType:         "vault",
   541  		TokenRotateInterval: ptr(666 * time.Minute),
   542  		NextRotateTime:      ptr(next),
   543  		Config:              map[string]interface{}{"foo.key": "bar"},
   544  	}
   545  	id, err := s.storage.CreateSecretBackend(cp)
   546  	c.Assert(err, jc.ErrorIsNil)
   547  	w, err := s.State.WatchSecretBackendRotationChanges()
   548  	c.Assert(err, jc.ErrorIsNil)
   549  
   550  	wc := testing.NewSecretBackendRotateWatcherC(c, w)
   551  	wc.AssertChange(watcher.SecretBackendRotateChange{
   552  		ID:              id,
   553  		Name:            "myvault",
   554  		NextTriggerTime: next,
   555  	})
   556  	wc.AssertNoChange()
   557  	return w, id
   558  }
   559  
   560  func (s *SecretBackendWatcherSuite) TestWatchInitialEvent(c *gc.C) {
   561  	w, _ := s.setupWatcher(c)
   562  	testing.AssertStop(c, w)
   563  }
   564  
   565  func (s *SecretBackendWatcherSuite) TestWatchSingleUpdate(c *gc.C) {
   566  	w, id := s.setupWatcher(c)
   567  	wc := testing.NewSecretBackendRotateWatcherC(c, w)
   568  	defer testing.AssertStop(c, w)
   569  
   570  	now := s.Clock.Now().Round(time.Second).UTC()
   571  	next := now.Add(2 * time.Hour).Round(time.Second).UTC()
   572  	err := s.storage.SecretBackendRotated(id, next)
   573  	c.Assert(err, jc.ErrorIsNil)
   574  
   575  	wc.AssertChange(watcher.SecretBackendRotateChange{
   576  		ID:              id,
   577  		Name:            "myvault",
   578  		NextTriggerTime: next,
   579  	})
   580  	wc.AssertNoChange()
   581  }
   582  
   583  func (s *SecretBackendWatcherSuite) TestWatchDelete(c *gc.C) {
   584  	w, id := s.setupWatcher(c)
   585  	wc := testing.NewSecretBackendRotateWatcherC(c, w)
   586  	defer testing.AssertStop(c, w)
   587  
   588  	err := s.storage.UpdateSecretBackend(state.UpdateSecretBackendParams{
   589  		ID:                  id,
   590  		TokenRotateInterval: ptr(0 * time.Second),
   591  	})
   592  	c.Assert(err, jc.ErrorIsNil)
   593  
   594  	wc.AssertChange(watcher.SecretBackendRotateChange{
   595  		ID:   id,
   596  		Name: "myvault",
   597  	})
   598  	wc.AssertNoChange()
   599  }
   600  
   601  func (s *SecretBackendWatcherSuite) TestWatchMultipleUpdatesSameBackend(c *gc.C) {
   602  	w, id := s.setupWatcher(c)
   603  	wc := testing.NewSecretBackendRotateWatcherC(c, w)
   604  	defer testing.AssertStop(c, w)
   605  
   606  	// TODO(quiescence): these two changes should be one event.
   607  	now := s.Clock.Now().Round(time.Second).UTC()
   608  	next := now.Add(time.Minute).Round(time.Second).UTC()
   609  	err := s.storage.SecretBackendRotated(id, next)
   610  	c.Assert(err, jc.ErrorIsNil)
   611  	wc.AssertChange(watcher.SecretBackendRotateChange{
   612  		ID:              id,
   613  		Name:            "myvault",
   614  		NextTriggerTime: next,
   615  	})
   616  	next2 := now.Add(time.Hour).Round(time.Second).UTC()
   617  	err = s.storage.SecretBackendRotated(id, next2)
   618  	c.Assert(err, jc.ErrorIsNil)
   619  
   620  	wc.AssertChange(watcher.SecretBackendRotateChange{
   621  		ID:              id,
   622  		Name:            "myvault",
   623  		NextTriggerTime: next2,
   624  	})
   625  	wc.AssertNoChange()
   626  }
   627  
   628  func (s *SecretBackendWatcherSuite) TestWatchMultipleUpdatesSameBackendDeleted(c *gc.C) {
   629  	w, id := s.setupWatcher(c)
   630  	wc := testing.NewSecretBackendRotateWatcherC(c, w)
   631  	defer testing.AssertStop(c, w)
   632  
   633  	// TODO(quiescence): these two changes should be one event.
   634  	now := s.Clock.Now().Round(time.Second).UTC()
   635  	next := now.Add(time.Hour).Round(time.Second).UTC()
   636  	err := s.storage.SecretBackendRotated(id, next)
   637  	c.Assert(err, jc.ErrorIsNil)
   638  	wc.AssertChange(watcher.SecretBackendRotateChange{
   639  		ID:              id,
   640  		Name:            "myvault",
   641  		NextTriggerTime: next,
   642  	})
   643  	err = s.storage.UpdateSecretBackend(state.UpdateSecretBackendParams{
   644  		ID:                  id,
   645  		TokenRotateInterval: ptr(time.Duration(0)),
   646  	})
   647  	c.Assert(err, jc.ErrorIsNil)
   648  
   649  	wc.AssertChange(watcher.SecretBackendRotateChange{
   650  		ID:   id,
   651  		Name: "myvault",
   652  	})
   653  	wc.AssertNoChange()
   654  }
   655  
   656  func (s *SecretBackendWatcherSuite) TestWatchMultipleUpdates(c *gc.C) {
   657  	w, id := s.setupWatcher(c)
   658  	wc := testing.NewSecretBackendRotateWatcherC(c, w)
   659  	defer testing.AssertStop(c, w)
   660  
   661  	// TODO(quiescence): these two changes should be one event.
   662  	now := s.Clock.Now().Round(time.Second).UTC()
   663  	next := now.Add(time.Hour).Round(time.Second).UTC()
   664  	err := s.storage.SecretBackendRotated(id, next)
   665  	c.Assert(err, jc.ErrorIsNil)
   666  	wc.AssertChange(watcher.SecretBackendRotateChange{
   667  		ID:              id,
   668  		Name:            "myvault",
   669  		NextTriggerTime: next,
   670  	})
   671  
   672  	next2 := now.Add(time.Minute).Round(time.Second).UTC()
   673  	id2, err := s.storage.CreateSecretBackend(state.CreateSecretBackendParams{
   674  		Name:                "myvault2",
   675  		BackendType:         "vault",
   676  		TokenRotateInterval: ptr(666 * time.Minute),
   677  		NextRotateTime:      ptr(next2),
   678  		Config:              map[string]interface{}{"foo.key": "bar"},
   679  	})
   680  	c.Assert(err, jc.ErrorIsNil)
   681  	wc.AssertChange(watcher.SecretBackendRotateChange{
   682  		ID:              id2,
   683  		Name:            "myvault2",
   684  		NextTriggerTime: next2,
   685  	})
   686  
   687  	err = s.storage.UpdateSecretBackend(state.UpdateSecretBackendParams{
   688  		ID:                  id,
   689  		TokenRotateInterval: ptr(time.Duration(0)),
   690  	})
   691  	c.Assert(err, jc.ErrorIsNil)
   692  
   693  	wc.AssertChange(watcher.SecretBackendRotateChange{
   694  		ID:   id,
   695  		Name: "myvault",
   696  	})
   697  	wc.AssertNoChange()
   698  }