Skip to content

Commit

Permalink
Merge pull request #1246 from alesstimec/controller-unavailable-since…
Browse files Browse the repository at this point in the history
…-update

Fixes updates to controller's UnavailableSince field.
  • Loading branch information
alesstimec authored Jul 4, 2024
2 parents 02f4b3e + 5b904d4 commit 10789ab
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 30 deletions.
54 changes: 26 additions & 28 deletions internal/jimm/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,27 +130,38 @@ func (w *Watcher) WatchAllModelSummaries(ctx context.Context, interval time.Dura
}
}

func (w *Watcher) dialController(ctx context.Context, ctl *dbmodel.Controller) (API, error) {
func (w *Watcher) dialController(ctx context.Context, ctl *dbmodel.Controller) (api API, err error) {
const op = errors.Op("jimm.dialController")

// connect to the controller
api, err := w.Dialer.Dial(ctx, ctl, names.ModelTag{}, nil)
if err != nil {
if !ctl.UnavailableSince.Valid {
ctl.UnavailableSince = db.Now()
var err error
if err = w.Database.UpdateController(ctx, ctl); err != nil {
zapctx.Error(ctx, "cannot set controller unavailable", zap.Error(err))
}
if w.controllerUnavailableChan != nil {
select {
case w.controllerUnavailableChan <- err:
default:
}
updateController := false
defer func() {
if !updateController {
return
}
if uerr := w.Database.UpdateController(ctx, ctl); err != nil {
zapctx.Error(ctx, "cannot set controller available", zap.Error(uerr))
}
// Note (alesstimec) This channel is only available in tests.
if w.controllerUnavailableChan != nil {
select {
case w.controllerUnavailableChan <- err:
default:
}
}
}()

// connect to the controller
api, err = w.Dialer.Dial(ctx, ctl, names.ModelTag{}, nil)
if err != nil {
ctl.UnavailableSince = db.Now()
updateController = true

return nil, errors.E(op, err)
}
if ctl.UnavailableSince.Valid {
ctl.UnavailableSince = sql.NullTime{}
updateController = true
}
return api, nil
}

Expand Down Expand Up @@ -350,19 +361,6 @@ func (w *Watcher) watchAllModelSummaries(ctx context.Context, ctl *dbmodel.Contr
// connect to the controller
api, err := w.dialController(ctx, ctl)
if err != nil {
if !ctl.UnavailableSince.Valid {
ctl.UnavailableSince = db.Now()
var err error
if err = w.Database.UpdateController(ctx, ctl); err != nil {
zapctx.Error(ctx, "cannot set controller unavailable", zap.Error(err))
}
if w.controllerUnavailableChan != nil {
select {
case w.controllerUnavailableChan <- err:
default:
}
}
}
return errors.E(op, err)
}
defer api.Close()
Expand Down
73 changes: 71 additions & 2 deletions internal/jimm/watcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -805,13 +805,82 @@ func TestWatcherSetsControllerUnavailable(t *testing.T) {
err = w.Database.GetController(ctx, &ctl)
c.Assert(err, qt.IsNil)
c.Check(ctl.UnavailableSince.Valid, qt.Equals, true)
c.Logf("%v %v", ctl.UnavailableSince.Time, time.Now())
c.Check(ctl.UnavailableSince.Time.After(time.Now().Add(-10*time.Millisecond)), qt.Equals, true)
}
cancel()
wg.Wait()
}

func TestWatcherClearsControllerUnavailable(t *testing.T) {
c := qt.New(t)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

w := jimm.Watcher{
Database: db.Database{
DB: jimmtest.PostgresDB(c, nil),
},
Dialer: &jimmtest.Dialer{
API: &jimmtest.API{
AllModelWatcherNext_: func(_ context.Context, _ string) ([]jujuparams.Delta, error) {
cancel()
<-ctx.Done()
return nil, ctx.Err()
},
ModelInfo_: func(_ context.Context, info *jujuparams.ModelInfo) error {
switch info.UUID {
default:
c.Errorf("unexpected model uuid: %s", info.UUID)
case "00000002-0000-0000-0000-000000000002":
case "00000002-0000-0000-0000-000000000003":
}
return errors.E(errors.CodeNotFound)
},
WatchAllModels_: func(ctx context.Context) (string, error) {
return "1234", nil
},
},
},
Pubsub: &testPublisher{},
}

env := jimmtest.ParseEnvironment(c, testWatcherEnv)
err := w.Database.Migrate(ctx, false)
c.Assert(err, qt.IsNil)
env.PopulateDB(c, w.Database)

// update controller's UnavailableSince field
ctl := dbmodel.Controller{
Name: "controller-1",
}
err = w.Database.GetController(ctx, &ctl)
c.Assert(err, qt.IsNil)
ctl.UnavailableSince = sql.NullTime{
Time: time.Now(),
Valid: true,
}
err = w.Database.UpdateController(ctx, &ctl)
c.Assert(err, qt.IsNil)

// start the watcher
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
err := w.Watch(ctx, time.Millisecond)
checkIfContextCanceled(c, ctx, err)
}()
wg.Wait()

// check that the unavailable since time has been cleared
ctl = dbmodel.Controller{
Name: "controller-1",
}
err = w.Database.GetController(context.Background(), &ctl)
c.Assert(err, qt.IsNil)
c.Assert(ctl.UnavailableSince.Valid, qt.IsFalse)
}

func TestWatcherRemoveDyingModelsOnStartup(t *testing.T) {
c := qt.New(t)

Expand Down

0 comments on commit 10789ab

Please sign in to comment.