wip: migrate to mono-repo. SPN has already been moved to spn/
This commit is contained in:
175
service/broadcasts/install_info.go
Normal file
175
service/broadcasts/install_info.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package broadcasts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
semver "github.com/hashicorp/go-version"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/info"
|
||||
"github.com/safing/portbase/log"
|
||||
)
|
||||
|
||||
const installInfoDBKey = "core:status/install-info"
|
||||
|
||||
// InstallInfo holds generic info about the install.
|
||||
type InstallInfo struct {
|
||||
record.Base
|
||||
sync.Mutex
|
||||
|
||||
Version string
|
||||
NumericVersion int64
|
||||
|
||||
Time time.Time
|
||||
NumericDate int64
|
||||
DaysSinceInstall int64
|
||||
UnixTimestamp int64
|
||||
}
|
||||
|
||||
// GetInstallInfo returns the install info from the database.
|
||||
func GetInstallInfo() (*InstallInfo, error) {
|
||||
r, err := db.Get(installInfoDBKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Unwrap.
|
||||
if r.IsWrapped() {
|
||||
// Only allocate a new struct, if we need it.
|
||||
newRecord := &InstallInfo{}
|
||||
err = record.Unwrap(r, newRecord)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newRecord, nil
|
||||
}
|
||||
|
||||
// or adjust type
|
||||
newRecord, ok := r.(*InstallInfo)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("record not of type *InstallInfo, but %T", r)
|
||||
}
|
||||
return newRecord, nil
|
||||
}
|
||||
|
||||
func ensureInstallInfo() {
|
||||
// Get current install info from database.
|
||||
installInfo, err := GetInstallInfo()
|
||||
if err != nil {
|
||||
installInfo = &InstallInfo{}
|
||||
if !errors.Is(err, database.ErrNotFound) {
|
||||
log.Warningf("updates: failed to load install info: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in missing data and save.
|
||||
installInfo.checkAll()
|
||||
if err := installInfo.save(); err != nil {
|
||||
log.Warningf("updates: failed to save install info: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (ii *InstallInfo) save() error {
|
||||
if !ii.KeyIsSet() {
|
||||
ii.SetKey(installInfoDBKey)
|
||||
}
|
||||
return db.Put(ii)
|
||||
}
|
||||
|
||||
func (ii *InstallInfo) checkAll() {
|
||||
ii.checkVersion()
|
||||
ii.checkInstallDate()
|
||||
}
|
||||
|
||||
func (ii *InstallInfo) checkVersion() {
|
||||
// Check if everything is present.
|
||||
if ii.Version != "" && ii.NumericVersion > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Update version information.
|
||||
versionInfo := info.GetInfo()
|
||||
ii.Version = versionInfo.Version
|
||||
|
||||
// Update numeric version.
|
||||
if versionInfo.Version != "" {
|
||||
numericVersion, err := MakeNumericVersion(versionInfo.Version)
|
||||
if err != nil {
|
||||
log.Warningf("updates: failed to make numeric version: %s", err)
|
||||
} else {
|
||||
ii.NumericVersion = numericVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MakeNumericVersion makes a numeric version with the first three version
|
||||
// segment always using three digits.
|
||||
func MakeNumericVersion(version string) (numericVersion int64, err error) {
|
||||
// Parse version string.
|
||||
ver, err := semver.NewVersion(version)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to parse core version: %w", err)
|
||||
}
|
||||
|
||||
// Transform version for numeric representation.
|
||||
segments := ver.Segments()
|
||||
for i := 0; i < 3 && i < len(segments); i++ {
|
||||
segmentNumber := int64(segments[i])
|
||||
if segmentNumber > 999 {
|
||||
segmentNumber = 999
|
||||
}
|
||||
switch i {
|
||||
case 0:
|
||||
numericVersion += segmentNumber * 1000000
|
||||
case 1:
|
||||
numericVersion += segmentNumber * 1000
|
||||
case 2:
|
||||
numericVersion += segmentNumber
|
||||
}
|
||||
}
|
||||
|
||||
return numericVersion, nil
|
||||
}
|
||||
|
||||
func (ii *InstallInfo) checkInstallDate() {
|
||||
// Check if everything is present.
|
||||
if ii.UnixTimestamp > 0 &&
|
||||
ii.NumericDate > 0 &&
|
||||
ii.DaysSinceInstall > 0 &&
|
||||
!ii.Time.IsZero() {
|
||||
return
|
||||
}
|
||||
|
||||
// Find oldest created database entry and use it as install time.
|
||||
oldest := time.Now().Unix()
|
||||
it, err := db.Query(query.New("core"))
|
||||
if err != nil {
|
||||
log.Warningf("updates: failed to create iterator for searching DB for install time: %s", err)
|
||||
return
|
||||
}
|
||||
defer it.Cancel()
|
||||
for r := range it.Next {
|
||||
if oldest > r.Meta().Created {
|
||||
oldest = r.Meta().Created
|
||||
}
|
||||
}
|
||||
|
||||
// Set data.
|
||||
ii.UnixTimestamp = oldest
|
||||
ii.Time = time.Unix(oldest, 0)
|
||||
ii.DaysSinceInstall = int64(time.Since(ii.Time).Hours()) / 24
|
||||
|
||||
// Transform date for numeric representation.
|
||||
numericDate, err := strconv.ParseInt(ii.Time.Format("20060102"), 10, 64)
|
||||
if err != nil {
|
||||
log.Warningf("updates: failed to make numeric date from %s: %s", ii.Time, err)
|
||||
} else {
|
||||
ii.NumericDate = numericDate
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user