internal/version: do version checks much earlier in the process initialization, take 2 (#1666)
* internal/version: do version checks much earlier in the process initialization, take 2 Change-Id: Ida8c7e3757e0deea0ec7aea867d3d27ce97dc134 * linter and test failures Change-Id: I45b02a16ec1c0f0981227dc842e68dbdf67fdbf4
This commit is contained in:
parent
8549421385
commit
09be9964eb
@ -98,8 +98,8 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, config Config, ver
|
||||
if test != versionInfo {
|
||||
peer.Log.Sugar().Debugf("Binary Version: %s with CommitHash %s, built at %s as Release %v",
|
||||
versionInfo.Version.String(), versionInfo.CommitHash, versionInfo.Timestamp.String(), versionInfo.Release)
|
||||
peer.Version = version.NewService(config.Version, versionInfo, "Bootstrap")
|
||||
}
|
||||
peer.Version = version.NewService(config.Version, versionInfo, "Bootstrap")
|
||||
}
|
||||
|
||||
{ // setup listener and server
|
||||
@ -189,10 +189,7 @@ func (peer *Peer) Run(ctx context.Context) error {
|
||||
group, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
group.Go(func() error {
|
||||
if peer.Version != nil {
|
||||
return ignoreCancel(peer.Version.Run(ctx))
|
||||
}
|
||||
return nil
|
||||
return ignoreCancel(peer.Version.Run(ctx))
|
||||
})
|
||||
group.Go(func() error {
|
||||
return ignoreCancel(peer.Kademlia.Service.Bootstrap(ctx))
|
||||
|
@ -62,6 +62,9 @@ func init() {
|
||||
}
|
||||
|
||||
func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
// inert constructors only ====
|
||||
|
||||
ctx := process.Ctx(cmd)
|
||||
log := zap.L()
|
||||
|
||||
identity, err := runCfg.Identity.Load()
|
||||
@ -74,11 +77,6 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := process.Ctx(cmd)
|
||||
if err := process.InitMetricsWithCertPath(ctx, nil, runCfg.Identity.CertPath); err != nil {
|
||||
zap.S().Error("Failed to initialize telemetry batcher: ", err)
|
||||
}
|
||||
|
||||
db, err := bootstrapdb.New(bootstrapdb.Config{
|
||||
Kademlia: runCfg.Kademlia.DBPath,
|
||||
})
|
||||
@ -90,16 +88,27 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
err = errs.Combine(err, db.Close())
|
||||
}()
|
||||
|
||||
err = db.CreateTables()
|
||||
if err != nil {
|
||||
return errs.New("Error creating tables for master database on bootstrap: %+v", err)
|
||||
}
|
||||
|
||||
peer, err := bootstrap.New(log, identity, db, runCfg, version.Build)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// okay, start doing stuff ====
|
||||
|
||||
err = peer.Version.CheckVersion(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := process.InitMetricsWithCertPath(ctx, nil, runCfg.Identity.CertPath); err != nil {
|
||||
zap.S().Error("Failed to initialize telemetry batcher: ", err)
|
||||
}
|
||||
|
||||
err = db.CreateTables()
|
||||
if err != nil {
|
||||
return errs.New("Error creating tables for master database on bootstrap: %+v", err)
|
||||
}
|
||||
|
||||
runError := peer.Run(ctx)
|
||||
closeError := peer.Close()
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/storj/internal/fpath"
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/pkg/certificates"
|
||||
"storj.io/storj/pkg/cfgstruct"
|
||||
"storj.io/storj/pkg/identity"
|
||||
@ -79,6 +80,13 @@ func serviceDirectory(serviceName string) string {
|
||||
}
|
||||
|
||||
func cmdNewService(cmd *cobra.Command, args []string) error {
|
||||
ctx := process.Ctx(cmd)
|
||||
|
||||
err := version.CheckProcessVersion(ctx, version.Config{}, version.Build, "Identity")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceDir := serviceDirectory(args[0])
|
||||
|
||||
caCertPath := filepath.Join(serviceDir, "ca.cert")
|
||||
@ -116,7 +124,7 @@ func cmdNewService(cmd *cobra.Command, args []string) error {
|
||||
return errs.New("Identity certificate and/or key already exists, NOT overwriting!")
|
||||
}
|
||||
|
||||
ca, caerr := caConfig.Create(process.Ctx(cmd), os.Stdout)
|
||||
ca, caerr := caConfig.Create(ctx, os.Stdout)
|
||||
if caerr != nil {
|
||||
return caerr
|
||||
}
|
||||
@ -135,6 +143,11 @@ func cmdNewService(cmd *cobra.Command, args []string) error {
|
||||
func cmdAuthorize(cmd *cobra.Command, args []string) error {
|
||||
ctx := process.Ctx(cmd)
|
||||
|
||||
err := version.CheckProcessVersion(ctx, version.Config{}, version.Build, "Identity")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceDir := serviceDirectory(args[0])
|
||||
|
||||
authToken := args[1]
|
||||
|
@ -107,6 +107,9 @@ func init() {
|
||||
}
|
||||
|
||||
func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
// inert constructors only ====
|
||||
|
||||
ctx := process.Ctx(cmd)
|
||||
log := zap.L()
|
||||
|
||||
identity, err := runCfg.Identity.Load()
|
||||
@ -114,13 +117,7 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
zap.S().Fatal(err)
|
||||
}
|
||||
|
||||
ctx := process.Ctx(cmd)
|
||||
if err := process.InitMetricsWithCertPath(ctx, nil, runCfg.Identity.CertPath); err != nil {
|
||||
zap.S().Error("Failed to initialize telemetry batcher: ", err)
|
||||
}
|
||||
|
||||
db, err := satellitedb.New(log.Named("db"), runCfg.Database)
|
||||
|
||||
if err != nil {
|
||||
return errs.New("Error starting master database on satellite: %+v", err)
|
||||
}
|
||||
@ -129,16 +126,27 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
err = errs.Combine(err, db.Close())
|
||||
}()
|
||||
|
||||
err = db.CreateTables()
|
||||
if err != nil {
|
||||
return errs.New("Error creating tables for master database on satellite: %+v", err)
|
||||
}
|
||||
|
||||
peer, err := satellite.New(log, identity, db, &runCfg.Config, version.Build)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// okay, start doing stuff ====
|
||||
|
||||
err = peer.Version.CheckVersion(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := process.InitMetricsWithCertPath(ctx, nil, runCfg.Identity.CertPath); err != nil {
|
||||
zap.S().Error("Failed to initialize telemetry batcher: ", err)
|
||||
}
|
||||
|
||||
err = db.CreateTables()
|
||||
if err != nil {
|
||||
return errs.New("Error creating tables for master database on satellite: %+v", err)
|
||||
}
|
||||
|
||||
runError := peer.Run(ctx)
|
||||
closeError := peer.Close()
|
||||
return errs.Combine(runError, closeError)
|
||||
|
@ -116,6 +116,9 @@ func databaseConfig(config storagenode.Config) storagenodedb.Config {
|
||||
}
|
||||
|
||||
func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
// inert constructors only ====
|
||||
|
||||
ctx := process.Ctx(cmd)
|
||||
log := zap.L()
|
||||
|
||||
identity, err := runCfg.Identity.Load()
|
||||
@ -128,13 +131,7 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := process.Ctx(cmd)
|
||||
if err := process.InitMetricsWithCertPath(ctx, nil, runCfg.Identity.CertPath); err != nil {
|
||||
zap.S().Error("Failed to initialize telemetry batcher: ", err)
|
||||
}
|
||||
|
||||
db, err := storagenodedb.New(log.Named("db"), databaseConfig(runCfg.Config))
|
||||
|
||||
if err != nil {
|
||||
return errs.New("Error starting master database on storagenode: %+v", err)
|
||||
}
|
||||
@ -143,16 +140,27 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
err = errs.Combine(err, db.Close())
|
||||
}()
|
||||
|
||||
err = db.CreateTables()
|
||||
if err != nil {
|
||||
return errs.New("Error creating tables for master database on storagenode: %+v", err)
|
||||
}
|
||||
|
||||
peer, err := storagenode.New(log, identity, db, runCfg.Config, version.Build)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// okay, start doing stuff ====
|
||||
|
||||
err = peer.Version.CheckVersion(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := process.InitMetricsWithCertPath(ctx, nil, runCfg.Identity.CertPath); err != nil {
|
||||
zap.S().Error("Failed to initialize telemetry batcher: ", err)
|
||||
}
|
||||
|
||||
err = db.CreateTables()
|
||||
if err != nil {
|
||||
return errs.New("Error creating tables for master database on storagenode: %+v", err)
|
||||
}
|
||||
|
||||
runError := peer.Run(ctx)
|
||||
closeError := peer.Close()
|
||||
|
||||
|
@ -6,21 +6,17 @@ package version
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"storj.io/storj/internal/sync2"
|
||||
)
|
||||
|
||||
const (
|
||||
errOldVersion = "Outdated Software Version, please update!"
|
||||
)
|
||||
|
||||
// Config contains the necessary Information to check the Software Version
|
||||
type Config struct {
|
||||
ServerAddress string `help:"server address to check its version against" default:"https://version.alpha.storj.io"`
|
||||
@ -36,7 +32,7 @@ type Service struct {
|
||||
|
||||
Loop *sync2.Cycle
|
||||
|
||||
checked chan struct{}
|
||||
checked sync2.Fence
|
||||
mu sync.Mutex
|
||||
allowed bool
|
||||
}
|
||||
@ -48,75 +44,87 @@ func NewService(config Config, info Info, service string) (client *Service) {
|
||||
info: info,
|
||||
service: service,
|
||||
Loop: sync2.NewCycle(config.CheckInterval),
|
||||
checked: make(chan struct{}, 0),
|
||||
allowed: false,
|
||||
allowed: true,
|
||||
}
|
||||
}
|
||||
|
||||
// CheckVersion checks to make sure the version is still okay, returning an error if not
|
||||
func (srv *Service) CheckVersion(ctx context.Context) error {
|
||||
if !srv.checkVersion(ctx) {
|
||||
return fmt.Errorf("outdated software version (%v), please update", srv.info.Version.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckProcessVersion is not meant to be used for peers but is meant to be
|
||||
// used for other utilities
|
||||
func CheckProcessVersion(ctx context.Context, config Config, info Info, service string) error {
|
||||
return NewService(config, info, service).CheckVersion(ctx)
|
||||
}
|
||||
|
||||
// Run logs the current version information
|
||||
func (srv *Service) Run(ctx context.Context) error {
|
||||
firstCheck := true
|
||||
return srv.Loop.Run(ctx, func(ctx context.Context) error {
|
||||
var err error
|
||||
allowed, err := srv.checkVersion(ctx)
|
||||
if !srv.checked.Released() {
|
||||
err := srv.CheckVersion(ctx)
|
||||
if err != nil {
|
||||
// Log about the error, but dont crash the service and allow further operation
|
||||
zap.S().Errorf("Failed to do periodic version check: ", err)
|
||||
allowed = true
|
||||
return err
|
||||
}
|
||||
|
||||
srv.mu.Lock()
|
||||
srv.allowed = allowed
|
||||
srv.mu.Unlock()
|
||||
|
||||
if firstCheck {
|
||||
close(srv.checked)
|
||||
firstCheck = false
|
||||
if !allowed {
|
||||
zap.S().Fatal(errOldVersion)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return srv.Loop.Run(ctx, func(ctx context.Context) error {
|
||||
srv.checkVersion(ctx)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// IsUpToDate returns whether if the Service is allowed to operate or not
|
||||
func (srv *Service) IsUpToDate() bool {
|
||||
<-srv.checked
|
||||
|
||||
// IsAllowed returns whether if the Service is allowed to operate or not
|
||||
func (srv *Service) IsAllowed() bool {
|
||||
srv.checked.Wait()
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
|
||||
return srv.allowed
|
||||
}
|
||||
|
||||
// CheckVersion checks if the client is running latest/allowed code
|
||||
func (srv *Service) checkVersion(ctx context.Context) (allowed bool, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
func (srv *Service) checkVersion(ctx context.Context) (allowed bool) {
|
||||
defer mon.Task()(&ctx)(nil)
|
||||
|
||||
defer func() {
|
||||
srv.mu.Lock()
|
||||
srv.allowed = allowed
|
||||
srv.mu.Unlock()
|
||||
srv.checked.Release()
|
||||
}()
|
||||
|
||||
if !srv.info.Release {
|
||||
return true
|
||||
}
|
||||
|
||||
accepted, err := srv.queryVersionFromControlServer(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
// Log about the error, but dont crash the service and allow further operation
|
||||
zap.S().Errorf("Failed to do periodic version check: ", err)
|
||||
return true
|
||||
}
|
||||
|
||||
list := getFieldString(&accepted, srv.service)
|
||||
zap.S().Debugf("allowed versions from Control Server: %v", list)
|
||||
|
||||
if list == nil {
|
||||
return true, errs.New("Empty List from Versioning Server")
|
||||
zap.S().Errorf("Empty List from Versioning Server")
|
||||
return true
|
||||
}
|
||||
if containsVersion(list, srv.info.Version) {
|
||||
zap.S().Infof("running on version %s", srv.info.Version.String())
|
||||
allowed = true
|
||||
} else {
|
||||
zap.S().Errorf("running on not allowed/outdated version %s", srv.info.Version.String())
|
||||
allowed = false
|
||||
return true
|
||||
}
|
||||
return allowed, err
|
||||
zap.S().Errorf("running on not allowed/outdated version %s", srv.info.Version.String())
|
||||
return false
|
||||
}
|
||||
|
||||
// QueryVersionFromControlServer handles the HTTP request to gather the allowed and latest version information
|
||||
func (srv *Service) queryVersionFromControlServer(ctx context.Context) (ver AllowedVersions, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
// Tune Client to have a custom Timeout (reduces hanging software)
|
||||
client := http.Client{
|
||||
Timeout: srv.config.RequestTimeout,
|
||||
|
@ -205,8 +205,8 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, config *Config, ve
|
||||
if test != versionInfo {
|
||||
peer.Log.Sugar().Debugf("Binary Version: %s with CommitHash %s, built at %s as Release %v",
|
||||
versionInfo.Version.String(), versionInfo.CommitHash, versionInfo.Timestamp.String(), versionInfo.Release)
|
||||
peer.Version = version.NewService(config.Version, versionInfo, "Satellite")
|
||||
}
|
||||
peer.Version = version.NewService(config.Version, versionInfo, "Satellite")
|
||||
}
|
||||
|
||||
{ // setup listener and server
|
||||
@ -528,10 +528,7 @@ func (peer *Peer) Run(ctx context.Context) error {
|
||||
group, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
group.Go(func() error {
|
||||
if peer.Version != nil {
|
||||
return ignoreCancel(peer.Version.Run(ctx))
|
||||
}
|
||||
return nil
|
||||
return ignoreCancel(peer.Version.Run(ctx))
|
||||
})
|
||||
group.Go(func() error {
|
||||
return ignoreCancel(peer.Kademlia.Service.Bootstrap(ctx))
|
||||
|
@ -122,8 +122,8 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, config Config, ver
|
||||
if test != versionInfo {
|
||||
peer.Log.Sugar().Debugf("Binary Version: %s with CommitHash %s, built at %s as Release %v",
|
||||
versionInfo.Version.String(), versionInfo.CommitHash, versionInfo.Timestamp.String(), versionInfo.Release)
|
||||
peer.Version = version.NewService(config.Version, versionInfo, "Storagenode")
|
||||
}
|
||||
peer.Version = version.NewService(config.Version, versionInfo, "Storagenode")
|
||||
}
|
||||
|
||||
{ // setup listener and server
|
||||
@ -253,10 +253,7 @@ func (peer *Peer) Run(ctx context.Context) error {
|
||||
group, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
group.Go(func() error {
|
||||
if peer.Version != nil {
|
||||
return ignoreCancel(peer.Version.Run(ctx))
|
||||
}
|
||||
return nil
|
||||
return ignoreCancel(peer.Version.Run(ctx))
|
||||
})
|
||||
group.Go(func() error {
|
||||
return ignoreCancel(peer.Kademlia.Service.Bootstrap(ctx))
|
||||
|
Loading…
Reference in New Issue
Block a user