Skip to content

Commit

Permalink
Limit update checks to once per day
Browse files Browse the repository at this point in the history
In #2893 we made more commands check for updates. Strictly not all of
these checks are necessary as the code is unlikely to have changed
regularly. Instead we should limit the amount we check for updates to
(perhaps) once per day.

Every time we perform an update check, we write the current time into
update.json. Every time we run a command. if the current time is less
than 24h after this value, don't do any checking. If the current time
is 24h ahead of this value, do an update check.
  • Loading branch information
simonwo committed Oct 16, 2023
1 parent 2be3b90 commit c2beb90
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 25 deletions.
7 changes: 4 additions & 3 deletions cmd/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func NewRootCmd() *cobra.Command {
PreRun: util.StartUpdateCheck,
PostRun: util.PrintUpdateCheck,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
repoDir, err := config.Get[string]("repo")
if err != nil {
util.Fatal(cmd, fmt.Errorf("failed to read --repo value: %w", err), 1)
Expand All @@ -58,16 +59,16 @@ func NewRootCmd() *cobra.Command {
// didn't provide on.
util.Fatal(cmd, fmt.Errorf("bacalhau repo not set, please use BACALHAU_DIR or --repo"), 1)
}
if _, err := setup.SetupBacalhauRepo(repoDir); err != nil {
if fsRepo, err := setup.SetupBacalhauRepo(repoDir); err != nil {
util.Fatal(cmd, fmt.Errorf("failed to initialize bacalhau repo at '%s': %w", repoDir, err), 1)
} else {
ctx = context.WithValue(ctx, util.FSRepoKey, fsRepo)
}

if err := configflags.BindFlags(cmd, rootFlags); err != nil {
util.Fatal(cmd, err, 1)
}

ctx := cmd.Context()

logger.ConfigureLogging(util.LoggingMode)

cm := system.NewCleanupManager()
Expand Down
17 changes: 0 additions & 17 deletions cmd/util/cleanup.go

This file was deleted.

25 changes: 25 additions & 0 deletions cmd/util/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package util

import (
"context"

"github.com/bacalhau-project/bacalhau/pkg/repo"
"github.com/bacalhau-project/bacalhau/pkg/system"
)

type contextKey struct {
name string
}

var (
SystemManagerKey = contextKey{name: "context key for storing the system manager"}
FSRepoKey = contextKey{name: "context key for storing the filesystem-based repo"}
)

func GetCleanupManager(ctx context.Context) *system.CleanupManager {
return ctx.Value(SystemManagerKey).(*system.CleanupManager)
}

func GetFSRepo(ctx context.Context) *repo.FsRepo {
return ctx.Value(FSRepoKey).(*repo.FsRepo)
}
73 changes: 68 additions & 5 deletions cmd/util/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package util

import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"

"github.com/Masterminds/semver"
"github.com/pkg/errors"
Expand Down Expand Up @@ -65,23 +69,82 @@ func GetAllVersions(ctx context.Context) (Versions, error) {

var printMessage *string = nil

const updateStatePath string = "update.json"
const updateFrequency time.Duration = 24 * time.Hour

// StartUpdateCheck is a Cobra pre run hook to run an update check in the
// background. There should be no output if the check fails or the context is
// cancelled before the check can complete.
func StartUpdateCheck(cmd *cobra.Command, args []string) {
go func(ctx context.Context) {
if skip, err := config.Get[bool](types.SkipUpdateCheck); skip || err != nil {
log.Ctx(ctx).Debug().Err(err).Bool(types.SkipUpdateCheck, skip).Msg("Skipping update check")
return
}
if skip, err := config.Get[bool](types.SkipUpdateCheck); skip || err != nil {
log.Ctx(cmd.Context()).Debug().Err(err).Bool(types.SkipUpdateCheck, skip).Msg("Skipping update check due to config")
return
}

lastCheck, err := readLastCheckTime(cmd.Context())
if err == nil && time.Since(lastCheck) < updateFrequency {
log.Ctx(cmd.Context()).Debug().Time("LastCheck", lastCheck).Msg("Skipping update check as there was a recent enough check")
return
} else if err != nil {
log.Ctx(cmd.Context()).Warn().Err(err).Msg("Error reading update check state – will perform check anyway")
}

go func(ctx context.Context) {
versions, err := GetAllVersions(ctx)
if err == nil {
printMessage = &versions.UpdateMessage
err = writeNewLastCheckTime(ctx)
log.Ctx(ctx).Debug().Err(err).Str("LatestVersion", versions.LatestVersion.GitVersion).Msg("Performed update check")
}
}(cmd.Context())
}

type updateState struct {
LastCheck time.Time
}

func readLastCheckTime(ctx context.Context) (time.Time, error) {
path, err := GetFSRepo(ctx).Path()
if err != nil {
return time.Now(), errors.Wrap(err, "error getting repo path")
}

file, err := os.Open(filepath.Join(path, updateStatePath))
if err != nil {
return time.Now(), errors.Wrap(err, "error opening update state file")
}
defer file.Close()

var state updateState
err = json.NewDecoder(file).Decode(&state)
if err != nil {
return time.Now(), errors.Wrap(err, "error reading update state")
}

return state.LastCheck, nil
}

func writeNewLastCheckTime(ctx context.Context) error {
path, err := GetFSRepo(ctx).Path()
if err != nil {
return errors.Wrap(err, "error getting repo path")
}

file, err := os.Create(filepath.Join(path, updateStatePath))
if err != nil {
return errors.Wrap(err, "error creating update state file")
}
defer file.Close()

state := updateState{LastCheck: time.Now()}
err = json.NewEncoder(file).Encode(&state)
if err != nil {
return errors.Wrap(err, "error writing update state")
}

return nil
}

// PrintUpdateCheck is a Cobra post run hook to print the results of an update
// check. The message will be a non-nil pointer only if the update check
// succeeds and should only have visible output if the message is non-empty.
Expand Down

0 comments on commit c2beb90

Please sign in to comment.