2019-01-24 20:15:10 +00:00
// Copyright (C) 2019 Storj Labs, Inc.
2019-01-02 18:07:49 +00:00
// See LICENSE for copying information.
package main
import (
"context"
"errors"
"fmt"
2019-11-01 17:27:47 +00:00
"io/ioutil"
2019-01-02 18:07:49 +00:00
"net"
2019-12-05 20:42:12 +00:00
"net/url"
2019-01-02 18:07:49 +00:00
"os"
"os/exec"
"path/filepath"
2019-03-02 15:22:20 +00:00
"runtime"
2019-01-11 16:18:16 +00:00
"sort"
2019-01-02 18:07:49 +00:00
"strconv"
"strings"
2019-02-05 17:22:17 +00:00
"time"
2019-01-02 18:07:49 +00:00
2019-11-11 18:58:08 +00:00
"github.com/alessio/shellescape"
2019-01-29 14:23:30 +00:00
"github.com/spf13/viper"
2019-01-02 18:07:49 +00:00
"github.com/zeebo/errs"
"golang.org/x/sync/errgroup"
2022-01-31 12:57:13 +00:00
"storj.io/common/base58"
2019-12-27 11:48:47 +00:00
"storj.io/common/fpath"
"storj.io/common/identity"
2020-10-16 17:01:28 +01:00
"storj.io/common/pb"
2020-03-03 09:31:51 +00:00
"storj.io/common/processgroup"
2019-12-27 11:48:47 +00:00
"storj.io/common/storj"
2021-04-23 10:52:40 +01:00
"storj.io/private/dbutil"
"storj.io/private/dbutil/pgutil"
2020-05-28 16:50:23 +01:00
"storj.io/uplink"
2019-01-02 18:07:49 +00:00
)
2019-04-19 16:49:46 +01:00
const (
maxInstanceCount = 100
maxStoragenodeCount = 200
folderPermissions = 0744
)
2022-05-31 18:59:38 +01:00
var defaultAccess = "12edqtGZnqQo6QHwTB92EDqg9B1WrWn34r7ALu94wkqXL4eXjBNnVr6F5W7GhJjVqJCqxpFERmDR1dhZWyMt3Qq5zwrE9yygXeT6kBoS9AfiPuwB6kNjjxepg5UtPPtp4VLp9mP5eeyobKQRD5TsEsxTGhxamsrHvGGBPrZi8DeLtNYFMRTV6RyJVxpYX6MrPCw9HVoDQbFs7VcPeeRxRMQttSXL3y33BJhkqJ6ByFviEquaX5R2wjQT2Kx"
2020-10-27 14:23:41 +00:00
2019-04-19 16:49:46 +01:00
const (
// The following values of peer class and endpoints are used
// to create a port with a consistent format for storj-sim services.
2020-08-11 15:50:01 +01:00
// Peer classes.
2021-01-22 12:08:39 +00:00
satellitePeer = 0
satellitePeerWorker = 4
gatewayPeer = 1
versioncontrolPeer = 2
storagenodePeer = 3
2021-05-13 18:05:49 +01:00
multinodePeer = 5
2019-04-19 16:49:46 +01:00
2020-08-11 15:50:01 +01:00
// Endpoints.
2021-02-04 18:16:49 +00:00
publicRPC = 0
privateRPC = 1
publicHTTP = 2
debugHTTP = 9
2019-10-30 19:23:09 +00:00
2020-08-11 15:50:01 +01:00
// Satellite specific constants.
2021-01-22 12:08:39 +00:00
redisPort = 4
adminHTTP = 5
debugAdminHTTP = 6
debugCoreHTTP = 7
// Satellite worker specific constants.
debugMigrationHTTP = 0
debugRepairerHTTP = 1
debugGCHTTP = 2
2019-04-19 16:49:46 +01:00
)
// port creates a port with a consistent format for storj-sim services.
// The port format is: "1PXXE", where P is the peer class, XX is the index of the instance, and E is the endpoint.
func port ( peerclass , index , endpoint int ) string {
port := 10000 + peerclass * 1000 + index * 10 + endpoint
return strconv . Itoa ( port )
}
2019-01-02 18:07:49 +00:00
func networkExec ( flags * Flags , args [ ] string , command string ) error {
2019-01-08 15:24:15 +00:00
processes , err := newNetwork ( flags )
2019-01-02 18:07:49 +00:00
if err != nil {
return err
}
2022-08-02 12:52:24 +01:00
defer func ( ) { _ = processes . Output . Flush ( ) } ( )
2019-01-02 18:07:49 +00:00
ctx , cancel := NewCLIContext ( context . Background ( ) )
defer cancel ( )
2019-01-18 10:36:58 +00:00
if command == "setup" {
2019-10-16 21:34:25 +01:00
if flags . Postgres == "" {
2020-12-28 21:59:06 +00:00
return errors . New ( "postgres connection URL is required for running storj-sim. Example: `storj-sim network setup --postgres=<connection URL>`.\nSee docs for more details https://github.com/storj/docs/blob/main/Test-network.md#running-tests-with-postgres" )
2019-10-16 21:34:25 +01:00
}
2019-01-18 10:36:58 +00:00
identities , err := identitySetup ( processes )
if err != nil {
return err
}
err = identities . Exec ( ctx , command )
if err != nil {
return err
}
}
2019-01-02 18:07:49 +00:00
err = processes . Exec ( ctx , command )
closeErr := processes . Close ( )
return errs . Combine ( err , closeErr )
}
2019-11-11 18:58:08 +00:00
func escapeEnv ( env string ) string {
// TODO(jeff): escape env variables appropriately on windows. perhaps the
// env output should be of the form `set KEY=VALUE` as well.
if runtime . GOOS == "windows" {
return env
}
parts := strings . SplitN ( env , "=" , 2 )
if len ( parts ) != 2 {
return env
}
return parts [ 0 ] + "=" + shellescape . Quote ( parts [ 1 ] )
}
2019-03-23 21:53:03 +00:00
func networkEnv ( flags * Flags , args [ ] string ) error {
flags . OnlyEnv = true
2019-11-01 17:27:47 +00:00
2019-03-23 21:53:03 +00:00
processes , err := newNetwork ( flags )
if err != nil {
return err
}
2022-08-02 12:52:24 +01:00
defer func ( ) { _ = processes . Output . Flush ( ) } ( )
2019-03-23 21:53:03 +00:00
// run exec before, since it will load env vars from configs
for _ , process := range processes . List {
if exec := process . ExecBefore [ "run" ] ; exec != nil {
if err := exec ( process ) ; err != nil {
return err
}
}
}
2019-04-10 02:16:12 +01:00
if len ( args ) == 1 {
envprefix := strings . ToUpper ( args [ 0 ] + "=" )
// find the environment value that the environment variable is set to
for _ , env := range processes . Env ( ) {
if strings . HasPrefix ( strings . ToUpper ( env ) , envprefix ) {
2019-11-11 18:58:08 +00:00
fmt . Println ( escapeEnv ( env [ len ( envprefix ) : ] ) )
2019-04-10 02:16:12 +01:00
return nil
}
}
return nil
}
2019-03-23 21:53:03 +00:00
for _ , env := range processes . Env ( ) {
2019-11-11 18:58:08 +00:00
fmt . Println ( escapeEnv ( env ) )
2019-03-23 21:53:03 +00:00
}
return nil
}
2019-01-02 18:07:49 +00:00
func networkTest ( flags * Flags , command string , args [ ] string ) error {
2019-01-08 15:24:15 +00:00
processes , err := newNetwork ( flags )
2019-01-02 18:07:49 +00:00
if err != nil {
return err
}
2022-08-02 12:52:24 +01:00
defer func ( ) { _ = processes . Output . Flush ( ) } ( )
2019-01-02 18:07:49 +00:00
ctx , cancel := NewCLIContext ( context . Background ( ) )
2021-01-06 11:25:25 +00:00
var group * errgroup . Group
if processes . FailFast {
group , ctx = errgroup . WithContext ( ctx )
} else {
group = & errgroup . Group { }
}
processes . Start ( ctx , group , "run" )
2019-01-02 18:07:49 +00:00
2019-01-08 15:24:15 +00:00
for _ , process := range processes . List {
2019-10-28 14:04:31 +00:00
process . Status . Started . Wait ( ctx )
}
if err := ctx . Err ( ) ; err != nil {
2022-08-02 12:52:24 +01:00
// If the context has been cancelled, it means that one of the processes failed.
// Wait for the processes to shut down themselves and return the first error.
return fmt . Errorf ( "network canceled: %w" , group . Wait ( ) )
2019-01-08 15:24:15 +00:00
}
2019-01-02 18:07:49 +00:00
cmd := exec . CommandContext ( ctx , command , args ... )
cmd . Env = append ( os . Environ ( ) , processes . Env ( ) ... )
2022-08-02 12:52:24 +01:00
2019-01-02 18:07:49 +00:00
stdout := processes . Output . Prefixed ( "test:out" )
2022-08-02 12:52:24 +01:00
defer func ( ) { _ = stdout . Flush ( ) } ( )
2019-01-02 18:07:49 +00:00
stderr := processes . Output . Prefixed ( "test:err" )
2022-08-02 12:52:24 +01:00
defer func ( ) { _ = stderr . Flush ( ) } ( )
2019-01-02 18:07:49 +00:00
cmd . Stdout , cmd . Stderr = stdout , stderr
2022-08-02 12:52:24 +01:00
2019-01-02 18:07:49 +00:00
processgroup . Setup ( cmd )
if printCommands {
fmt . Fprintf ( processes . Output , "exec: %v\n" , strings . Join ( cmd . Args , " " ) )
}
errRun := cmd . Run ( )
2022-08-02 12:52:24 +01:00
if errRun != nil {
fmt . Fprintf ( processes . Output , "test command failed: %v\n" , errRun )
}
2019-01-02 18:07:49 +00:00
cancel ( )
2022-08-02 12:52:24 +01:00
_ = group . Wait ( )
return errs . Combine ( errRun , processes . Close ( ) )
2019-01-02 18:07:49 +00:00
}
func networkDestroy ( flags * Flags , args [ ] string ) error {
if fpath . IsRoot ( flags . Directory ) {
return errors . New ( "safety check: disallowed to remove root directory " + flags . Directory )
}
if printCommands {
2019-01-16 23:09:57 +00:00
fmt . Println ( "sim | exec: rm -rf" , flags . Directory )
2019-01-02 18:07:49 +00:00
}
return os . RemoveAll ( flags . Directory )
}
2020-07-16 15:18:02 +01:00
// newNetwork creates a default network.
2019-01-08 15:24:15 +00:00
func newNetwork ( flags * Flags ) ( * Processes , error ) {
2019-06-20 12:52:32 +01:00
_ , filename , _ , ok := runtime . Caller ( 0 )
if ! ok {
return nil , errs . New ( "no caller information" )
}
storjRoot := strings . TrimSuffix ( filename , "/cmd/storj-sim/network.go" )
2019-01-08 15:24:15 +00:00
// with common adds all common arguments to the process
2019-03-19 09:10:23 +00:00
withCommon := func ( dir string , all Arguments ) Arguments {
common := [ ] string { "--metrics.app-suffix" , "sim" , "--log.level" , "debug" , "--config-dir" , dir }
2019-03-12 12:51:06 +00:00
if flags . IsDev {
2019-08-05 18:01:20 +01:00
common = append ( common , "--defaults" , "dev" )
2021-01-05 11:13:27 +00:00
} else {
common = append ( common , "--defaults" , "release" )
2019-03-12 12:51:06 +00:00
}
2019-01-08 15:24:15 +00:00
for command , args := range all {
2021-01-05 11:13:27 +00:00
full := append ( [ ] string { } , common ... )
full = append ( full , command )
full = append ( full , args ... )
all [ command ] = full
2019-01-08 15:24:15 +00:00
}
return all
}
2019-01-02 18:07:49 +00:00
2021-01-06 11:25:25 +00:00
processes := NewProcesses ( flags . Directory , flags . FailFast )
2019-03-19 09:10:23 +00:00
2022-05-31 18:59:38 +01:00
host := flags . Host
2019-04-03 20:13:39 +01:00
versioncontrol := processes . New ( Info {
Name : "versioncontrol/0" ,
Executable : "versioncontrol" ,
Directory : filepath . Join ( processes . Directory , "versioncontrol" , "0" ) ,
2020-05-11 06:26:32 +01:00
Address : net . JoinHostPort ( host , port ( versioncontrolPeer , 0 , publicRPC ) ) ,
2019-04-03 20:13:39 +01:00
} )
versioncontrol . Arguments = withCommon ( versioncontrol . Directory , Arguments {
"setup" : {
"--address" , versioncontrol . Address ,
2019-05-14 16:13:18 +01:00
"--debug.addr" , net . JoinHostPort ( host , port ( versioncontrolPeer , 0 , debugHTTP ) ) ,
2019-11-06 14:06:10 +00:00
"--binary.gateway.rollout.seed" , "0000000000000000000000000000000000000000000000000000000000000001" ,
"--binary.identity.rollout.seed" , "0000000000000000000000000000000000000000000000000000000000000001" ,
"--binary.satellite.rollout.seed" , "0000000000000000000000000000000000000000000000000000000000000001" ,
"--binary.storagenode-updater.rollout.seed" , "0000000000000000000000000000000000000000000000000000000000000001" ,
"--binary.storagenode.rollout.seed" , "0000000000000000000000000000000000000000000000000000000000000001" ,
"--binary.uplink.rollout.seed" , "0000000000000000000000000000000000000000000000000000000000000001" ,
2019-04-03 20:13:39 +01:00
} ,
"run" : { } ,
} )
versioncontrol . ExecBefore [ "run" ] = func ( process * Process ) error {
return readConfigString ( & versioncontrol . Address , versioncontrol . Directory , "address" )
}
// gateway must wait for the versioncontrol to start up
2019-10-04 21:48:41 +01:00
// Create satellites
2019-04-19 16:49:46 +01:00
if flags . SatelliteCount > maxInstanceCount {
return nil , fmt . Errorf ( "exceeded the max instance count of %d with Satellite count of %d" , maxInstanceCount , flags . SatelliteCount )
}
2019-11-01 17:27:47 +00:00
// set up redis servers
var redisServers [ ] * Process
2019-11-01 19:48:37 +00:00
if flags . Redis == "" {
for i := 0 ; i < flags . SatelliteCount ; i ++ {
rp := port ( satellitePeer , i , redisPort )
process := processes . New ( Info {
Name : fmt . Sprintf ( "redis/%d" , i ) ,
Executable : "redis-server" ,
Directory : filepath . Join ( processes . Directory , "satellite" , fmt . Sprint ( i ) , "redis" ) ,
Address : net . JoinHostPort ( host , rp ) ,
} )
redisServers = append ( redisServers , process )
process . ExecBefore [ "setup" ] = func ( process * Process ) error {
confpath := filepath . Join ( process . Directory , "redis.conf" )
arguments := [ ] string {
"daemonize no" ,
"bind " + host ,
"port " + rp ,
"timeout 0" ,
"databases 2" ,
"dbfilename sim.rdb" ,
"dir ./" ,
}
conf := strings . Join ( arguments , "\n" ) + "\n"
err := ioutil . WriteFile ( confpath , [ ] byte ( conf ) , 0755 )
return err
}
process . Arguments = Arguments {
"run" : [ ] string { filepath . Join ( process . Directory , "redis.conf" ) } ,
2019-11-01 17:27:47 +00:00
}
}
}
2019-01-09 15:59:51 +00:00
var satellites [ ] * Process
2019-01-08 15:24:15 +00:00
for i := 0 ; i < flags . SatelliteCount ; i ++ {
2019-11-04 19:01:02 +00:00
apiProcess := processes . New ( Info {
2019-01-08 15:24:15 +00:00
Name : fmt . Sprintf ( "satellite/%d" , i ) ,
Executable : "satellite" ,
2019-03-19 09:10:23 +00:00
Directory : filepath . Join ( processes . Directory , "satellite" , fmt . Sprint ( i ) ) ,
2020-05-11 06:26:32 +01:00
Address : net . JoinHostPort ( host , port ( satellitePeer , i , publicRPC ) ) ,
2019-01-08 15:24:15 +00:00
} )
2019-11-04 19:01:02 +00:00
satellites = append ( satellites , apiProcess )
2019-01-08 15:24:15 +00:00
2019-11-01 19:48:37 +00:00
redisAddress := flags . Redis
2019-11-04 12:55:02 +00:00
redisPortBase := flags . RedisStartDB + i * 2
2019-11-01 19:48:37 +00:00
if redisAddress == "" {
redisAddress = redisServers [ i ] . Address
redisPortBase = 0
2019-11-04 19:01:02 +00:00
apiProcess . WaitForStart ( redisServers [ i ] )
2019-11-01 19:48:37 +00:00
}
2019-11-04 19:01:02 +00:00
apiProcess . Arguments = withCommon ( apiProcess . Directory , Arguments {
2019-01-22 12:35:48 +00:00
"setup" : {
2019-11-04 19:01:02 +00:00
"--identity-dir" , apiProcess . Directory ,
2021-01-05 11:13:27 +00:00
2019-04-19 16:49:46 +01:00
"--console.address" , net . JoinHostPort ( host , port ( satellitePeer , i , publicHTTP ) ) ,
2019-03-02 15:22:20 +00:00
"--console.static-dir" , filepath . Join ( storjRoot , "web/satellite/" ) ,
2021-01-05 11:13:27 +00:00
"--console.auth-token-secret" , "my-suppa-secret-key" ,
2020-05-05 13:03:45 +01:00
"--console.open-registration-enabled" ,
"--console.rate-limit.burst" , "100" ,
2021-01-05 11:13:27 +00:00
2019-11-04 19:01:02 +00:00
"--server.address" , apiProcess . Address ,
2020-05-11 06:26:32 +01:00
"--server.private-address" , net . JoinHostPort ( host , port ( satellitePeer , i , privateRPC ) ) ,
2019-01-08 23:41:01 +00:00
2019-11-01 19:48:37 +00:00
"--live-accounting.storage-backend" , "redis://" + redisAddress + "?db=" + strconv . Itoa ( redisPortBase ) ,
"--server.revocation-dburl" , "redis://" + redisAddress + "?db=" + strconv . Itoa ( redisPortBase + 1 ) ,
2019-11-01 17:27:47 +00:00
2019-02-11 11:17:32 +00:00
"--server.extensions.revocation=false" ,
"--server.use-peer-ca-whitelist=false" ,
2019-03-02 15:22:20 +00:00
"--mail.smtp-server-address" , "smtp.gmail.com:587" ,
"--mail.from" , "Storj <yaroslav-satellite-test@storj.io>" ,
"--mail.template-path" , filepath . Join ( storjRoot , "web/satellite/static/emails" ) ,
2019-04-03 20:13:39 +01:00
"--version.server-address" , fmt . Sprintf ( "http://%s/" , versioncontrol . Address ) ,
2019-05-14 16:13:18 +01:00
"--debug.addr" , net . JoinHostPort ( host , port ( satellitePeer , i , debugHTTP ) ) ,
2020-02-07 16:36:28 +00:00
"--admin.address" , net . JoinHostPort ( host , port ( satellitePeer , i , adminHTTP ) ) ,
2022-05-31 18:59:38 +01:00
"--admin.static-dir" , filepath . Join ( storjRoot , "satellite/admin/ui/build" ) ,
2019-01-08 15:24:15 +00:00
} ,
2019-10-30 19:23:09 +00:00
"run" : { "api" } ,
2019-01-08 15:24:15 +00:00
} )
2019-02-06 12:47:00 +00:00
2019-05-14 16:13:18 +01:00
if flags . Postgres != "" {
2019-12-05 20:42:12 +00:00
masterDBURL , err := namespacedDatabaseURL ( flags . Postgres , fmt . Sprintf ( "satellite/%d" , i ) )
if err != nil {
return nil , err
}
metainfoDBURL , err := namespacedDatabaseURL ( flags . Postgres , fmt . Sprintf ( "satellite/%d/meta" , i ) )
if err != nil {
return nil , err
}
2019-11-04 19:01:02 +00:00
apiProcess . Arguments [ "setup" ] = append ( apiProcess . Arguments [ "setup" ] ,
2019-12-05 20:42:12 +00:00
"--database" , masterDBURL ,
"--metainfo.database-url" , metainfoDBURL ,
2020-11-18 21:39:13 +00:00
"--orders.encryption-keys" , "0100000000000000=0100000000000000000000000000000000000000000000000000000000000000" ,
2019-05-14 16:13:18 +01:00
)
}
2019-11-04 19:01:02 +00:00
apiProcess . ExecBefore [ "run" ] = func ( process * Process ) error {
2020-06-05 14:43:14 +01:00
if err := readConfigString ( & process . Address , process . Directory , "server.address" ) ; err != nil {
return err
}
satNodeID , err := identity . NodeIDFromCertPath ( filepath . Join ( apiProcess . Directory , "identity.cert" ) )
if err != nil {
return err
}
2022-05-25 05:22:05 +01:00
process . Info . ID = satNodeID . String ( )
2020-06-05 14:43:14 +01:00
return nil
2019-02-06 12:47:00 +00:00
}
2019-01-02 18:07:49 +00:00
2019-11-02 20:09:07 +00:00
migrationProcess := processes . New ( Info {
Name : fmt . Sprintf ( "satellite-migration/%d" , i ) ,
2019-10-16 21:34:25 +01:00
Executable : "satellite" ,
Directory : filepath . Join ( processes . Directory , "satellite" , fmt . Sprint ( i ) ) ,
2019-11-02 20:09:07 +00:00
} )
2019-11-04 19:01:02 +00:00
migrationProcess . Arguments = withCommon ( apiProcess . Directory , Arguments {
2019-11-02 20:09:07 +00:00
"run" : {
"migration" ,
2021-01-22 12:08:39 +00:00
"--debug.addr" , net . JoinHostPort ( host , port ( satellitePeerWorker , i , debugMigrationHTTP ) ) ,
2019-11-02 20:09:07 +00:00
} ,
2019-10-16 21:34:25 +01:00
} )
2020-03-12 15:40:22 +00:00
apiProcess . WaitForExited ( migrationProcess )
2019-10-16 21:34:25 +01:00
2019-11-02 20:09:07 +00:00
coreProcess := processes . New ( Info {
Name : fmt . Sprintf ( "satellite-core/%d" , i ) ,
Executable : "satellite" ,
Directory : filepath . Join ( processes . Directory , "satellite" , fmt . Sprint ( i ) ) ,
Address : "" ,
} )
2019-11-04 19:01:02 +00:00
coreProcess . Arguments = withCommon ( apiProcess . Directory , Arguments {
2019-10-16 21:34:25 +01:00
"run" : {
2021-01-22 12:08:39 +00:00
"--debug.addr" , net . JoinHostPort ( host , port ( satellitePeer , i , debugCoreHTTP ) ) ,
2020-11-18 21:39:13 +00:00
"--orders.encryption-keys" , "0100000000000000=0100000000000000000000000000000000000000000000000000000000000000" ,
2019-10-16 21:34:25 +01:00
} ,
} )
2019-11-02 20:09:07 +00:00
coreProcess . WaitForExited ( migrationProcess )
2019-10-16 21:34:25 +01:00
2020-02-07 15:56:59 +00:00
adminProcess := processes . New ( Info {
Name : fmt . Sprintf ( "satellite-admin/%d" , i ) ,
Executable : "satellite" ,
Directory : filepath . Join ( processes . Directory , "satellite" , fmt . Sprint ( i ) ) ,
2020-02-07 16:36:28 +00:00
Address : net . JoinHostPort ( host , port ( satellitePeer , i , adminHTTP ) ) ,
2020-02-07 15:56:59 +00:00
} )
adminProcess . Arguments = withCommon ( apiProcess . Directory , Arguments {
"run" : {
2020-02-07 16:36:28 +00:00
"admin" ,
2020-02-07 15:56:59 +00:00
"--debug.addr" , net . JoinHostPort ( host , port ( satellitePeer , i , debugAdminHTTP ) ) ,
} ,
} )
adminProcess . WaitForExited ( migrationProcess )
2019-11-02 20:09:07 +00:00
repairProcess := processes . New ( Info {
2019-10-29 14:55:57 +00:00
Name : fmt . Sprintf ( "satellite-repairer/%d" , i ) ,
Executable : "satellite" ,
Directory : filepath . Join ( processes . Directory , "satellite" , fmt . Sprint ( i ) ) ,
} )
2019-11-04 19:01:02 +00:00
repairProcess . Arguments = withCommon ( apiProcess . Directory , Arguments {
2019-10-29 14:55:57 +00:00
"run" : {
"repair" ,
2021-01-22 12:08:39 +00:00
"--debug.addr" , net . JoinHostPort ( host , port ( satellitePeerWorker , i , debugRepairerHTTP ) ) ,
2020-11-18 21:39:13 +00:00
"--orders.encryption-keys" , "0100000000000000=0100000000000000000000000000000000000000000000000000000000000000" ,
2019-10-29 14:55:57 +00:00
} ,
} )
2019-11-02 20:09:07 +00:00
repairProcess . WaitForExited ( migrationProcess )
2020-03-12 15:40:22 +00:00
garbageCollectionProcess := processes . New ( Info {
Name : fmt . Sprintf ( "satellite-garbage-collection/%d" , i ) ,
Executable : "satellite" ,
Directory : filepath . Join ( processes . Directory , "satellite" , fmt . Sprint ( i ) ) ,
} )
garbageCollectionProcess . Arguments = withCommon ( apiProcess . Directory , Arguments {
"run" : {
"garbage-collection" ,
2021-01-22 12:08:39 +00:00
"--debug.addr" , net . JoinHostPort ( host , port ( satellitePeerWorker , i , debugGCHTTP ) ) ,
2020-03-12 15:40:22 +00:00
} ,
} )
garbageCollectionProcess . WaitForExited ( migrationProcess )
2019-10-29 14:55:57 +00:00
}
2019-10-16 21:34:25 +01:00
// Create gateways for each satellite
2019-10-30 19:23:09 +00:00
for i , satellite := range satellites {
2021-06-25 12:17:55 +01:00
if flags . NoGateways {
break
}
2019-02-06 12:47:00 +00:00
satellite := satellite
2019-01-08 15:24:15 +00:00
process := processes . New ( Info {
Name : fmt . Sprintf ( "gateway/%d" , i ) ,
Executable : "gateway" ,
2019-03-19 09:10:23 +00:00
Directory : filepath . Join ( processes . Directory , "gateway" , fmt . Sprint ( i ) ) ,
2020-05-11 06:26:32 +01:00
Address : net . JoinHostPort ( host , port ( gatewayPeer , i , publicRPC ) ) ,
2019-01-08 15:24:15 +00:00
} )
// gateway must wait for the corresponding satellite to start up
process . WaitForStart ( satellite )
2020-05-28 16:50:23 +01:00
2020-10-27 14:23:41 +00:00
accessData := defaultAccess
2019-03-19 09:10:23 +00:00
process . Arguments = withCommon ( process . Directory , Arguments {
2019-01-08 15:24:15 +00:00
"setup" : {
2020-10-27 14:23:41 +00:00
"--non-interactive" ,
"--access" , accessData ,
2019-01-08 15:24:15 +00:00
"--server.address" , process . Address ,
2020-10-27 14:23:41 +00:00
2019-04-19 16:49:46 +01:00
"--debug.addr" , net . JoinHostPort ( host , port ( gatewayPeer , i , debugHTTP ) ) ,
2019-01-08 15:24:15 +00:00
} ,
2019-08-05 18:01:20 +01:00
2019-06-07 17:14:40 +01:00
"run" : { } ,
2019-01-08 15:24:15 +00:00
} )
2019-01-29 14:23:30 +00:00
2019-12-11 00:51:10 +00:00
process . ExecBefore [ "run" ] = func ( process * Process ) ( err error ) {
err = readConfigString ( & process . Address , process . Directory , "server.address" )
2019-02-06 12:47:00 +00:00
if err != nil {
return err
}
2020-10-27 14:23:41 +00:00
vip := viper . New ( )
vip . AddConfigPath ( process . Directory )
if err := vip . ReadInConfig ( ) ; err != nil {
2019-01-29 14:23:30 +00:00
return err
}
2020-10-27 14:23:41 +00:00
// TODO: maybe all the config flags should be exposed for all processes?
// check if gateway config has an api key, if it's not
// create example project with key and add it to the config
// so that gateway can have access to the satellite
if runAccessData := vip . GetString ( "access" ) ; ! flags . OnlyEnv && runAccessData == accessData {
var consoleAddress string
err := readConfigString ( & consoleAddress , satellite . Directory , "console.address" )
if err != nil {
2022-08-02 12:52:24 +01:00
return fmt . Errorf ( "failed to read config string: %w" , err )
2020-02-18 10:14:50 +00:00
}
2020-10-27 14:23:41 +00:00
// try with 100ms delays until we hit 3s
apiKey , start := "" , time . Now ( )
for apiKey == "" {
2021-05-14 16:05:42 +01:00
apiKey , err = newConsoleEndpoints ( consoleAddress ) . createOrGetAPIKey ( context . Background ( ) )
2020-10-27 14:23:41 +00:00
if err != nil && time . Since ( start ) > 3 * time . Second {
2022-08-02 12:52:24 +01:00
return fmt . Errorf ( "failed to create account: %w" , err )
2020-10-27 14:23:41 +00:00
}
time . Sleep ( 100 * time . Millisecond )
}
2019-02-05 17:22:17 +00:00
2020-10-27 14:23:41 +00:00
satNodeID , err := identity . NodeIDFromCertPath ( filepath . Join ( satellite . Directory , "identity.cert" ) )
if err != nil {
2022-08-02 12:52:24 +01:00
return fmt . Errorf ( "failed to get node id from path: %w" , err )
2020-10-27 14:23:41 +00:00
}
nodeURL := storj . NodeURL {
ID : satNodeID ,
Address : satellite . Address ,
}
access , err := uplink . RequestAccessWithPassphrase ( context . Background ( ) , nodeURL . String ( ) , apiKey , "" )
if err != nil {
2022-08-02 12:52:24 +01:00
return fmt . Errorf ( "failed to get passphrase: %w" , err )
2020-10-27 14:23:41 +00:00
}
accessData , err := access . Serialize ( )
if err != nil {
2022-08-02 12:52:24 +01:00
return fmt . Errorf ( "failed to serialize access: %w" , err )
2020-10-27 14:23:41 +00:00
}
vip . Set ( "access" , accessData )
if err := vip . WriteConfig ( ) ; err != nil {
2022-08-02 12:52:24 +01:00
return fmt . Errorf ( "failed to write config: %w" , err )
2020-10-27 14:23:41 +00:00
}
2019-02-05 17:22:17 +00:00
}
2020-10-27 14:23:41 +00:00
if runAccessData := vip . GetString ( "access" ) ; runAccessData != accessData {
process . AddExtra ( "ACCESS" , runAccessData )
2020-10-16 17:01:28 +01:00
if apiKey , err := getAPIKey ( runAccessData ) ; err == nil {
process . AddExtra ( "API_KEY" , apiKey )
}
2019-03-23 21:53:03 +00:00
}
2020-10-27 14:23:41 +00:00
process . AddExtra ( "ACCESS_KEY" , vip . GetString ( "minio.access-key" ) )
process . AddExtra ( "SECRET_KEY" , vip . GetString ( "minio.secret-key" ) )
2019-03-23 21:53:03 +00:00
2019-01-29 14:23:30 +00:00
return nil
}
2019-01-02 18:07:49 +00:00
}
2019-01-08 15:24:15 +00:00
// Create storage nodes
2019-04-19 16:49:46 +01:00
if flags . StorageNodeCount > maxStoragenodeCount {
return nil , fmt . Errorf ( "exceeded the max instance count of %d with Storage Node count of %d" , maxStoragenodeCount , flags . StorageNodeCount )
}
2019-01-08 15:24:15 +00:00
for i := 0 ; i < flags . StorageNodeCount ; i ++ {
process := processes . New ( Info {
Name : fmt . Sprintf ( "storagenode/%d" , i ) ,
Executable : "storagenode" ,
2019-03-19 09:10:23 +00:00
Directory : filepath . Join ( processes . Directory , "storagenode" , fmt . Sprint ( i ) ) ,
2020-05-11 06:26:32 +01:00
Address : net . JoinHostPort ( host , port ( storagenodePeer , i , publicRPC ) ) ,
2019-01-08 15:24:15 +00:00
} )
2019-10-30 19:23:09 +00:00
for _ , satellite := range satellites {
2019-02-22 14:35:51 +00:00
process . WaitForStart ( satellite )
}
2019-01-08 15:24:15 +00:00
2019-03-19 09:10:23 +00:00
process . Arguments = withCommon ( process . Directory , Arguments {
2019-01-22 12:35:48 +00:00
"setup" : {
"--identity-dir" , process . Directory ,
2019-06-20 12:52:32 +01:00
"--console.address" , net . JoinHostPort ( host , port ( storagenodePeer , i , publicHTTP ) ) ,
2019-09-12 13:20:52 +01:00
"--console.static-dir" , filepath . Join ( storjRoot , "web/storagenode/" ) ,
2019-01-28 14:48:49 +00:00
"--server.address" , process . Address ,
2020-05-11 06:26:32 +01:00
"--server.private-address" , net . JoinHostPort ( host , port ( storagenodePeer , i , privateRPC ) ) ,
2019-01-28 14:48:49 +00:00
2019-10-04 21:48:41 +01:00
"--operator.email" , fmt . Sprintf ( "storage%d@mail.test" , i ) ,
"--operator.wallet" , "0x0123456789012345678901234567890123456789" ,
2019-02-11 11:17:32 +00:00
2019-08-05 18:01:20 +01:00
"--storage2.monitor.minimum-disk-space" , "0" ,
2019-06-10 11:14:50 +01:00
2019-02-11 11:17:32 +00:00
"--server.extensions.revocation=false" ,
"--server.use-peer-ca-whitelist=false" ,
2019-04-03 20:13:39 +01:00
"--version.server-address" , fmt . Sprintf ( "http://%s/" , versioncontrol . Address ) ,
2019-05-14 16:13:18 +01:00
"--debug.addr" , net . JoinHostPort ( host , port ( storagenodePeer , i , debugHTTP ) ) ,
2020-04-28 13:38:52 +01:00
"--tracing.app" , fmt . Sprintf ( "storagenode/%d" , i ) ,
2019-01-08 15:24:15 +00:00
} ,
2019-01-28 14:48:49 +00:00
"run" : { } ,
2019-01-08 15:24:15 +00:00
} )
2019-02-06 12:47:00 +00:00
2019-07-17 19:14:44 +01:00
process . ExecBefore [ "setup" ] = func ( process * Process ) error {
whitelisted := [ ] string { }
2019-10-30 19:23:09 +00:00
for _ , satellite := range satellites {
2019-07-17 19:14:44 +01:00
peer , err := identity . PeerConfig {
2019-10-30 19:23:09 +00:00
CertPath : filepath . Join ( satellite . Directory , "identity.cert" ) ,
2019-07-17 19:14:44 +01:00
} . Load ( )
if err != nil {
return err
}
2019-10-30 19:23:09 +00:00
whitelisted = append ( whitelisted , peer . ID . String ( ) + "@" + satellite . Address )
2019-07-17 19:14:44 +01:00
}
process . Arguments [ "setup" ] = append ( process . Arguments [ "setup" ] ,
2019-11-16 00:59:32 +00:00
"--storage2.trust.sources" , strings . Join ( whitelisted , "," ) ,
2019-07-17 19:14:44 +01:00
)
return nil
}
2019-02-06 12:47:00 +00:00
process . ExecBefore [ "run" ] = func ( process * Process ) error {
return readConfigString ( & process . Address , process . Directory , "server.address" )
}
2019-01-08 15:24:15 +00:00
}
2019-01-02 18:07:49 +00:00
2021-05-13 18:05:49 +01:00
{ // setup multinode
process := processes . New ( Info {
Name : fmt . Sprintf ( "multinode/%d" , 0 ) ,
Executable : "multinode" ,
Directory : filepath . Join ( processes . Directory , "multinode" , fmt . Sprint ( 0 ) ) ,
} )
process . Arguments = withCommon ( process . Directory , Arguments {
"setup" : {
"--identity-dir" , process . Directory ,
"--console.address" , net . JoinHostPort ( host , port ( multinodePeer , 0 , publicHTTP ) ) ,
"--console.static-dir" , filepath . Join ( storjRoot , "web/multinode/" ) ,
"--debug.addr" , net . JoinHostPort ( host , port ( multinodePeer , 0 , debugHTTP ) ) ,
} ,
"run" : { } ,
} )
process . AddExtra ( "SETUP_ARGS" , strings . Join ( process . Arguments [ "setup" ] , " " ) )
}
2019-01-11 16:18:16 +00:00
{ // verify that we have all binaries
missing := map [ string ] bool { }
for _ , process := range processes . List {
_ , err := exec . LookPath ( process . Executable )
if err != nil {
missing [ process . Executable ] = true
}
}
if len ( missing ) > 0 {
var list [ ] string
for executable := range missing {
list = append ( list , executable )
}
sort . Strings ( list )
return nil , fmt . Errorf ( "some executables cannot be found: %v" , list )
}
}
2019-01-08 15:24:15 +00:00
// Create directories for all processes
for _ , process := range processes . List {
if err := os . MkdirAll ( process . Directory , folderPermissions ) ; err != nil {
2019-01-02 18:07:49 +00:00
return nil , err
}
}
return processes , nil
}
2019-01-08 15:24:15 +00:00
2019-01-18 10:36:58 +00:00
func identitySetup ( network * Processes ) ( * Processes , error ) {
2021-01-06 11:25:25 +00:00
processes := NewProcesses ( network . Directory , network . FailFast )
2019-01-18 10:36:58 +00:00
for _ , process := range network . List {
2019-11-01 17:27:47 +00:00
if process . Info . Executable == "gateway" || process . Info . Executable == "redis-server" {
// gateways and redis-servers don't need an identity
2019-05-10 12:17:58 +01:00
continue
}
2019-11-02 20:09:07 +00:00
if strings . Contains ( process . Name , "satellite-" ) {
// we only need to create the identity once for the satellite system, we create the
// identity for the satellite process and share it with these other satellite processes
2019-10-29 14:55:57 +00:00
continue
}
2019-10-16 21:34:25 +01:00
2019-01-18 10:36:58 +00:00
identity := processes . New ( Info {
Name : "identity/" + process . Info . Name ,
Executable : "identity" ,
Directory : process . Directory ,
Address : "" ,
} )
identity . Arguments = Arguments {
"setup" : {
2019-01-22 12:35:48 +00:00
"--identity-dir" , process . Directory ,
2019-01-23 11:36:19 +00:00
"--concurrency" , "1" ,
"--difficulty" , "8" ,
2019-01-24 15:41:16 +00:00
"create" , "." ,
2019-01-18 10:36:58 +00:00
} ,
}
}
// create directories for all processes
for _ , process := range processes . List {
if err := os . MkdirAll ( process . Directory , folderPermissions ) ; err != nil {
return nil , err
}
}
return processes , nil
}
2019-02-06 12:47:00 +00:00
2020-10-16 17:01:28 +01:00
// getAPIKey parses an access string to return its corresponding api key.
func getAPIKey ( access string ) ( apiKey string , err error ) {
data , version , err := base58 . CheckDecode ( access )
if err != nil || version != 0 {
return "" , errors . New ( "invalid access grant format" )
}
p := new ( pb . Scope )
if err := pb . Unmarshal ( data , p ) ; err != nil {
return "" , err
}
apiKey = base58 . CheckEncode ( p . ApiKey , 0 )
return apiKey , nil
}
2020-07-16 15:18:02 +01:00
// readConfigString reads from dir/config.yaml flagName returns the value in `into`.
2019-02-06 12:47:00 +00:00
func readConfigString ( into * string , dir , flagName string ) error {
vip := viper . New ( )
vip . AddConfigPath ( dir )
if err := vip . ReadInConfig ( ) ; err != nil {
return err
}
if v := vip . GetString ( flagName ) ; v != "" {
* into = v
}
return nil
}
2019-12-05 20:42:12 +00:00
// namespacedDatabaseURL returns an equivalent database url with the given namespace
// so that a database opened with the url does not conflict with other databases
// opened with a different namespace.
func namespacedDatabaseURL ( dbURL , namespace string ) ( string , error ) {
parsed , err := url . Parse ( dbURL )
if err != nil {
return "" , err
}
switch dbutil . ImplementationForScheme ( parsed . Scheme ) {
case dbutil . Postgres :
return pgutil . ConnstrWithSchema ( dbURL , namespace ) , nil
case dbutil . Cockroach :
parsed . Path += "/" + namespace
return parsed . String ( ) , nil
default :
return "" , errs . New ( "unable to namespace db url: %q" , dbURL )
}
}