charts, common, tests/*: add skeleton for load test

This commit is contained in:
Alan Chen 2017-09-01 14:54:54 +08:00
parent 83e681c947
commit 756ba4772f
10 changed files with 669 additions and 0 deletions

30
charts/const.go Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2017 AMIS Technologies
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package charts
import (
"os"
"path/filepath"
)
var (
chartBasePath string
)
func init() {
chartBasePath = filepath.Join(os.Getenv("CURDIR"), "benchmark/kubernetes")
}

80
charts/genesis.go Normal file
View File

@ -0,0 +1,80 @@
// Copyright 2017 AMIS Technologies
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package charts
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/getamis/istanbul-tools/genesis"
)
type GenesisChart struct {
name string
chartPath string
genesisFile string
args []string
}
func NewGenesisChart(addrs []common.Address, gasLimit uint64) *GenesisChart {
chartPath := filepath.Join(chartBasePath, "genesis-block")
genesisPath := filepath.Join(chartPath, ".genesis")
err := os.MkdirAll(genesisPath, 0700)
if err != nil {
log.Fatal(err)
}
chart := &GenesisChart{
name: "genesis-block",
chartPath: chartPath,
genesisFile: genesis.NewFileAt(
genesisPath,
false,
genesis.Validators(addrs...),
genesis.GasLimit(gasLimit),
),
}
relPath := strings.Replace(chart.genesisFile, chartPath+"/", "", 1)
chart.Override("genesisFileName", relPath)
return chart
}
func (chart *GenesisChart) Override(key, value string) {
chart.args = append(chart.args, fmt.Sprintf("%s=%s", key, value))
}
func (chart *GenesisChart) Install(debug bool) error {
defer os.RemoveAll(filepath.Dir(chart.genesisFile))
return installRelease(
chart.name,
chart.args,
chart.chartPath,
debug,
)
}
func (chart *GenesisChart) Uninstall() error {
return uninstallRelease(chart.name)
}

78
charts/static_nodes.go Normal file
View File

@ -0,0 +1,78 @@
// Copyright 2017 AMIS Technologies
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package charts
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"github.com/getamis/istanbul-tools/common"
)
type StaticNodesChart struct {
name string
chartPath string
staticNodesFile string
args []string
}
func NewStaticNodesChart(nodekeys []string, ipAddrs []string) *StaticNodesChart {
chartPath := filepath.Join(chartBasePath, "static-nodes")
staticNodesPath := filepath.Join(chartPath, ".static-nodes")
err := os.MkdirAll(staticNodesPath, 0700)
if err != nil {
log.Fatal(err)
}
if len(nodekeys) != len(ipAddrs) {
log.Println("The number of nodekeys and the number of IP address should be equal")
return nil
}
chart := &StaticNodesChart{
name: "static-nodes",
chartPath: chartPath,
staticNodesFile: common.GenerateStaticNodesAt(staticNodesPath, nodekeys, ipAddrs),
}
relPath := strings.Replace(chart.staticNodesFile, chartPath+"/", "", 1)
chart.Override("fileName", relPath)
return chart
}
func (chart *StaticNodesChart) Override(key, value string) {
chart.args = append(chart.args, fmt.Sprintf("%s=%s", key, value))
}
func (chart *StaticNodesChart) Install(debug bool) error {
defer os.RemoveAll(filepath.Dir(chart.staticNodesFile))
return installRelease(
chart.name,
chart.args,
chart.chartPath,
debug,
)
}
func (chart *StaticNodesChart) Uninstall() error {
return uninstallRelease(chart.name)
}

98
charts/utils.go Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2017 AMIS Technologies
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package charts
import (
"errors"
"fmt"
"os/exec"
"strings"
)
func newInstallCommand() *exec.Cmd {
return exec.Command("helm", "install")
}
func newUninstallCommand() *exec.Cmd {
return exec.Command("helm", "delete", "--purge")
}
func installRelease(name string, args []string, path string, debug bool) error {
cmd := newInstallCommand()
if name != "" {
cmd.Args = append(cmd.Args, "--name")
cmd.Args = append(cmd.Args, name)
}
if len(args) > 0 {
cmd.Args = append(cmd.Args, "--set")
cmd.Args = append(cmd.Args, strings.Join(args, ","))
}
cmd.Args = append(cmd.Args, path)
if debug {
cmd.Args = append(cmd.Args, "--dry-run")
cmd.Args = append(cmd.Args, "--debug")
}
cmd.Args = append(cmd.Args, "--wait")
if debug {
fmt.Println(cmd.Args)
}
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(err, string(output))
return err
}
fmt.Println(string(output))
return nil
}
func uninstallRelease(release string) error {
cmd := newUninstallCommand()
if release != "" {
cmd.Args = append(cmd.Args, release)
} else {
return errors.New("Unknown release name")
}
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(err)
return err
}
fmt.Println(string(output))
return nil
}
func ListCharts() {
cmd := exec.Command("helm", "list")
output, err := cmd.Output()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(output))
}

61
charts/validator.go Normal file
View File

@ -0,0 +1,61 @@
// Copyright 2017 AMIS Technologies
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package charts
import (
"fmt"
"path/filepath"
)
type ValidatorChart struct {
name string
chartPath string
args []string
}
func NewValidatorChart(name string, args []string) *ValidatorChart {
chartPath := filepath.Join(chartBasePath, "validator")
chart := &ValidatorChart{
name: "validator-" + name,
args: args,
chartPath: chartPath,
}
return chart
}
func (chart *ValidatorChart) Override(key, value string) {
chart.args = append(chart.args, fmt.Sprintf("%s=%s", key, value))
}
func (chart *ValidatorChart) Install(debug bool) error {
return installRelease(
chart.name,
chart.args,
chart.chartPath,
debug,
)
}
func (chart *ValidatorChart) Uninstall() error {
return uninstallRelease(chart.name)
}
func (chart *ValidatorChart) Name() string {
return chart.name
}

View File

@ -19,13 +19,17 @@ package common
import (
"crypto/ecdsa"
"crypto/rand"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"path/filepath"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/getamis/go-ethereum/p2p/discover"
uuid "github.com/satori/go.uuid"
)
@ -35,6 +39,14 @@ const (
nodekeyFileName = "nodekey"
)
func GenerateIPs(num int) (ips []string) {
for i := 0; i < num; i++ {
ips = append(ips, fmt.Sprintf("10.1.1.%d", i+2))
}
return ips
}
func GenerateRandomDir() (string, error) {
err := os.MkdirAll(filepath.Join(defaultLocalDir), 0700)
if err != nil {
@ -83,6 +95,44 @@ func SaveNodeKey(key *ecdsa.PrivateKey, dataDir string) error {
return nil
}
func GenerateStaticNodesAt(dir string, nodekeys []string, ipAddrs []string) (filename string) {
var nodes []string
for i, nodekey := range nodekeys {
key, err := crypto.HexToECDSA(nodekey)
if err != nil {
log.Printf("Failed to create key, err: %v\n", err)
return ""
}
node := discover.NewNode(
discover.PubkeyID(&key.PublicKey),
net.ParseIP(ipAddrs[i]),
0,
uint16(30303))
nodes = append(nodes, node.String())
}
filename = filepath.Join(dir, "static-nodes.json")
bytes, _ := json.Marshal(nodes)
if err := ioutil.WriteFile(filename, bytes, 0644); err != nil {
log.Printf("Failed to write '%s', err: %v\n", filename, err)
return ""
}
return filename
}
func GenerateStaticNodes(nodekeys []string, ipAddrs []string) (filename string) {
dir, err := GenerateRandomDir()
if err != nil {
log.Printf("Failed to generate directory, err: %v\n", err)
return ""
}
return GenerateStaticNodesAt(dir, nodekeys, ipAddrs)
}
func RandomHex() string {
b, _ := RandomBytes(32)
return common.BytesToHash(b).Hex()

22
tests/chart_installer.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2017 AMIS Technologies
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package tests
type ChartInstaller interface {
Install(bool) error
Uninstall() error
}

85
tests/load/load_test.go Normal file
View File

@ -0,0 +1,85 @@
// Copyright 2017 AMIS Technologies
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package load
import (
"testing"
"github.com/getamis/istanbul-tools/charts"
"github.com/getamis/istanbul-tools/common"
"github.com/getamis/istanbul-tools/tests"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("TPS-01: Large amount of transactions", func() {
tests.CaseTable("with number of validators",
func(numberOfValidators int) {
tests.CaseTable("with gas limit",
func(gaslimit int) {
tests.CaseTable("with txpool size",
func(txpoolSize int) {
runTests(numberOfValidators, gaslimit, txpoolSize)
},
tests.Case("2048", 2048),
tests.Case("10240", 10240),
)
},
tests.Case("21000*1000", 21000*1000),
tests.Case("21000*3000", 21000*3000),
)
},
tests.Case("4 validators", 4),
tests.Case("7 validators", 7),
tests.Case("10 validators", 10),
)
})
func runTests(numberOfValidators int, gaslimit int, txpoolSize int) {
Describe("", func() {
var (
genesisChart tests.ChartInstaller
staticNodesChart tests.ChartInstaller
)
BeforeEach(func() {
_, nodekeys, addrs := common.GenerateKeys(numberOfValidators)
genesisChart = charts.NewGenesisChart(addrs, uint64(gaslimit))
Expect(genesisChart.Install(false)).To(BeNil())
staticNodesChart = charts.NewStaticNodesChart(nodekeys, common.GenerateIPs(len(nodekeys)))
Expect(staticNodesChart.Install(false)).To(BeNil())
})
AfterEach(func() {
Expect(genesisChart.Uninstall()).To(BeNil())
Expect(staticNodesChart.Uninstall()).To(BeNil())
})
It("", func() {
})
})
}
func IstanbulLoadTest(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Istanbul Load Test Suite")
}

80
tests/table.go Normal file
View File

@ -0,0 +1,80 @@
// Copyright 2017 AMIS Technologies
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package tests
import (
"fmt"
"reflect"
"github.com/onsi/ginkgo"
)
func CaseTable(description string, itBody interface{}, entries ...tableEntry) bool {
caseTable(description, itBody, entries, false, false)
return true
}
/*
You can focus a table with `FCaseTable`.
*/
func FCaseTable(description string, itBody interface{}, entries ...tableEntry) bool {
caseTable(description, itBody, entries, false, true)
return true
}
/*
You can mark a table as pending with `PCaseTable`.
*/
func PCaseTable(description string, itBody interface{}, entries ...tableEntry) bool {
caseTable(description, itBody, entries, true, false)
return true
}
/*
You can mark a table as pending with `XCaseTable`.
*/
func XCaseTable(description string, itBody interface{}, entries ...tableEntry) bool {
caseTable(description, itBody, entries, true, false)
return true
}
func caseTable(description string, itBody interface{}, entries []tableEntry, pending bool, focused bool) {
itBodyValue := reflect.ValueOf(itBody)
if itBodyValue.Kind() != reflect.Func {
panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody))
}
if pending {
ginkgo.PDescribe(description, func() {
for _, entry := range entries {
entry.generate(itBodyValue, entries, pending, focused)
}
})
} else if focused {
ginkgo.FDescribe(description, func() {
for _, entry := range entries {
entry.generate(itBodyValue, entries, pending, focused)
}
})
} else {
ginkgo.Describe(description, func() {
for _, entry := range entries {
entry.generate(itBodyValue, entries, pending, focused)
}
})
}
}

