container: refactor docker network

This commit is contained in:
Miya Chen 2017-09-07 11:34:53 +08:00
parent 2950c03765
commit 2cc0b23b92
4 changed files with 126 additions and 131 deletions

View File

@ -57,7 +57,12 @@ type Blockchain interface {
}
func NewBlockchain(network *DockerNetwork, numOfValidators int, options ...Option) (bc *blockchain) {
bc = &blockchain{opts: options}
if network == nil {
log.Error("Docker network is required")
return nil
}
bc = &blockchain{dockerNetwork: network, opts: options}
var err error
bc.dockerClient, err = client.NewEnvClient()
@ -66,18 +71,7 @@ func NewBlockchain(network *DockerNetwork, numOfValidators int, options ...Optio
return nil
}
if network == nil {
bc.defaultNetwork, err = NewDockerNetwork()
if err != nil {
log.Error("Failed to create Docker network", "err", err)
return nil
}
network = bc.defaultNetwork
}
bc.dockerNetworkName = network.Name()
bc.getFreeIPAddrs = network.GetFreeIPAddrs
bc.opts = append(bc.opts, DockerNetworkName(bc.dockerNetworkName))
bc.opts = append(bc.opts, DockerNetworkName(bc.dockerNetwork.Name()))
//Create accounts
bc.generateAccounts(numOfValidators)
@ -108,7 +102,13 @@ func NewDefaultBlockchain(network *DockerNetwork, numOfValidators int) (bc *bloc
}
func NewDefaultBlockchainWithFaulty(network *DockerNetwork, numOfNormal int, numOfFaulty int) (bc *blockchain) {
if network == nil {
log.Error("Docker network is required")
return nil
}
commonOpts := [...]Option{
DockerNetworkName(network.Name()),
DataDir("/data"),
WebSocket(),
WebSocketAddress("0.0.0.0"),
@ -130,7 +130,7 @@ func NewDefaultBlockchainWithFaulty(network *DockerNetwork, numOfNormal int, num
faultyOpts = append(faultyOpts, ImageRepository("quay.io/amis/geth_faulty"), ImageTag("latest"), FaultyMode(1))
// New env client
bc = &blockchain{}
bc = &blockchain{dockerNetwork: network}
var err error
bc.dockerClient, err = client.NewEnvClient()
if err != nil {
@ -138,24 +138,9 @@ func NewDefaultBlockchainWithFaulty(network *DockerNetwork, numOfNormal int, num
return nil
}
if network == nil {
bc.defaultNetwork, err = NewDockerNetwork()
if err != nil {
log.Error("Failed to create Docker network", "err", err)
return nil
}
network = bc.defaultNetwork
}
bc.dockerNetworkName = network.Name()
bc.getFreeIPAddrs = network.GetFreeIPAddrs
normalOpts = append(normalOpts, DockerNetworkName(bc.dockerNetworkName))
faultyOpts = append(faultyOpts, DockerNetworkName(bc.dockerNetworkName))
totalNodes := numOfNormal + numOfFaulty
ips, err := bc.getFreeIPAddrs(totalNodes)
ips, err := bc.dockerNetwork.GetFreeIPAddrs(totalNodes)
if err != nil {
log.Error("Failed to get free ip addresses", "err", err)
return nil
@ -176,7 +161,12 @@ func NewDefaultBlockchainWithFaulty(network *DockerNetwork, numOfNormal int, num
}
func NewQuorumBlockchain(network *DockerNetwork, ctn ConstellationNetwork, options ...Option) (bc *blockchain) {
bc = &blockchain{opts: options, isQuorum: true, constellationNetwork: ctn}
if network == nil {
log.Error("Docker network is required")
return nil
}
bc = &blockchain{dockerNetwork: network, opts: options, isQuorum: true, constellationNetwork: ctn}
bc.opts = append(bc.opts, IsQuorum(true))
bc.opts = append(bc.opts, NoUSB())
@ -187,18 +177,7 @@ func NewQuorumBlockchain(network *DockerNetwork, ctn ConstellationNetwork, optio
return nil
}
if network == nil {
bc.defaultNetwork, err = NewDockerNetwork()
if err != nil {
log.Error("Failed to create Docker network", "err", err)
return nil
}
network = bc.defaultNetwork
}
bc.dockerNetworkName = network.Name()
bc.getFreeIPAddrs = network.GetFreeIPAddrs
bc.opts = append(bc.opts, DockerNetworkName(bc.dockerNetworkName))
bc.opts = append(bc.opts, DockerNetworkName(bc.dockerNetwork.Name()))
//Create accounts
bc.generateAccounts(ctn.NumOfConstellations())
@ -229,7 +208,13 @@ func NewDefaultQuorumBlockchain(network *DockerNetwork, ctn ConstellationNetwork
}
func NewDefaultQuorumBlockchainWithFaulty(network *DockerNetwork, ctn ConstellationNetwork, numOfNormal int, numOfFaulty int) (bc *blockchain) {
if network == nil {
log.Error("Docker network is required")
return nil
}
commonOpts := [...]Option{
DockerNetworkName(network.Name()),
DataDir("/data"),
WebSocket(),
WebSocketAddress("0.0.0.0"),
@ -250,11 +235,10 @@ func NewDefaultQuorumBlockchainWithFaulty(network *DockerNetwork, ctn Constellat
normalOpts = append(normalOpts, ImageRepository("quay.io/amis/quorum"), ImageTag("feature_istanbul"))
faultyOpts := make([]Option, len(commonOpts), len(commonOpts)+3)
copy(faultyOpts, commonOpts[:])
// FIXME: Needs a faulty quorum
faultyOpts = append(faultyOpts, ImageRepository("quay.io/amis/quorum_faulty"), ImageTag("latest"), FaultyMode(1))
// New env client
bc = &blockchain{isQuorum: true, constellationNetwork: ctn}
bc = &blockchain{dockerNetwork: network, isQuorum: true, constellationNetwork: ctn}
var err error
bc.dockerClient, err = client.NewEnvClient()
if err != nil {
@ -262,24 +246,9 @@ func NewDefaultQuorumBlockchainWithFaulty(network *DockerNetwork, ctn Constellat
return nil
}
if network == nil {
bc.defaultNetwork, err = NewDockerNetwork()
if err != nil {
log.Error("Failed to create Docker network", "err", err)
return nil
}
network = bc.defaultNetwork
}
bc.dockerNetworkName = network.Name()
bc.getFreeIPAddrs = network.GetFreeIPAddrs
normalOpts = append(normalOpts, DockerNetworkName(bc.dockerNetworkName))
faultyOpts = append(faultyOpts, DockerNetworkName(bc.dockerNetworkName))
totalNodes := numOfNormal + numOfFaulty
ips, err := bc.getFreeIPAddrs(totalNodes)
ips, err := bc.dockerNetwork.GetFreeIPAddrs(totalNodes)
if err != nil {
log.Error("Failed to get free ip addresses", "err", err)
return nil
@ -303,9 +272,7 @@ func NewDefaultQuorumBlockchainWithFaulty(network *DockerNetwork, ctn Constellat
type blockchain struct {
dockerClient *client.Client
defaultNetwork *DockerNetwork
dockerNetworkName string
getFreeIPAddrs func(int) ([]net.IP, error)
dockerNetwork *DockerNetwork
genesisFile string
isQuorum bool
validators []Ethereum
@ -400,11 +367,6 @@ func (bc *blockchain) Stop(force bool) error {
return err
}
if bc.defaultNetwork != nil {
err := bc.defaultNetwork.Remove()
bc.defaultNetwork = nil
return err
}
return nil
}
@ -417,7 +379,7 @@ func (bc *blockchain) Validators() []Ethereum {
}
func (bc *blockchain) CreateNodes(num int, options ...Option) (nodes []Ethereum, err error) {
ips, err := bc.getFreeIPAddrs(num)
ips, err := bc.dockerNetwork.GetFreeIPAddrs(num)
if err != nil {
return nil, err
}
@ -435,7 +397,7 @@ func (bc *blockchain) CreateNodes(num int, options ...Option) (nodes []Ethereum,
opts = append(opts, HostDataDir(dataDir))
opts = append(opts, HostWebSocketPort(freeport.GetPort()))
opts = append(opts, HostIP(ips[i]))
opts = append(opts, DockerNetworkName(bc.dockerNetworkName))
opts = append(opts, DockerNetworkName(bc.dockerNetwork.Name()))
geth := NewEthereum(
bc.dockerClient,
@ -457,7 +419,7 @@ func (bc *blockchain) CreateNodes(num int, options ...Option) (nodes []Ethereum,
// ----------------------------------------------------------------------------
func (bc *blockchain) addValidators(numOfValidators int) error {
ips, err := bc.getFreeIPAddrs(numOfValidators)
ips, err := bc.dockerNetwork.GetFreeIPAddrs(numOfValidators)
if err != nil {
return err
}
@ -598,7 +560,11 @@ type ConstellationNetwork interface {
}
func NewConstellationNetwork(network *DockerNetwork, numOfValidators int, options ...ConstellationOption) (ctn *constellationNetwork) {
ctn = &constellationNetwork{opts: options}
if network == nil {
log.Error("Docker network is required")
return nil
}
ctn = &constellationNetwork{dockerNetwork: network, opts: options}
var err error
ctn.dockerClient, err = client.NewEnvClient()
@ -607,14 +573,7 @@ func NewConstellationNetwork(network *DockerNetwork, numOfValidators int, option
return nil
}
if network == nil {
log.Error("Network is required")
return
}
ctn.dockerNetworkName = network.Name()
ctn.getFreeIPAddrs = network.GetFreeIPAddrs
ctn.opts = append(ctn.opts, CTDockerNetworkName(ctn.dockerNetworkName))
ctn.opts = append(ctn.opts, CTDockerNetworkName(ctn.dockerNetwork.Name()))
ctn.setupConstellations(numOfValidators)
return ctn
@ -686,7 +645,7 @@ func (ctn *constellationNetwork) GetConstellation(idx int) Constellation {
}
func (ctn *constellationNetwork) getFreeHosts(num int) ([]net.IP, []int) {
ips, err := ctn.getFreeIPAddrs(num)
ips, err := ctn.dockerNetwork.GetFreeIPAddrs(num)
if err != nil {
log.Error("Cannot get free ip", "err", err)
return nil, nil
@ -710,9 +669,8 @@ func (ctn *constellationNetwork) getOtherNodes(ips []net.IP, ports []int, idx in
}
type constellationNetwork struct {
dockerClient *client.Client
dockerNetworkName string
getFreeIPAddrs func(int) ([]net.IP, error)
opts []ConstellationOption
constellations []Constellation
dockerClient *client.Client
dockerNetwork *DockerNetwork
opts []ConstellationOption
constellations []Constellation
}

View File

@ -22,8 +22,14 @@ import (
)
func TestEthereumBlockchain(t *testing.T) {
dockerNetwork, err := NewDockerNetwork()
if err != nil {
t.Error(err)
}
defer dockerNetwork.Remove()
chain := NewBlockchain(
nil,
dockerNetwork,
4,
ImageRepository("quay.io/amis/geth"),
ImageTag("istanbul_develop"),
@ -34,11 +40,11 @@ func TestEthereumBlockchain(t *testing.T) {
WebSocketOrigin("*"),
NoDiscover(),
Password("password.txt"),
Logging(true),
Logging(false),
)
defer chain.Finalize()
err := chain.Start(true)
err = chain.Start(true)
if err != nil {
t.Error(err)
}

View File

@ -48,7 +48,7 @@ func TestConstellationContainer(t *testing.T) {
CTHost(ip, port),
CTDockerNetworkName(dockerNetwork.Name()),
CTWorkDir("/data"),
CTLogging(true),
CTLogging(false),
CTKeyName("node"),
CTSocketFilename("node.ipc"),
CTVerbosity(3),

View File

@ -22,20 +22,43 @@ import (
"fmt"
"net"
"sync"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/getamis/istanbul-tools/log"
)
const (
FirstOctet = 172
SecondOctet = 19
NetworkName = "testnet"
FirstOctet = 172
SecondOctet = 17
networkNamePrefix = "testnet"
)
type NetworkManager interface {
TryGetFreeSubnet() string
}
var defaultNetworkManager = newNetworkManager()
func newNetworkManager() *networkManager {
return &networkManager{
secondOctet: SecondOctet,
}
}
type networkManager struct {
mutex sync.Mutex
secondOctet int
}
func (n *networkManager) TryGetFreeSubnet() string {
n.mutex.Lock()
defer n.mutex.Unlock()
n.secondOctet++
return fmt.Sprintf("%d.%d.0.0/16", FirstOctet, n.secondOctet)
}
type DockerNetwork struct {
client *client.Client
id string
@ -52,45 +75,53 @@ func NewDockerNetwork() (*DockerNetwork, error) {
return nil, err
}
for i := SecondOctet; i < 256; i++ {
// IP xxx.xxx.0.1 is reserved for docker network gateway
ipv4Addr, ipv4Net, err := net.ParseCIDR(fmt.Sprintf("%d.%d.0.1/16", FirstOctet, i))
networkName := fmt.Sprintf("%s_%d_%d", NetworkName, FirstOctet, i)
if err != nil {
return nil, err
}
network := &DockerNetwork{
client: c,
name: networkName,
ipv4Net: ipv4Net,
ipIndex: ipv4Addr,
}
if err = network.create(); err != nil {
log.Error("Failed to create network, retry...", "err", err)
} else {
return network, nil
}
network := &DockerNetwork{
client: c,
}
return nil, err
if err := network.create(); err != nil {
return nil, err
}
return network, nil
}
// create creates a docker network with given subnet
// create creates a user-defined docker network
func (n *DockerNetwork) create() error {
ipamConfig := network.IPAMConfig{
Subnet: n.ipv4Net.String(),
}
ipam := &network.IPAM{
Config: []network.IPAMConfig{ipamConfig},
n.name = fmt.Sprintf("%s%d", networkNamePrefix, time.Now().Unix())
var maxTryCount = 15
var err error
var cResp types.NetworkCreateResponse
var subnet string
for i := 0; i < maxTryCount && n.id == ""; i++ {
subnet = defaultNetworkManager.TryGetFreeSubnet()
ipam := &network.IPAM{
Config: []network.IPAMConfig{
network.IPAMConfig{
Subnet: subnet,
},
},
}
cResp, err = n.client.NetworkCreate(context.Background(), n.name, types.NetworkCreate{
IPAM: ipam,
})
if err == nil {
break
}
}
r, err := n.client.NetworkCreate(context.Background(), n.name, types.NetworkCreate{
IPAM: ipam,
})
if err != nil {
return err
}
n.id = r.ID
n.id = cResp.ID
_, n.ipv4Net, err = net.ParseCIDR(subnet)
if err != nil {
return err
}
// IP starts with xxx.xxx.0.1
// Because xxx.xxx.0.1 is reserved for default Gateway IP
n.ipIndex = net.IPv4(n.ipv4Net.IP[0], n.ipv4Net.IP[1], 0, 1)
return nil
}
@ -102,6 +133,10 @@ func (n *DockerNetwork) Name() string {
return n.name
}
func (n *DockerNetwork) Subnet() string {
return n.ipv4Net.String()
}
func (n *DockerNetwork) Remove() error {
return n.client.NetworkRemove(context.Background(), n.id)
}
@ -111,7 +146,7 @@ func (n *DockerNetwork) GetFreeIPAddrs(num int) ([]net.IP, error) {
defer n.mutex.Unlock()
ips := make([]net.IP, 0)
for i := 0; i < num; i++ {
for len(ips) < num && n.ipv4Net.Contains(n.ipIndex) {
ip := dupIP(n.ipIndex)
for j := len(ip) - 1; j >= 0; j-- {
ip[j]++
@ -119,12 +154,8 @@ func (n *DockerNetwork) GetFreeIPAddrs(num int) ([]net.IP, error) {
break
}
}
if !n.ipv4Net.Contains(ip) {
break
}
ips = append(ips, ip)
n.ipIndex = ip
ips = append(ips, ip)
}
if len(ips) != num {