storj/private/version/checker/service.go
Jess G 75b9a5971e
satellite: update log levels (#3851)
* satellite: update log levels

Change-Id: I86bc32e042d742af6dbc469a294291a2e667e81f

* log version on start up for every service

Change-Id: Ic128bb9c5ac52d4dc6d6c4cb3059fbad73f5d3de

* Use monkit for tracking failed ip resolutions

Change-Id: Ia5aa71d315515e0c5f62c98d9d115ef984cd50c2

* fix compile errors

Change-Id: Ia33c8b6e34e780bd1115120dc347a439d99e83bf

* add request limit value to storage node rpc err

Change-Id: I1ad6706a60237928e29da300d96a1bafa94156e5

* we cant track storage node ids in monkit metrics so lets use logging to track that for expired orders

Change-Id: I1cc1d240b29019ae2f8c774792765df3cbeac887

* fix build errs

Change-Id: I6d0ffe058e9a38b7ed031c85a29440f3d68e8d47
2020-04-15 12:32:22 -07:00

148 lines
4.4 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package checker
import (
"context"
"fmt"
"sync"
"time"
"go.uber.org/zap"
"storj.io/common/sync2"
"storj.io/private/version"
)
// Config contains the necessary Information to check the Software Version
type Config struct {
ClientConfig
CheckInterval time.Duration `help:"Interval to check the version" default:"0h15m0s"`
}
// Service contains the information and variables to ensure the Software is up to date
//
// architecture: Service
type Service struct {
log *zap.Logger
config Config
client *Client
Info version.Info
service string
checked sync2.Fence
mu sync.Mutex
allowed bool
acceptedVersion version.SemVer
}
// NewService creates a Version Check Client with default configuration
func NewService(log *zap.Logger, config Config, info version.Info, service string) (client *Service) {
return &Service{
log: log,
config: config,
client: New(config.ClientConfig),
Info: info,
service: service,
allowed: true,
}
}
// CheckProcessVersion is not meant to be used for peers but is meant to be
// used for other utilities
func CheckProcessVersion(ctx context.Context, log *zap.Logger, config Config, info version.Info, service string) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = NewService(log, config, info, service).CheckVersion(ctx)
return err
}
// IsAllowed returns whether if the Service is allowed to operate or not.
func (service *Service) IsAllowed(ctx context.Context) (version.SemVer, bool) {
if !service.checked.Wait(ctx) {
return version.SemVer{}, false
}
service.mu.Lock()
defer service.mu.Unlock()
return service.acceptedVersion, service.allowed
}
// CheckVersion checks to make sure the version is still relevant and returns suggested version, returning an error if not.
func (service *Service) CheckVersion(ctx context.Context) (latest version.SemVer, err error) {
defer mon.Task()(&ctx)(&err)
latest, allowed := service.checkVersion(ctx)
if !allowed {
return latest, fmt.Errorf("outdated software version (%s), please update", service.Info.Version.String())
}
return latest, nil
}
// checkVersion checks if the client is running latest/allowed version.
func (service *Service) checkVersion(ctx context.Context) (latestVersion version.SemVer, allowed bool) {
var err error
defer mon.Task()(&ctx)(&err)
var minimum version.SemVer
defer func() {
service.mu.Lock()
service.allowed = allowed
if err == nil {
service.acceptedVersion = minimum
}
service.mu.Unlock()
service.checked.Release()
}()
allowedVersions, err := service.client.All(ctx)
if err != nil {
return service.acceptedVersion, true
}
suggestedVersion, err := allowedVersions.Processes.Storagenode.Suggested.SemVer()
if err != nil {
return service.acceptedVersion, true
}
if !service.Info.Release {
minimum = service.Info.Version
return suggestedVersion, true
}
minimumOld, err := service.client.OldMinimum(ctx, service.service)
if err != nil {
// Log about the error, but dont crash the Service and allow further operation
service.log.Error("Failed to do periodic version check.", zap.Error(err))
return suggestedVersion, true
}
minimum, err = version.NewSemVer(minimumOld.String())
if err != nil {
service.log.Error("Failed to convert old sem version to sem version.")
return suggestedVersion, true
}
service.log.Debug("Allowed minimum version from control server.", zap.Stringer("Minimum Version", minimum.Version))
if isAcceptedVersion(service.Info.Version, minimumOld) {
service.log.Debug("Running on allowed version.", zap.Stringer("Version", service.Info.Version.Version))
return suggestedVersion, true
}
service.log.Warn("version not allowed/outdated",
zap.Stringer("current version", service.Info.Version.Version),
zap.Stringer("minimum allowed version", minimumOld),
)
return suggestedVersion, false
}
// Checked returns whether the version has been updated.
func (service *Service) Checked() bool {
return service.checked.Released()
}
// isAcceptedVersion compares and checks if the passed version is greater/equal than the minimum required version
func isAcceptedVersion(test version.SemVer, target version.OldSemVer) bool {
return test.Major > uint64(target.Major) || (test.Major == uint64(target.Major) && (test.Minor > uint64(target.Minor) || (test.Minor == uint64(target.Minor) && test.Patch >= uint64(target.Patch))))
}