[NOD-445] Enable using EC2 AutoScalingGroup to get list of btcds for mining simulator (#484)

* [NOD-445] Added option to mining simulator to get address list from AWS

* [NOD-445] Add support to get miningsimulator addresslist from AWS

* [NOD-445] Added mechanism to update when new servers come online

* [NOD-445] Set config in connectionManager

* [NOD-445] Invert DisableTLS condition in readCert
This commit is contained in:
Svarog 2019-11-24 13:03:26 +02:00 committed by Ori Newman
parent 28ee6a8026
commit 8cbc6670cc
10 changed files with 248 additions and 86 deletions

View File

@ -1,17 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICnzCCAgCgAwIBAgIRAMtb0YzwW1FdMNf8T0UNqR8wCgYIKoZIzj0EAwQwLTER
MA8GA1UEChMIZ2VuY2VydHMxGDAWBgNVBAMTD3VzZXItVmlydHVhbEJveDAeFw0x
OTAzMzAwODQ1MjhaFw0yOTAzMjgwODQ1MjhaMC0xETAPBgNVBAoTCGdlbmNlcnRz
MRgwFgYDVQQDEw91c2VyLVZpcnR1YWxCb3gwgZswEAYHKoZIzj0CAQYFK4EEACMD
gYYABAFQTRhFGAfgyfYoxCfTCoaWKYhABQ3h+JScx+oTvGfC8kVc2s7JLckOJN9u
tQTHMwrhx2ElTGpuC3vHFsuUlAtBDgF//YJUAF5fv3IQTVkSUECUp4uLYMbmvcLQ
gBE1a/jduTT8Dfhjd5SV+sV0TOxqjf+Qkb66oti1ruFvRTiWy+hu26OBvTCBujAO
BgNVHQ8BAf8EBAMCAqQwDwYDVR0TAQH/BAUwAwEB/zCBlgYDVR0RBIGOMIGLgg91
c2VyLVZpcnR1YWxCb3iCCWxvY2FsaG9zdIINKi5kYWdsYWJzLmNvbYcEfwAAAYcQ
AAAAAAAAAAAAAAAAAAAAAYcECgACD4cErBEAAYcErBvoNYcQ/oAAAAAAAABT1E8K
u3k8v4cQ/oAAAAAAAAAAQtb//ntyVocQ/oAAAAAAAABC3PMbkAyPcjAKBggqhkjO
PQQDBAOBjAAwgYgCQgD4aX3+8HCaPkUMLBRFrZ2BLP8CUeoBwSaD77Yw9vOtSEfL
Q/RMJKCG7SqDD6KnoJh6suwQwNJ6TelFWz8y2woBBwJCAJXET2Xeh5Ez7mV+geCv
7yyO7IjaNXMidyIDb+MxonvePSw4twH7uQSMcAJM97LB52tLWMhxidQ1g9ShhNM8
up7L
MIICszCCAhSgAwIBAgIQROJgDcPic4zOu5pqHnLL6jAKBggqhkjOPQQDBDAnMREw
DwYDVQQKEwhnZW5jZXJ0czESMBAGA1UEAxMJbWlrZWJ1bnR1MB4XDTE5MTExOTA4
NDYwNFoXDTI5MTExNzA4NDYwNFowJzERMA8GA1UEChMIZ2VuY2VydHMxEjAQBgNV
BAMTCW1pa2VidW50dTCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEATjMf/8pPRxm
BLU02cM9drNLds8fyW1YOtDE+LItQ59nC0dvEZhnYcotWy4TRZ0b/SYbSfg+/7CG
yYJWaTAPFPk3Abd6rKl0kfTbp6mqpmn4Jz6YbDCmAkGrSjGpZMePDeUZ+SAgR1MX
Kezy9UxP4214jFgZQO6u9iOIoueNE/5j6J0eo4HeMIHbMA4GA1UdDwEB/wQEAwIC
pDAPBgNVHRMBAf8EBTADAQH/MIG3BgNVHREEga8wgayCCW1pa2VidW50dYIJbG9j
YWxob3N0gh8qLmV1LWNlbnRyYWwtMS5jb21wdXRlLmludGVybmFsgg0qLmRhZ2xh
YnMuY29thwR/AAABhxAAAAAAAAAAAAAAAAAAAAABhwTAqACBhwSsEQABhwSsEgAB
hwSsG+pBhxD+gAAAAAAAAEDwidLhbKZ1hxD+gAAAAAAAAABC4//+dS5UhxD+gAAA
AAAAAC3rJp/UOkuVMAoGCCqGSM49BAMEA4GMADCBiAJCAeXt5Jyl/92QrTxvdQZc
fPBX28gjiG4/XTM0NdAqc2BcUGElDUtdZJ1yE6aTYdMffeaNUyk0lJ33VJWc9RB6
iOxkAkIBdg5LzImJTqunDEVJPB/hB+9P8SYJdT/Xq/uXZrgDUOd74qhrQeV6PnyT
MRR4d4Cfd51i8Q45brQPjKSdoZi8bzg=
-----END CERTIFICATE-----

View File

@ -1,7 +1,7 @@
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBaKKSYIpYlc1ERcfWQ7ljkHBLEyQM51JIdhQ1cYJjiMrW9W0FB3zY
1lF6RMIOmE/vXV8B941q7Vyg8TylAMcKPCygBwYFK4EEACOhgYkDgYYABAFQTRhF
GAfgyfYoxCfTCoaWKYhABQ3h+JScx+oTvGfC8kVc2s7JLckOJN9utQTHMwrhx2El
TGpuC3vHFsuUlAtBDgF//YJUAF5fv3IQTVkSUECUp4uLYMbmvcLQgBE1a/jduTT8
Dfhjd5SV+sV0TOxqjf+Qkb66oti1ruFvRTiWy+hu2w==
MIHcAgEBBEIBdqJs4hINoLpeYY3wFvcIWknsP30kWT94oANqTNdrcc7UkSj7s8Xv
hPqT6hNJxae9GCkuVg9lydRRaPL+Lxva8DegBwYFK4EEACOhgYkDgYYABAE4zH//
KT0cZgS1NNnDPXazS3bPH8ltWDrQxPiyLUOfZwtHbxGYZ2HKLVsuE0WdG/0mG0n4
Pv+whsmCVmkwDxT5NwG3eqypdJH026epqqZp+Cc+mGwwpgJBq0oxqWTHjw3lGfkg
IEdTFyns8vVMT+NteIxYGUDurvYjiKLnjRP+Y+idHg==
-----END EC PRIVATE KEY-----

1
go.mod
View File

@ -5,6 +5,7 @@ go 1.13
require (
bou.ke/monkey v1.0.1
github.com/aead/siphash v1.0.1
github.com/aws/aws-sdk-go v1.25.37
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd
github.com/btcsuite/goleveldb v1.0.0
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792

4
go.sum
View File

@ -18,6 +18,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.37 h1:gBtB/F3dophWpsUQKN/Kni+JzYEH2mGHF4hWNtfED1w=
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
@ -130,6 +132,7 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
@ -272,6 +275,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a h1:jd4PGQGmrzmDZANUzIol3eClsCB/Jp5GmpGWMhi6hnY=
golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=

View File

@ -2,10 +2,53 @@ package main
import (
"bufio"
"fmt"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/daglabs/btcd/dagconfig"
"github.com/pkg/errors"
)
const instanceStateCodeActive = "16"
func getAddressList(cfg *config) ([]string, error) {
if cfg.AddressListPath != "" {
return getAddressListFromPath(cfg)
}
return getAddressListFromAWS(cfg)
}
func getAddressListFromAWS(cfg *config) ([]string, error) {
log.Infof("Getting hosts list for autoscaling group %s", cfg.AutoScalingGroup)
sess := session.Must(session.NewSession(&aws.Config{Region: aws.String(cfg.Region)}))
ec2Client := ec2.New(sess)
instances, err := ec2Client.DescribeInstances(&ec2.DescribeInstancesInput{
Filters: []*ec2.Filter{
&ec2.Filter{Name: aws.String("tag:aws:autoscaling:groupName"), Values: []*string{&cfg.AutoScalingGroup}},
&ec2.Filter{Name: aws.String("instance-state-code"), Values: []*string{aws.String(instanceStateCodeActive)}},
},
})
if err != nil {
return nil, errors.Wrap(err, "Error describing instances")
}
addressList := []string{}
for _, reservation := range instances.Reservations {
for _, instance := range reservation.Instances {
if instance.PrivateDnsName == nil {
continue
}
addressList = append(addressList, fmt.Sprintf("%s:%s", *instance.PrivateDnsName, dagconfig.DevNetParams.RPCPort))
}
}
return addressList, nil
}
func getAddressListFromPath(cfg *config) ([]string, error) {
file, err := os.Open(cfg.AddressListPath)
if err != nil {
return nil, err

View File

@ -1,9 +1,10 @@
package main
import (
"path/filepath"
"github.com/daglabs/btcd/util"
"github.com/pkg/errors"
"path/filepath"
"github.com/jessevdk/go-flags"
)
@ -21,10 +22,12 @@ var (
)
type config struct {
AddressListPath string `long:"addresslist" description:"Path to a list of nodes' JSON-RPC endpoints" required:"true"`
CertificatePath string `long:"cert" description:"Path to certificate accepted by JSON-RPC endpoint"`
DisableTLS bool `long:"notls" description:"Disable TLS"`
Verbose bool `long:"verbose" short:"v" description:"Enable logging of RPC requests"`
AutoScalingGroup string `long:"autoscaling" description:"AWS AutoScalingGroup to use for address list"`
Region string `long:"region" description:"AWS region to use for address list"`
AddressListPath string `long:"addresslist" description:"Path to a list of nodes' JSON-RPC endpoints"`
CertificatePath string `long:"cert" description:"Path to certificate accepted by JSON-RPC endpoint"`
DisableTLS bool `long:"notls" description:"Disable TLS"`
Verbose bool `long:"verbose" short:"v" description:"Enable logging of RPC requests"`
}
func parseConfig() (*config, error) {
@ -44,6 +47,18 @@ func parseConfig() (*config, error) {
return nil, errors.New("--cert should be omitted if --notls is used")
}
if (cfg.AutoScalingGroup == "" || cfg.Region == "") && cfg.AddressListPath == "" {
return nil, errors.New("Either (--autoscaling and --region) or --addresslist must be specified")
}
if (cfg.AutoScalingGroup != "" || cfg.Region != "") && cfg.AddressListPath != "" {
return nil, errors.New("Both (--autoscaling and --region) and --addresslist can't be specified at the same time")
}
if cfg.AutoScalingGroup != "" && cfg.Region == "" {
return nil, errors.New("If --autoscaling is specified --region must be specified as well")
}
initLog(defaultLogFile, defaultErrLogFile)
return cfg, nil

View File

@ -0,0 +1,149 @@
package main
import (
"io/ioutil"
"time"
"github.com/daglabs/btcd/rpcclient"
"github.com/pkg/errors"
)
type connectionManager struct {
addressList []string
cert []byte
clients []*simulatorClient
cfg *config
disconnectChan chan struct{}
}
func newConnectionManager(cfg *config) (*connectionManager, error) {
connManager := &connectionManager{
cfg: cfg,
}
var err error
connManager.addressList, err = getAddressList(cfg)
if err != nil {
return nil, err
}
connManager.cert, err = readCert(cfg)
if err != nil {
return nil, err
}
connManager.clients, err = connectToServers(connManager.addressList, connManager.cert)
if err != nil {
return nil, err
}
if cfg.AutoScalingGroup != "" {
connManager.disconnectChan = make(chan struct{})
spawn(func() { connManager.refreshAddressesLoop() })
}
return connManager, nil
}
func connectToServer(address string, cert []byte) (*simulatorClient, error) {
connCfg := &rpcclient.ConnConfig{
Host: address,
Endpoint: "ws",
User: "user",
Pass: "pass",
DisableTLS: cert == nil,
RequestTimeout: time.Second * 10,
Certificates: cert,
}
client, err := newSimulatorClient(address, connCfg)
if err != nil {
return nil, err
}
log.Infof("Connected to server %s", address)
return client, nil
}
func connectToServers(addressList []string, cert []byte) ([]*simulatorClient, error) {
clients := make([]*simulatorClient, 0, len(addressList))
for _, address := range addressList {
client, err := connectToServer(address, cert)
if err != nil {
return nil, err
}
clients = append(clients, client)
}
return clients, nil
}
func readCert(cfg *config) ([]byte, error) {
if cfg.DisableTLS {
return nil, nil
}
cert, err := ioutil.ReadFile(cfg.CertificatePath)
if err != nil {
return nil, errors.Errorf("Error reading certificates file: %s", err)
}
return cert, nil
}
func (cm *connectionManager) close() {
if cm.disconnectChan != nil {
cm.disconnectChan <- struct{}{}
}
for _, client := range cm.clients {
client.Disconnect()
}
}
const refreshAddressInterval = time.Minute * 10
func (cm *connectionManager) refreshAddressesLoop() {
for {
select {
case <-time.After(refreshAddressInterval):
err := cm.refreshAddresses()
if err != nil {
panic(err)
}
case <-cm.disconnectChan:
return
}
}
}
func (cm *connectionManager) refreshAddresses() error {
newAddressList, err := getAddressList(cm.cfg)
if err != nil {
return err
}
if len(newAddressList) == len(cm.addressList) {
return nil
}
outerLoop:
for _, newAddress := range newAddressList {
for _, oldAddress := range cm.addressList {
if newAddress == oldAddress {
continue outerLoop
}
}
client, err := connectToServer(newAddress, cm.cert)
if err != nil {
return err
}
cm.clients = append(cm.clients, client)
}
cm.addressList = newAddressList
return nil
}

View File

@ -1,47 +0,0 @@
package main
import (
"github.com/pkg/errors"
"io/ioutil"
"time"
"github.com/daglabs/btcd/rpcclient"
)
func connectToServers(cfg *config, addressList []string) ([]*simulatorClient, error) {
clients := make([]*simulatorClient, len(addressList))
var cert []byte
if !cfg.DisableTLS {
var err error
cert, err = ioutil.ReadFile(cfg.CertificatePath)
if err != nil {
return nil, errors.Errorf("Error reading certificates file: %s", err)
}
}
for i, address := range addressList {
connCfg := &rpcclient.ConnConfig{
Host: address,
Endpoint: "ws",
User: "user",
Pass: "pass",
DisableTLS: cfg.DisableTLS,
RequestTimeout: time.Second * 10,
}
if !cfg.DisableTLS {
connCfg.Certificates = cert
}
var err error
clients[i], err = newSimulatorClient(address, connCfg)
if err != nil {
return nil, err
}
log.Infof("Connected to server %s", address)
}
return clients, nil
}

View File

@ -2,9 +2,10 @@ package main
import (
"fmt"
"github.com/pkg/errors"
"os"
"github.com/pkg/errors"
"github.com/daglabs/btcd/signal"
"github.com/daglabs/btcd/util/panics"
)
@ -21,19 +22,14 @@ func main() {
enableRPCLogging()
}
addressList, err := getAddressList(cfg)
connManager, err := newConnectionManager(cfg)
if err != nil {
panic(errors.Errorf("Couldn't load address list: %s", err))
panic(errors.Errorf("Error initializing connection manager: %s", err))
}
clients, err := connectToServers(cfg, addressList)
if err != nil {
panic(errors.Errorf("Error connecting to servers: %s", err))
}
defer disconnect(clients)
defer connManager.close()
spawn(func() {
err = mineLoop(clients)
err = mineLoop(connManager)
if err != nil {
panic(errors.Errorf("Error in main loop: %s", err))
}

View File

@ -3,13 +3,14 @@ package main
import (
"encoding/hex"
nativeerrors "errors"
"github.com/daglabs/btcd/rpcclient"
"github.com/pkg/errors"
"math/rand"
"strconv"
"strings"
"time"
"github.com/daglabs/btcd/rpcclient"
"github.com/pkg/errors"
"github.com/daglabs/btcd/blockdag"
"github.com/daglabs/btcd/btcjson"
"github.com/daglabs/btcd/util"
@ -170,7 +171,7 @@ func getRandomClient(clients []*simulatorClient) *simulatorClient {
return clients[random.Int63n(clientsCount)]
}
func mineLoop(clients []*simulatorClient) error {
func mineLoop(connManager *connectionManager) error {
errChan := make(chan error)
templateStopChan := make(chan struct{})
@ -178,7 +179,7 @@ func mineLoop(clients []*simulatorClient) error {
spawn(func() {
for {
foundBlock := make(chan *util.Block)
currentClient := getRandomClient(clients)
currentClient := getRandomClient(connManager.clients)
currentClient.notifyForNewBlocks = true
log.Infof("Next block will be mined by: %s", currentClient.Host())
mineNextBlock(currentClient, foundBlock, templateStopChan, errChan)