85
tests/table_entry.go Normal file
View File

@ -0,0 +1,85 @@
package tests
import (
"reflect"
"github.com/onsi/ginkgo"
)
/*
TableEntry represents an entry in a table test. You generally use the `Entry` constructor.
*/
type tableEntry struct {
Description string
Parameters []interface{}
Pending bool
Focused bool
}
func (t tableEntry) generate(itBody reflect.Value, entries []tableEntry, pending bool, focused bool) {
if t.Pending {
ginkgo.PDescribe(t.Description, func() {
for _, entry := range entries {
entry.generate(itBody, entries, pending, focused)
}
})
return
}
values := []reflect.Value{}
for i, param := range t.Parameters {
var value reflect.Value
if param == nil {
inType := itBody.Type().In(i)
value = reflect.Zero(inType)
} else {
value = reflect.ValueOf(param)
}
values = append(values, value)
}
body := func() {
itBody.Call(values)
}
if t.Focused {
ginkgo.FDescribe(t.Description, body)
} else {
ginkgo.Describe(t.Description, body)
}
}
/*
Entry constructs a tableEntry.
The first argument is a required description (this becomes the content of the generated Ginkgo `It`).
Subsequent parameters are saved off and sent to the callback passed in to `DescribeTable`.
Each Entry ends up generating an individual Ginkgo It.
*/
func Case(description string, parameters ...interface{}) tableEntry {
return tableEntry{description, parameters, false, false}
}
/*
You can focus a particular entry with FEntry. This is equivalent to FIt.
*/
func FCase(description string, parameters ...interface{}) tableEntry {
return tableEntry{description, parameters, false, true}
}
/*
You can mark a particular entry as pending with PEntry. This is equivalent to PIt.
*/
func PCase(description string, parameters ...interface{}) tableEntry {
return tableEntry{description, parameters, true, false}
}
/*
You can mark a particular entry as pending with XEntry. This is equivalent to XIt.
*/
func XCase(description string, parameters ...interface{}) tableEntry {
return tableEntry{description, parameters, true, false}
}