{versioncontrol,internal/version,cmd/*}: refactor version control (#3253)
This commit is contained in:
parent
f65801309c
commit
243ba1cb17
@ -11,7 +11,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
base58 "github.com/jbenet/go-base58"
|
||||
"github.com/jbenet/go-base58"
|
||||
"github.com/minio/cli"
|
||||
minio "github.com/minio/minio/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
@ -21,6 +21,7 @@ import (
|
||||
"storj.io/storj/cmd/internal/wizard"
|
||||
"storj.io/storj/internal/fpath"
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/internal/version/checker"
|
||||
libuplink "storj.io/storj/lib/uplink"
|
||||
"storj.io/storj/pkg/cfgstruct"
|
||||
"storj.io/storj/pkg/miniogw"
|
||||
@ -38,7 +39,7 @@ type GatewayFlags struct {
|
||||
|
||||
uplink.Config
|
||||
|
||||
Version version.Config
|
||||
Version checker.Config
|
||||
}
|
||||
|
||||
var (
|
||||
@ -140,7 +141,7 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
zap.S().Warn("Failed to initialize telemetry batcher: ", err)
|
||||
}
|
||||
|
||||
err = version.CheckProcessVersion(ctx, zap.L(), runCfg.Version, version.Build, "Gateway")
|
||||
err = checker.CheckProcessVersion(ctx, zap.L(), runCfg.Version, version.Build, "Gateway")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"storj.io/storj/certificate/certificateclient"
|
||||
"storj.io/storj/internal/fpath"
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/internal/version/checker"
|
||||
"storj.io/storj/pkg/cfgstruct"
|
||||
"storj.io/storj/pkg/identity"
|
||||
"storj.io/storj/pkg/peertls/extensions"
|
||||
@ -63,7 +64,7 @@ var (
|
||||
// TODO: ideally the default is the latest version; can't interpolate struct tags
|
||||
IdentityVersion uint `default:"0" help:"identity version to use when creating an identity or CA"`
|
||||
|
||||
Version version.Config
|
||||
Version checker.Config
|
||||
}
|
||||
|
||||
identityDir, configDir string
|
||||
@ -90,7 +91,7 @@ func serviceDirectory(serviceName string) string {
|
||||
func cmdNewService(cmd *cobra.Command, args []string) error {
|
||||
ctx, _ := process.Ctx(cmd)
|
||||
|
||||
err := version.CheckProcessVersion(ctx, zap.L(), config.Version, version.Build, "Identity")
|
||||
err := checker.CheckProcessVersion(ctx, zap.L(), config.Version, version.Build, "Identity")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -152,7 +153,7 @@ func cmdNewService(cmd *cobra.Command, args []string) error {
|
||||
func cmdAuthorize(cmd *cobra.Command, args []string) (err error) {
|
||||
ctx, _ := process.Ctx(cmd)
|
||||
|
||||
err = version.CheckProcessVersion(ctx, zap.L(), config.Version, version.Build, "Identity")
|
||||
err = checker.CheckProcessVersion(ctx, zap.L(), config.Version, version.Build, "Identity")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -29,7 +28,7 @@ import (
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/storj/internal/sync2"
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/internal/version/checker"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -56,7 +55,7 @@ var (
|
||||
interval string
|
||||
versionURL string
|
||||
binaryLocation string
|
||||
snServiceName string
|
||||
serviceName string
|
||||
logPath string
|
||||
)
|
||||
|
||||
@ -67,7 +66,7 @@ func init() {
|
||||
runCmd.Flags().StringVar(&versionURL, "version-url", "https://version.storj.io/release/", "version server URL")
|
||||
runCmd.Flags().StringVar(&binaryLocation, "binary-location", "storagenode.exe", "the storage node executable binary location")
|
||||
|
||||
runCmd.Flags().StringVar(&snServiceName, "service-name", "storagenode", "storage node OS service name")
|
||||
runCmd.Flags().StringVar(&serviceName, "service-name", "storagenode", "storage node OS service name")
|
||||
runCmd.Flags().StringVar(&logPath, "log", "", "path to log file, if empty standard output will be used")
|
||||
}
|
||||
|
||||
@ -97,74 +96,6 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
cancel()
|
||||
}()
|
||||
|
||||
update := func(ctx context.Context) (err error) {
|
||||
currentVersion, err := binaryVersion(binaryLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("downloading versions from", versionURL)
|
||||
suggestedVersion, downloadURL, err := suggestedVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
downloadURL = strings.Replace(downloadURL, "{os}", runtime.GOOS, 1)
|
||||
downloadURL = strings.Replace(downloadURL, "{arch}", runtime.GOARCH, 1)
|
||||
|
||||
if currentVersion.Compare(suggestedVersion) < 0 {
|
||||
tempArchive, err := ioutil.TempFile(os.TempDir(), "storagenode")
|
||||
if err != nil {
|
||||
return errs.New("cannot create temporary archive: %v", err)
|
||||
}
|
||||
defer func() { err = errs.Combine(err, os.Remove(tempArchive.Name())) }()
|
||||
|
||||
log.Println("start downloading", downloadURL, "to", tempArchive.Name())
|
||||
err = downloadArchive(ctx, tempArchive, downloadURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("finished downloading", downloadURL, "to", tempArchive.Name())
|
||||
|
||||
extension := filepath.Ext(binaryLocation)
|
||||
if extension != "" {
|
||||
extension = "." + extension
|
||||
}
|
||||
|
||||
dir := filepath.Dir(binaryLocation)
|
||||
backupExec := filepath.Join(dir, "storagenode.old."+currentVersion.String()+extension)
|
||||
|
||||
if err = os.Rename(binaryLocation, backupExec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = unpackBinary(ctx, tempArchive.Name(), binaryLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
downloadedVersion, err := binaryVersion(binaryLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if suggestedVersion.Compare(downloadedVersion) != 0 {
|
||||
return errs.New("invalid version downloaded: wants %s got %s", suggestedVersion.String(), downloadedVersion.String())
|
||||
}
|
||||
|
||||
log.Println("restarting service", snServiceName)
|
||||
err = restartSNService(snServiceName)
|
||||
if err != nil {
|
||||
return errs.New("unable to restart service: %v", err)
|
||||
}
|
||||
log.Println("service", snServiceName, "restarted successfully")
|
||||
|
||||
// TODO remove old binary ??
|
||||
} else {
|
||||
log.Println("storage node version is up to date")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
loopInterval, err := time.ParseDuration(interval)
|
||||
if err != nil {
|
||||
return errs.New("unable to parse interval parameter: %v", err)
|
||||
@ -190,6 +121,90 @@ func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: refactor
|
||||
func update(ctx context.Context) (err error) {
|
||||
// TODO: use config struct binding
|
||||
clientConfig := checker.ClientConfig{
|
||||
ServerAddress: versionURL,
|
||||
RequestTimeout: time.Minute,
|
||||
}
|
||||
client := checker.New(clientConfig)
|
||||
|
||||
currentVersion, err := binaryVersion(binaryLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("downloading versions from", versionURL)
|
||||
process, err := client.Process(ctx, serviceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
downloadURL := process.Suggested.URL
|
||||
downloadURL = strings.Replace(downloadURL, "{os}", runtime.GOOS, 1)
|
||||
downloadURL = strings.Replace(downloadURL, "{arch}", runtime.GOARCH, 1)
|
||||
|
||||
// TODO: check rollout
|
||||
suggestedVersion, err := semver.Parse(process.Suggested.Version)
|
||||
if err != nil {
|
||||
return checker.Error.Wrap(err)
|
||||
}
|
||||
|
||||
if currentVersion.Compare(suggestedVersion) < 0 {
|
||||
tempArchive, err := ioutil.TempFile(os.TempDir(), serviceName)
|
||||
if err != nil {
|
||||
return errs.New("cannot create temporary archive: %v", err)
|
||||
}
|
||||
defer func() { err = errs.Combine(err, os.Remove(tempArchive.Name())) }()
|
||||
|
||||
log.Println("start downloading", downloadURL, "to", tempArchive.Name())
|
||||
err = downloadArchive(ctx, tempArchive, downloadURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("finished downloading", downloadURL, "to", tempArchive.Name())
|
||||
|
||||
extension := filepath.Ext(binaryLocation)
|
||||
if extension != "" {
|
||||
extension = "." + extension
|
||||
}
|
||||
|
||||
dir := filepath.Dir(binaryLocation)
|
||||
backupExec := filepath.Join(dir, serviceName+".old."+currentVersion.String()+extension)
|
||||
|
||||
if err = os.Rename(binaryLocation, backupExec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = unpackBinary(ctx, tempArchive.Name(), binaryLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
downloadedVersion, err := binaryVersion(binaryLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if suggestedVersion.Compare(downloadedVersion) != 0 {
|
||||
return errs.New("invalid version downloaded: wants %s got %s", suggestedVersion.String(), downloadedVersion.String())
|
||||
}
|
||||
|
||||
log.Println("restarting service", serviceName)
|
||||
err = restartSNService(serviceName)
|
||||
if err != nil {
|
||||
return errs.New("unable to restart service: %v", err)
|
||||
}
|
||||
log.Println("service", serviceName, "restarted successfully")
|
||||
|
||||
// TODO remove old binary ??
|
||||
} else {
|
||||
log.Printf("%s version is up to date\n", serviceName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func binaryVersion(location string) (semver.Version, error) {
|
||||
out, err := exec.Command(location, "version").Output()
|
||||
if err != nil {
|
||||
@ -211,32 +226,6 @@ func binaryVersion(location string) (semver.Version, error) {
|
||||
return semver.Version{}, errs.New("unable to determine binary version")
|
||||
}
|
||||
|
||||
func suggestedVersion() (ver semver.Version, url string, err error) {
|
||||
resp, err := http.Get(versionURL)
|
||||
if err != nil {
|
||||
return ver, url, err
|
||||
}
|
||||
defer func() { err = errs.Combine(err, resp.Body.Close()) }()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return ver, url, err
|
||||
}
|
||||
|
||||
var response version.AllowedVersions
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
return ver, url, err
|
||||
}
|
||||
|
||||
suggestedVersion := response.Processes.Storagenode.Suggested
|
||||
ver, err = semver.Make(suggestedVersion.Version)
|
||||
if err != nil {
|
||||
return ver, url, err
|
||||
}
|
||||
return ver, suggestedVersion.URL, nil
|
||||
}
|
||||
|
||||
func downloadArchive(ctx context.Context, file io.Writer, url string) (err error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
|
||||
"storj.io/storj/internal/fpath"
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/internal/version/checker"
|
||||
libuplink "storj.io/storj/lib/uplink"
|
||||
"storj.io/storj/pkg/cfgstruct"
|
||||
"storj.io/storj/pkg/process"
|
||||
@ -29,7 +30,7 @@ type UplinkFlags struct {
|
||||
NonInteractive bool `help:"disable interactive mode" default:"false" setup:"true"`
|
||||
uplink.Config
|
||||
|
||||
Version version.Config
|
||||
Version checker.Config
|
||||
}
|
||||
|
||||
var (
|
||||
@ -86,7 +87,7 @@ func (cliCfg *UplinkFlags) NewUplink(ctx context.Context) (*libuplink.Uplink, er
|
||||
|
||||
// GetProject returns a *libuplink.Project for interacting with a specific project
|
||||
func (cliCfg *UplinkFlags) GetProject(ctx context.Context) (_ *libuplink.Project, err error) {
|
||||
err = version.CheckProcessVersion(ctx, zap.L(), cliCfg.Version, version.Build, "Uplink")
|
||||
err = checker.CheckProcessVersion(ctx, zap.L(), cliCfg.Version, version.Build, "Uplink")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"storj.io/storj/internal/errs2"
|
||||
"storj.io/storj/internal/memory"
|
||||
"storj.io/storj/internal/version"
|
||||
vc_checker "storj.io/storj/internal/version/checker"
|
||||
"storj.io/storj/pkg/identity"
|
||||
"storj.io/storj/pkg/peertls/extensions"
|
||||
"storj.io/storj/pkg/peertls/tlsopts"
|
||||
@ -65,7 +66,7 @@ type SatelliteSystem struct {
|
||||
|
||||
Server *server.Server
|
||||
|
||||
Version *version.Service
|
||||
Version *vc_checker.Service
|
||||
|
||||
Contact struct {
|
||||
Service *contact.Service
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/internal/version/checker"
|
||||
"storj.io/storj/versioncontrol"
|
||||
)
|
||||
|
||||
@ -59,10 +60,12 @@ func (planet *Planet) NewVersionInfo() version.Info {
|
||||
}
|
||||
|
||||
// NewVersionConfig returns the Version Config for this planet with tuned metrics.
|
||||
func (planet *Planet) NewVersionConfig() version.Config {
|
||||
return version.Config{
|
||||
ServerAddress: fmt.Sprintf("http://%s/", planet.VersionControl.Addr()),
|
||||
RequestTimeout: time.Second * 15,
|
||||
CheckInterval: time.Minute * 5,
|
||||
func (planet *Planet) NewVersionConfig() checker.Config {
|
||||
config := checker.Config{
|
||||
CheckInterval: time.Minute * 5,
|
||||
}
|
||||
|
||||
config.ServerAddress = fmt.Sprintf("http://%s/", planet.VersionControl.Addr())
|
||||
config.RequestTimeout = time.Second * 15
|
||||
return config
|
||||
}
|
||||
|
124
internal/version/checker/client.go
Normal file
124
internal/version/checker/client.go
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package checker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
"gopkg.in/spacemonkeygo/monkit.v2"
|
||||
|
||||
"storj.io/storj/internal/version"
|
||||
)
|
||||
|
||||
var (
|
||||
mon = monkit.Package()
|
||||
|
||||
// Error is the error class for version control client errors.
|
||||
Error = errs.Class("version control client error")
|
||||
)
|
||||
|
||||
// ClientConfig is the config struct for the version control client.
|
||||
type ClientConfig struct {
|
||||
ServerAddress string `help:"server address to check its version against" default:"https://version.storj.io"`
|
||||
RequestTimeout time.Duration `help:"Request timeout for version checks" default:"0h1m0s"`
|
||||
}
|
||||
|
||||
// Client defines helper methods for using version control server response data.
|
||||
//
|
||||
// architecture: Client
|
||||
type Client struct {
|
||||
config ClientConfig
|
||||
}
|
||||
|
||||
// New constructs a new verson control server client.
|
||||
func New(config ClientConfig) *Client {
|
||||
return &Client{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// All handles the HTTP request to gather the latest version information.
|
||||
func (client *Client) All(ctx context.Context) (ver version.AllowedVersions, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
// Tune Client to have a custom Timeout (reduces hanging software)
|
||||
httpClient := http.Client{
|
||||
Timeout: client.config.RequestTimeout,
|
||||
}
|
||||
|
||||
// New Request that used the passed in context
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, client.config.ServerAddress, nil)
|
||||
if err != nil {
|
||||
return version.AllowedVersions{}, Error.Wrap(err)
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return version.AllowedVersions{}, Error.Wrap(err)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return version.AllowedVersions{}, Error.Wrap(err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return version.AllowedVersions{}, Error.New("non-success http status code: %d; body: %s\n", resp.StatusCode, body)
|
||||
}
|
||||
|
||||
err = json.NewDecoder(bytes.NewReader(body)).Decode(&ver)
|
||||
return ver, Error.Wrap(err)
|
||||
}
|
||||
|
||||
// OldMinimum returns the version with the given name at the root-level of the version control response.
|
||||
// NB: This will be deprecated eventually in favor of what is currently the `processes` root-level object.
|
||||
func (client *Client) OldMinimum(ctx context.Context, serviceName string) (ver version.SemVer, err error) {
|
||||
defer mon.Task()(&ctx, serviceName)(&err)
|
||||
|
||||
versions, err := client.All(ctx)
|
||||
if err != nil {
|
||||
return version.SemVer{}, err
|
||||
}
|
||||
|
||||
r := reflect.ValueOf(&versions)
|
||||
f := reflect.Indirect(r).FieldByName(serviceName).Interface()
|
||||
result, ok := f.(version.SemVer)
|
||||
if !ok {
|
||||
return version.SemVer{}, Error.New("invalid process name: %s", serviceName)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Process returns the version info for the named process from the version control server response.
|
||||
func (client *Client) Process(ctx context.Context, processName string) (process version.Process, err error) {
|
||||
defer mon.Task()(&ctx, processName)(&err)
|
||||
|
||||
versions, err := client.All(ctx)
|
||||
if err != nil {
|
||||
return version.Process{}, err
|
||||
}
|
||||
|
||||
processesValue := reflect.ValueOf(versions.Processes)
|
||||
field := processesValue.FieldByName(strings.Title(processName))
|
||||
|
||||
processNameErr := Error.New("invalid process name: %s\n", processName)
|
||||
if field == (reflect.Value{}) {
|
||||
return version.Process{}, processNameErr
|
||||
}
|
||||
|
||||
process, ok := field.Interface().(version.Process)
|
||||
if !ok {
|
||||
return version.Process{}, processNameErr
|
||||
}
|
||||
return process, nil
|
||||
}
|
127
internal/version/checker/client_test.go
Normal file
127
internal/version/checker/client_test.go
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package checker_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zaptest"
|
||||
|
||||
"storj.io/storj/internal/testcontext"
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/internal/version/checker"
|
||||
"storj.io/storj/versioncontrol"
|
||||
)
|
||||
|
||||
func TestClient_All(t *testing.T) {
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
|
||||
peer := newTestPeer(t, ctx)
|
||||
defer ctx.Check(peer.Close)
|
||||
|
||||
clientConfig := checker.ClientConfig{
|
||||
ServerAddress: "http://" + peer.Addr(),
|
||||
RequestTimeout: 0,
|
||||
}
|
||||
client := checker.New(clientConfig)
|
||||
|
||||
versions, err := client.All(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
processesValue := reflect.ValueOf(&versions.Processes)
|
||||
fieldCount := reflect.Indirect(processesValue).NumField()
|
||||
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
field := reflect.Indirect(processesValue).Field(i)
|
||||
|
||||
versionString := fmt.Sprintf("v%d.%d.%d", i+1, i+2, i+3)
|
||||
|
||||
process, ok := field.Interface().(version.Process)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, versionString, process.Minimum.Version)
|
||||
require.Equal(t, versionString, process.Suggested.Version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Process(t *testing.T) {
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
|
||||
peer := newTestPeer(t, ctx)
|
||||
defer ctx.Check(peer.Close)
|
||||
|
||||
clientConfig := checker.ClientConfig{
|
||||
ServerAddress: "http://" + peer.Addr(),
|
||||
RequestTimeout: 0,
|
||||
}
|
||||
client := checker.New(clientConfig)
|
||||
|
||||
processesType := reflect.TypeOf(version.Processes{})
|
||||
fieldCount := processesType.NumField()
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
field := processesType.Field(i)
|
||||
|
||||
expectedVersionStr := fmt.Sprintf("v%d.%d.%d", i+1, i+2, i+3)
|
||||
|
||||
process, err := client.Process(ctx, field.Name)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expectedVersionStr, process.Minimum.Version)
|
||||
require.Equal(t, expectedVersionStr, process.Suggested.Version)
|
||||
}
|
||||
}
|
||||
|
||||
func newTestPeer(t *testing.T, ctx *testcontext.Context) *versioncontrol.Peer {
|
||||
t.Helper()
|
||||
|
||||
testVersions := newTestVersions(t)
|
||||
serverConfig := &versioncontrol.Config{
|
||||
Address: "127.0.0.1:0",
|
||||
Versions: versioncontrol.ServiceVersions{
|
||||
Satellite: "v0.0.1",
|
||||
Storagenode: "v0.0.1",
|
||||
Uplink: "v0.0.1",
|
||||
Gateway: "v0.0.1",
|
||||
Identity: "v0.0.1",
|
||||
},
|
||||
Binary: testVersions,
|
||||
}
|
||||
peer, err := versioncontrol.New(zaptest.NewLogger(t), serverConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx.Go(func() error {
|
||||
return peer.Run(ctx)
|
||||
})
|
||||
|
||||
return peer
|
||||
}
|
||||
|
||||
func newTestVersions(t *testing.T) (versions versioncontrol.Versions) {
|
||||
t.Helper()
|
||||
|
||||
versionsValue := reflect.ValueOf(&versions)
|
||||
versionsElem := versionsValue.Elem()
|
||||
fieldCount := versionsElem.NumField()
|
||||
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
field := versionsElem.Field(i)
|
||||
|
||||
versionString := fmt.Sprintf("v%d.%d.%d", i+1, i+2, i+3)
|
||||
binary := versioncontrol.Binary{
|
||||
Minimum: versioncontrol.Version{
|
||||
Version: versionString,
|
||||
},
|
||||
Suggested: versioncontrol.Version{
|
||||
Version: versionString,
|
||||
},
|
||||
}
|
||||
|
||||
field.Set(reflect.ValueOf(binary))
|
||||
}
|
||||
return versions
|
||||
}
|
@ -1,27 +1,26 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package version
|
||||
package checker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"storj.io/storj/internal/sync2"
|
||||
"storj.io/storj/internal/version"
|
||||
)
|
||||
|
||||
// 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.storj.io"`
|
||||
RequestTimeout time.Duration `help:"Request timeout for version checks" default:"0h1m0s"`
|
||||
CheckInterval time.Duration `help:"Interval to check the version" default:"0h15m0s"`
|
||||
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
|
||||
@ -30,7 +29,8 @@ type Config struct {
|
||||
type Service struct {
|
||||
log *zap.Logger
|
||||
config Config
|
||||
info Info
|
||||
client *Client
|
||||
info version.Info
|
||||
service string
|
||||
|
||||
Loop *sync2.Cycle
|
||||
@ -41,10 +41,11 @@ type Service struct {
|
||||
}
|
||||
|
||||
// NewService creates a Version Check Client with default configuration
|
||||
func NewService(log *zap.Logger, config Config, info Info, service string) (client *Service) {
|
||||
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,
|
||||
Loop: sync2.NewCycle(config.CheckInterval),
|
||||
@ -63,7 +64,7 @@ func (srv *Service) CheckVersion(ctx context.Context) (err error) {
|
||||
|
||||
// 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 Info, service string) (err error) {
|
||||
func CheckProcessVersion(ctx context.Context, log *zap.Logger, config Config, info version.Info, service string) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
return NewService(log, config, info, service).CheckVersion(ctx)
|
||||
}
|
||||
@ -106,14 +107,13 @@ func (srv *Service) checkVersion(ctx context.Context) (allowed bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
accepted, err := srv.queryVersionFromControlServer(ctx)
|
||||
minimum, err := srv.client.OldMinimum(ctx, srv.service)
|
||||
if err != nil {
|
||||
// Log about the error, but dont crash the service and allow further operation
|
||||
srv.log.Sugar().Errorf("Failed to do periodic version check: %s", err.Error())
|
||||
return true
|
||||
}
|
||||
|
||||
minimum := getFieldString(&accepted, srv.service)
|
||||
srv.log.Sugar().Debugf("allowed minimum version from control server is: %s", minimum.String())
|
||||
|
||||
if minimum.String() == "" {
|
||||
@ -128,38 +128,6 @@ func (srv *Service) checkVersion(ctx context.Context) (allowed bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
// isAcceptedVersion compares and checks if the passed version is greater/equal than the minimum required version
|
||||
func isAcceptedVersion(test SemVer, target SemVer) bool {
|
||||
return test.Major > target.Major || (test.Major == target.Major && (test.Minor > target.Minor || (test.Minor == target.Minor && test.Patch >= target.Patch)))
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
// New Request that used the passed in context
|
||||
req, err := http.NewRequest("GET", srv.config.ServerAddress, nil)
|
||||
if err != nil {
|
||||
return AllowedVersions{}, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return AllowedVersions{}, err
|
||||
}
|
||||
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(&ver)
|
||||
return ver, err
|
||||
}
|
||||
|
||||
// DebugHandler implements version info endpoint.
|
||||
type DebugHandler struct {
|
||||
log *zap.Logger
|
||||
@ -172,7 +140,7 @@ func NewDebugHandler(log *zap.Logger) *DebugHandler {
|
||||
|
||||
// ServeHTTP returns a json representation of the current version information for the binary.
|
||||
func (server *DebugHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
j, err := Build.Marshal()
|
||||
j, err := version.Build.Marshal()
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
@ -187,12 +155,7 @@ func (server *DebugHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func getFieldString(array *AllowedVersions, field string) SemVer {
|
||||
r := reflect.ValueOf(array)
|
||||
f := reflect.Indirect(r).FieldByName(field).Interface()
|
||||
result, ok := f.(SemVer)
|
||||
if ok {
|
||||
return result
|
||||
}
|
||||
return SemVer{}
|
||||
// isAcceptedVersion compares and checks if the passed version is greater/equal than the minimum required version
|
||||
func isAcceptedVersion(test version.SemVer, target version.SemVer) bool {
|
||||
return test.Major > target.Major || (test.Major == target.Major && (test.Minor > target.Minor || (test.Minor == target.Minor && test.Patch >= target.Patch)))
|
||||
}
|
@ -13,7 +13,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
"gopkg.in/spacemonkeygo/monkit.v2"
|
||||
|
||||
"storj.io/storj/pkg/pb"
|
||||
)
|
||||
@ -26,8 +25,6 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
mon = monkit.Package()
|
||||
|
||||
// VerError is the error class for version-related errors.
|
||||
VerError = errs.Class("version error")
|
||||
|
||||
|
@ -12,10 +12,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
||||
"gopkg.in/spacemonkeygo/monkit.v2"
|
||||
"gopkg.in/spacemonkeygo/monkit.v2/present"
|
||||
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/internal/version/checker"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -36,7 +36,7 @@ func initDebug(logger *zap.Logger, r *monkit.Registry) (err error) {
|
||||
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
|
||||
mux.Handle("/version/", http.StripPrefix("/version", version.NewDebugHandler(logger.Named("version"))))
|
||||
mux.Handle("/version/", http.StripPrefix("/version", checker.NewDebugHandler(logger.Named("version"))))
|
||||
mux.Handle("/mon/", http.StripPrefix("/mon", present.HTTP(r)))
|
||||
mux.HandleFunc("/metrics", prometheus)
|
||||
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"storj.io/storj/internal/post"
|
||||
"storj.io/storj/internal/post/oauth2"
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/internal/version/checker"
|
||||
"storj.io/storj/pkg/auth/grpcauth"
|
||||
"storj.io/storj/pkg/identity"
|
||||
"storj.io/storj/pkg/pb"
|
||||
@ -55,7 +56,7 @@ type API struct {
|
||||
|
||||
Dialer rpc.Dialer
|
||||
Server *server.Server
|
||||
Version *version.Service
|
||||
Version *checker.Service
|
||||
|
||||
Contact struct {
|
||||
Service *contact.Service
|
||||
@ -139,7 +140,7 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB, pointerDB metai
|
||||
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(log.Named("version"), config.Version, versionInfo, "Satellite")
|
||||
peer.Version = checker.NewService(log.Named("version"), config.Version, versionInfo, "Satellite")
|
||||
}
|
||||
|
||||
{ // setup listener and server
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"storj.io/storj/internal/post"
|
||||
"storj.io/storj/internal/post/oauth2"
|
||||
"storj.io/storj/internal/version"
|
||||
version_checker "storj.io/storj/internal/version/checker"
|
||||
"storj.io/storj/pkg/auth/grpcauth"
|
||||
"storj.io/storj/pkg/identity"
|
||||
"storj.io/storj/pkg/pb"
|
||||
@ -134,7 +135,7 @@ type Config struct {
|
||||
|
||||
Marketing marketingweb.Config
|
||||
|
||||
Version version.Config
|
||||
Version version_checker.Config
|
||||
|
||||
GracefulExit gracefulexit.Config
|
||||
|
||||
@ -154,7 +155,7 @@ type Peer struct {
|
||||
|
||||
Server *server.Server
|
||||
|
||||
Version *version.Service
|
||||
Version *version_checker.Service
|
||||
|
||||
// services and endpoints
|
||||
Contact struct {
|
||||
@ -264,7 +265,7 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, pointerDB metainfo
|
||||
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(log.Named("version"), config.Version, versionInfo, "Satellite")
|
||||
peer.Version = version_checker.NewService(log.Named("version"), config.Version, versionInfo, "Satellite")
|
||||
}
|
||||
|
||||
{ // setup listener and server
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"storj.io/storj/internal/date"
|
||||
"storj.io/storj/internal/memory"
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/internal/version/checker"
|
||||
"storj.io/storj/pkg/storj"
|
||||
"storj.io/storj/storagenode/bandwidth"
|
||||
"storj.io/storj/storagenode/contact"
|
||||
@ -42,7 +43,7 @@ type Service struct {
|
||||
pieceStore *pieces.Store
|
||||
contact *contact.Service
|
||||
|
||||
version *version.Service
|
||||
version *checker.Service
|
||||
pingStats *contact.PingStats
|
||||
|
||||
allocatedBandwidth memory.Size
|
||||
@ -54,7 +55,7 @@ type Service struct {
|
||||
}
|
||||
|
||||
// NewService returns new instance of Service.
|
||||
func NewService(log *zap.Logger, bandwidth bandwidth.DB, pieceStore *pieces.Store, version *version.Service,
|
||||
func NewService(log *zap.Logger, bandwidth bandwidth.DB, pieceStore *pieces.Store, version *checker.Service,
|
||||
allocatedBandwidth, allocatedDiskSpace memory.Size, walletAddress string, versionInfo version.Info, trust *trust.Pool,
|
||||
reputationDB reputation.DB, storageUsageDB storageusage.DB, pingStats *contact.PingStats, contact *contact.Service) (*Service, error) {
|
||||
if log == nil {
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
|
||||
"storj.io/storj/internal/errs2"
|
||||
"storj.io/storj/internal/version"
|
||||
"storj.io/storj/internal/version/checker"
|
||||
"storj.io/storj/pkg/identity"
|
||||
"storj.io/storj/pkg/pb"
|
||||
"storj.io/storj/pkg/peertls/extensions"
|
||||
@ -91,7 +92,7 @@ type Config struct {
|
||||
|
||||
Console consoleserver.Config
|
||||
|
||||
Version version.Config
|
||||
Version checker.Config
|
||||
|
||||
Bandwidth bandwidth.Config
|
||||
|
||||
@ -116,7 +117,7 @@ type Peer struct {
|
||||
|
||||
Server *server.Server
|
||||
|
||||
Version *version.Service
|
||||
Version *checker.Service
|
||||
|
||||
// services and endpoints
|
||||
// TODO: similar grouping to satellite.Peer
|
||||
@ -179,7 +180,7 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, revocationDB exten
|
||||
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(log.Named("version"), config.Version, versionInfo, "Storagenode")
|
||||
peer.Version = checker.NewService(log.Named("version"), config.Version, versionInfo, "Storagenode")
|
||||
}
|
||||
|
||||
{ // setup listener and server
|
||||
|
@ -78,6 +78,8 @@ type Rollout struct {
|
||||
}
|
||||
|
||||
// Peer is the representation of a VersionControl Server.
|
||||
//
|
||||
// architecture: Peer
|
||||
type Peer struct {
|
||||
// core dependencies
|
||||
Log *zap.Logger
|
||||
@ -227,7 +229,7 @@ func (versions Versions) ValidateRollouts(log *zap.Logger) error {
|
||||
value := reflect.ValueOf(versions)
|
||||
fieldCount := value.NumField()
|
||||
validationErrs := errs.Group{}
|
||||
for i := 1; i < fieldCount; i++ {
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
binary, ok := value.Field(i).Interface().(Binary)
|
||||
if !ok {
|
||||
log.Warn("non-binary field in versions config struct", zap.String("field name", value.Type().Field(i).Name))
|
||||
|
@ -93,10 +93,10 @@ func TestPeer_Run(t *testing.T) {
|
||||
fieldCount := versionsType.NumField()
|
||||
|
||||
// test invalid rollout for each binary
|
||||
for i := 1; i < fieldCount; i++ {
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
versions := versioncontrol.Versions{}
|
||||
versionsValue := reflect.ValueOf(&versions)
|
||||
field := reflect.Indirect(versionsValue).Field(i)
|
||||
field := versionsValue.Elem().Field(i)
|
||||
|
||||
binary := versioncontrol.Binary{
|
||||
Rollout: versioncontrol.Rollout{
|
||||
@ -127,7 +127,7 @@ func TestPeer_Run_error(t *testing.T) {
|
||||
fieldCount := versionsType.NumField()
|
||||
|
||||
// test invalid rollout for each binary
|
||||
for i := 1; i < fieldCount; i++ {
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
versions := versioncontrol.Versions{}
|
||||
versionsValue := reflect.ValueOf(&versions)
|
||||
field := reflect.Indirect(versionsValue).Field(i)
|
||||
|
Loading…
Reference in New Issue
Block a user