resolve merge conflicts
This commit is contained in:
commit
e6bc6aafdf
75
lotus-soup/compositions/composition-drand-halt.toml
Normal file
75
lotus-soup/compositions/composition-drand-halt.toml
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
[metadata]
|
||||||
|
name = "lotus-soup"
|
||||||
|
author = ""
|
||||||
|
|
||||||
|
[global]
|
||||||
|
plan = "lotus-soup"
|
||||||
|
case = "drand-halting"
|
||||||
|
total_instances = 6
|
||||||
|
builder = "docker:go"
|
||||||
|
runner = "local:docker"
|
||||||
|
|
||||||
|
[global.build]
|
||||||
|
selectors = ["testground"]
|
||||||
|
|
||||||
|
[global.build_config]
|
||||||
|
enable_go_build_cache = true
|
||||||
|
|
||||||
|
[global.run.test_params]
|
||||||
|
clients = "1"
|
||||||
|
miners = "1"
|
||||||
|
balance = "2000"
|
||||||
|
sectors = "10"
|
||||||
|
random_beacon_type = "local-drand"
|
||||||
|
genesis_timestamp_offset = "0"
|
||||||
|
|
||||||
|
[[groups]]
|
||||||
|
id = "bootstrapper"
|
||||||
|
[groups.resources]
|
||||||
|
memory = "120Mi"
|
||||||
|
cpu = "10m"
|
||||||
|
[groups.instances]
|
||||||
|
count = 1
|
||||||
|
percentage = 0.0
|
||||||
|
[groups.run]
|
||||||
|
[groups.run.test_params]
|
||||||
|
role = "bootstrapper"
|
||||||
|
|
||||||
|
|
||||||
|
[[groups]]
|
||||||
|
id = "miners"
|
||||||
|
[groups.resources]
|
||||||
|
memory = "120Mi"
|
||||||
|
cpu = "10m"
|
||||||
|
[groups.instances]
|
||||||
|
count = 1
|
||||||
|
percentage = 0.0
|
||||||
|
[groups.run]
|
||||||
|
[groups.run.test_params]
|
||||||
|
role = "miner"
|
||||||
|
|
||||||
|
|
||||||
|
[[groups]]
|
||||||
|
id = "clients"
|
||||||
|
[groups.resources]
|
||||||
|
memory = "120Mi"
|
||||||
|
cpu = "10m"
|
||||||
|
[groups.instances]
|
||||||
|
count = 1
|
||||||
|
percentage = 0.0
|
||||||
|
[groups.run]
|
||||||
|
[groups.run.test_params]
|
||||||
|
role = "client"
|
||||||
|
|
||||||
|
|
||||||
|
[[groups]]
|
||||||
|
id = "drand"
|
||||||
|
[groups.instances]
|
||||||
|
count = 3
|
||||||
|
percentage = 0.0
|
||||||
|
[groups.run]
|
||||||
|
[groups.run.test_params]
|
||||||
|
role = "drand"
|
||||||
|
drand_period = "1s"
|
||||||
|
drand_log_level = "none"
|
||||||
|
suspend_events = "wait 20s -> halt -> wait 1m -> resume -> wait 2s -> halt -> wait 1m -> resume"
|
@ -9,6 +9,17 @@
|
|||||||
builder = "docker:go"
|
builder = "docker:go"
|
||||||
runner = "local:docker"
|
runner = "local:docker"
|
||||||
|
|
||||||
|
[global.build_config]
|
||||||
|
enable_go_build_cache = true
|
||||||
|
|
||||||
|
[global.run.test_params]
|
||||||
|
clients = "1"
|
||||||
|
miners = "1"
|
||||||
|
balance = "2000"
|
||||||
|
sectors = "10"
|
||||||
|
random_beacon_type = "local-drand"
|
||||||
|
genesis_timestamp_offset = "0"
|
||||||
|
|
||||||
[[groups]]
|
[[groups]]
|
||||||
id = "bootstrapper"
|
id = "bootstrapper"
|
||||||
[groups.resources]
|
[groups.resources]
|
||||||
@ -20,11 +31,7 @@
|
|||||||
[groups.run]
|
[groups.run]
|
||||||
[groups.run.test_params]
|
[groups.run.test_params]
|
||||||
role = "bootstrapper"
|
role = "bootstrapper"
|
||||||
clients = "1"
|
|
||||||
miners = "1"
|
|
||||||
balance = "2000"
|
|
||||||
sectors = "10"
|
|
||||||
random_beacon_type = "local-drand"
|
|
||||||
|
|
||||||
[[groups]]
|
[[groups]]
|
||||||
id = "miners"
|
id = "miners"
|
||||||
@ -37,12 +44,6 @@
|
|||||||
[groups.run]
|
[groups.run]
|
||||||
[groups.run.test_params]
|
[groups.run.test_params]
|
||||||
role = "miner"
|
role = "miner"
|
||||||
clients = "1"
|
|
||||||
miners = "1"
|
|
||||||
balance = "2000"
|
|
||||||
sectors = "10"
|
|
||||||
random_beacon_type = "local-drand"
|
|
||||||
|
|
||||||
|
|
||||||
[[groups]]
|
[[groups]]
|
||||||
id = "clients"
|
id = "clients"
|
||||||
@ -55,12 +56,6 @@
|
|||||||
[groups.run]
|
[groups.run]
|
||||||
[groups.run.test_params]
|
[groups.run.test_params]
|
||||||
role = "client"
|
role = "client"
|
||||||
clients = "1"
|
|
||||||
miners = "1"
|
|
||||||
balance = "2000"
|
|
||||||
sectors = "10"
|
|
||||||
random_beacon_type = "local-drand"
|
|
||||||
|
|
||||||
|
|
||||||
[[groups]]
|
[[groups]]
|
||||||
id = "drand"
|
id = "drand"
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
[global.run_config]
|
[global.run_config]
|
||||||
exposed_ports = ["6060", "1234", "2345"]
|
exposed_ports = ["6060", "1234", "2345"]
|
||||||
|
|
||||||
|
[global.build]
|
||||||
|
selectors = ["testground"]
|
||||||
|
|
||||||
[global.run.test_params]
|
[global.run.test_params]
|
||||||
clients = "3"
|
clients = "3"
|
||||||
miners = "2"
|
miners = "2"
|
||||||
|
@ -3,12 +3,13 @@ module github.com/filecoin-project/oni/lotus-soup
|
|||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/drand/drand v0.9.2-0.20200616080806-a94e9c1636a4
|
github.com/drand/drand v0.9.2-0.20200616080806-a94e9c1636a4
|
||||||
github.com/filecoin-project/go-address v0.0.2-0.20200504173055-8b6f2fb2b3ef
|
github.com/filecoin-project/go-address v0.0.2-0.20200504173055-8b6f2fb2b3ef
|
||||||
github.com/filecoin-project/go-fil-markets v0.3.0
|
github.com/filecoin-project/go-fil-markets v0.3.0
|
||||||
github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24
|
github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24
|
||||||
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b
|
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b
|
||||||
github.com/filecoin-project/lotus v0.4.1-0.20200623211458-e8642442267b
|
github.com/filecoin-project/lotus v0.4.1-0.20200625154728-28b4476398ae
|
||||||
github.com/filecoin-project/specs-actors v0.6.2-0.20200617175406-de392ca14121
|
github.com/filecoin-project/specs-actors v0.6.2-0.20200617175406-de392ca14121
|
||||||
github.com/gorilla/mux v1.7.4
|
github.com/gorilla/mux v1.7.4
|
||||||
github.com/influxdata/influxdb v1.8.0 // indirect
|
github.com/influxdata/influxdb v1.8.0 // indirect
|
||||||
|
@ -255,21 +255,23 @@ github.com/filecoin-project/go-statestore v0.1.0 h1:t56reH59843TwXHkMcwyuayStBIi
|
|||||||
github.com/filecoin-project/go-statestore v0.1.0/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI=
|
github.com/filecoin-project/go-statestore v0.1.0/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI=
|
||||||
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b h1:fkRZSPrYpk42PV3/lIXiL0LHetxde7vyYYvSsttQtfg=
|
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b h1:fkRZSPrYpk42PV3/lIXiL0LHetxde7vyYYvSsttQtfg=
|
||||||
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b/go.mod h1:Q0GQOBtKf1oE10eSXSlhN45kDBdGvEcVOqMiffqX+N8=
|
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b/go.mod h1:Q0GQOBtKf1oE10eSXSlhN45kDBdGvEcVOqMiffqX+N8=
|
||||||
github.com/filecoin-project/lotus v0.4.1-0.20200623211458-e8642442267b h1:Oq1ABSZVYNFtvWqnj3bHINcw34T0if/0/zlsRq8rGgc=
|
github.com/filecoin-project/lotus v0.4.1-0.20200625154728-28b4476398ae h1:HuI6Y3TUFQ4Zy3NhPrZkk1aR6KbwaYH8bzFzX28wyXg=
|
||||||
github.com/filecoin-project/lotus v0.4.1-0.20200623211458-e8642442267b/go.mod h1:uxvEKQiyuXisy/7MB6pFwb4WxxcA3UY3hyJnhL+k7M4=
|
github.com/filecoin-project/lotus v0.4.1-0.20200625154728-28b4476398ae/go.mod h1:1nbfphyE76GxxUWVPBQGhMTDAOQXZMBPBpIfn+od4qg=
|
||||||
github.com/filecoin-project/sector-storage v0.0.0-20200615154852-728a47ab99d6/go.mod h1:M59QnAeA/oV+Z8oHFLoNpGMv0LZ8Rll+vHVXX7GirPM=
|
github.com/filecoin-project/sector-storage v0.0.0-20200615154852-728a47ab99d6/go.mod h1:M59QnAeA/oV+Z8oHFLoNpGMv0LZ8Rll+vHVXX7GirPM=
|
||||||
github.com/filecoin-project/sector-storage v0.0.0-20200618073200-d9de9b7cb4b4 h1:lQC8Fbyn31/H4QxYAYwVV3PYZ9vS61EmjktZc5CaiYs=
|
github.com/filecoin-project/sector-storage v0.0.0-20200623210524-47d93356586d/go.mod h1:8f0hWDzzIi1hKs4IVKH9RnDsO4LEHVz8BNat0okDOuY=
|
||||||
github.com/filecoin-project/sector-storage v0.0.0-20200618073200-d9de9b7cb4b4/go.mod h1:M59QnAeA/oV+Z8oHFLoNpGMv0LZ8Rll+vHVXX7GirPM=
|
github.com/filecoin-project/sector-storage v0.0.0-20200623224636-de544b531601 h1:EgMmHLoJ4caLU8RzgKQux4TyX/ZploXGtIu5Q1SaxKw=
|
||||||
|
github.com/filecoin-project/sector-storage v0.0.0-20200623224636-de544b531601/go.mod h1:8f0hWDzzIi1hKs4IVKH9RnDsO4LEHVz8BNat0okDOuY=
|
||||||
github.com/filecoin-project/specs-actors v0.0.0-20200210130641-2d1fbd8672cf/go.mod h1:xtDZUB6pe4Pksa/bAJbJ693OilaC5Wbot9jMhLm3cZA=
|
github.com/filecoin-project/specs-actors v0.0.0-20200210130641-2d1fbd8672cf/go.mod h1:xtDZUB6pe4Pksa/bAJbJ693OilaC5Wbot9jMhLm3cZA=
|
||||||
github.com/filecoin-project/specs-actors v0.3.0/go.mod h1:nQYnFbQ7Y0bHZyq6HDEuVlCPR+U3z5Q3wMOQ+2aiV+Y=
|
github.com/filecoin-project/specs-actors v0.3.0/go.mod h1:nQYnFbQ7Y0bHZyq6HDEuVlCPR+U3z5Q3wMOQ+2aiV+Y=
|
||||||
github.com/filecoin-project/specs-actors v0.6.0/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY=
|
github.com/filecoin-project/specs-actors v0.6.0/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY=
|
||||||
github.com/filecoin-project/specs-actors v0.6.1/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY=
|
github.com/filecoin-project/specs-actors v0.6.1/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY=
|
||||||
github.com/filecoin-project/specs-actors v0.6.2-0.20200617175406-de392ca14121 h1:oRA+b4iN4H86xXDXbU3TOyvmBZp7//c5VqTc0oJ6nLg=
|
github.com/filecoin-project/specs-actors v0.6.2-0.20200617175406-de392ca14121 h1:oRA+b4iN4H86xXDXbU3TOyvmBZp7//c5VqTc0oJ6nLg=
|
||||||
github.com/filecoin-project/specs-actors v0.6.2-0.20200617175406-de392ca14121/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY=
|
github.com/filecoin-project/specs-actors v0.6.2-0.20200617175406-de392ca14121/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY=
|
||||||
github.com/filecoin-project/specs-storage v0.1.0 h1:PkDgTOT5W5Ao7752onjDl4QSv+sgOVdJbvFjOnD5w94=
|
|
||||||
github.com/filecoin-project/specs-storage v0.1.0/go.mod h1:Pr5ntAaxsh+sLG/LYiL4tKzvA83Vk5vLODYhfNwOg7k=
|
github.com/filecoin-project/specs-storage v0.1.0/go.mod h1:Pr5ntAaxsh+sLG/LYiL4tKzvA83Vk5vLODYhfNwOg7k=
|
||||||
github.com/filecoin-project/storage-fsm v0.0.0-20200617183754-4380106d3e94 h1:zPKiZPMgkFF0Lq13hsk8lcWlxeVAs6vvJaa3uHn9v70=
|
github.com/filecoin-project/specs-storage v0.1.1-0.20200622113353-88a9704877ea h1:iixjULRQFPn7Q9KlIqfwLJnlAXO10bbkI+xy5GKGdLY=
|
||||||
github.com/filecoin-project/storage-fsm v0.0.0-20200617183754-4380106d3e94/go.mod h1:q1YCutTSMq/yGYvDPHReT37bPfDLHltnwJutzR9kOY0=
|
github.com/filecoin-project/specs-storage v0.1.1-0.20200622113353-88a9704877ea/go.mod h1:Pr5ntAaxsh+sLG/LYiL4tKzvA83Vk5vLODYhfNwOg7k=
|
||||||
|
github.com/filecoin-project/storage-fsm v0.0.0-20200623213010-fe71d5b42de3 h1:nH3L7YVqrHINOmvZ+5jFjFNSi9/swXcm+uufXpkFJfo=
|
||||||
|
github.com/filecoin-project/storage-fsm v0.0.0-20200623213010-fe71d5b42de3/go.mod h1:Nl0JX9I3fIVtPEJ9HzGzO4D8LXehT9PqvUQUbNvcstc=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||||
@ -478,8 +480,9 @@ github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJ
|
|||||||
github.com/ipfs/go-blockservice v0.0.3/go.mod h1:/NNihwTi6V2Yr6g8wBI+BSwPuURpBRMtYNGrlxZ8KuI=
|
github.com/ipfs/go-blockservice v0.0.3/go.mod h1:/NNihwTi6V2Yr6g8wBI+BSwPuURpBRMtYNGrlxZ8KuI=
|
||||||
github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbRhbvNSdgc/7So=
|
github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbRhbvNSdgc/7So=
|
||||||
github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M=
|
github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M=
|
||||||
github.com/ipfs/go-blockservice v0.1.3 h1:9XgsPMwwWJSC9uVr2pMDsW2qFTBSkxpGMhmna8mIjPM=
|
|
||||||
github.com/ipfs/go-blockservice v0.1.3/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU=
|
github.com/ipfs/go-blockservice v0.1.3/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU=
|
||||||
|
github.com/ipfs/go-blockservice v0.1.4-0.20200624145336-a978cec6e834 h1:hFJoI1D2a3MqiNkSb4nKwrdkhCngUxUTFNwVwovZX2s=
|
||||||
|
github.com/ipfs/go-blockservice v0.1.4-0.20200624145336-a978cec6e834/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU=
|
||||||
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
||||||
github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
||||||
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
var cases = map[string]interface{}{
|
var cases = map[string]interface{}{
|
||||||
"deals-e2e": testkit.WrapTestEnvironment(dealsE2E),
|
"deals-e2e": testkit.WrapTestEnvironment(dealsE2E),
|
||||||
|
"drand-halting": testkit.WrapTestEnvironment(dealsE2E),
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -35,12 +35,39 @@ instances = { min = 1, max = 100, default = 5 }
|
|||||||
# in the same composition group. There must be at least threshold drand nodes.
|
# in the same composition group. There must be at least threshold drand nodes.
|
||||||
# To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand"
|
# To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand"
|
||||||
# for the lotus node groups.
|
# for the lotus node groups.
|
||||||
drand_period = { type = "duration", default="10s" }
|
drand_period = { type = "duration", default="10s" }
|
||||||
drand_threshold = { type = "int", default = 2 }
|
drand_threshold = { type = "int", default = 2 }
|
||||||
drand_gossip_relay = { type = "bool", default = true }
|
drand_gossip_relay = { type = "bool", default = true }
|
||||||
|
drand_log_level = { type = "string", default="info" }
|
||||||
|
|
||||||
# Params relevant to pubsub tracing
|
# Params relevant to pubsub tracing
|
||||||
enable_pubsub_tracer = { type = "bool", default = false }
|
enable_pubsub_tracer = { type = "bool", default = false }
|
||||||
|
|
||||||
# Mining Mode: synchronized -vs- natural time
|
[[testcases]]
|
||||||
|
name = "drand-halting"
|
||||||
|
instances = { min = 1, max = 100, default = 5 }
|
||||||
|
|
||||||
|
[testcases.params]
|
||||||
|
clients = { type = "int", default = 1 }
|
||||||
|
miners = { type = "int", default = 1 }
|
||||||
|
balance = { type = "int", default = 1 }
|
||||||
|
sectors = { type = "int", default = 1 }
|
||||||
|
role = { type = "string" }
|
||||||
|
genesis_timestamp_offset = { type = "int", default = 0 }
|
||||||
|
|
||||||
|
|
||||||
|
random_beacon_type = { type = "enum", default = "local-drand", options = ["mock", "local-drand", "external-drand"] }
|
||||||
|
|
||||||
|
# Params relevant to drand nodes. drand nodes should have role="drand", and must all be
|
||||||
|
# in the same composition group. There must be at least threshold drand nodes.
|
||||||
|
# To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand"
|
||||||
|
# for the lotus node groups.
|
||||||
|
drand_period = { type = "duration", default="10s" }
|
||||||
|
drand_threshold = { type = "int", default = 2 }
|
||||||
|
drand_gossip_relay = { type = "bool", default = true }
|
||||||
|
drand_log_level = { type = "string", default="info" }
|
||||||
|
suspend_events = { type = "string", default="", desc = "a sequence of halt/resume/wait events separated by '->'" }
|
||||||
|
|
||||||
|
# Params relevant to pubsub tracing
|
||||||
|
enable_pubsub_tracer = { type = "bool", default = false } # Mining Mode: synchronized -vs- natural time
|
||||||
mining_mode = { type = "enum", default = "synchronized", options = ["synchronized", "natural"] }
|
mining_mode = { type = "enum", default = "synchronized", options = ["synchronized", "natural"] }
|
||||||
|
108
lotus-soup/statemachine/statemachine.go
Normal file
108
lotus-soup/statemachine/statemachine.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package statemachine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This code has been shamelessly lifted from this blog post:
|
||||||
|
// https://venilnoronha.io/a-simple-state-machine-framework-in-go
|
||||||
|
// Many thanks to the author, Venil Norohnha
|
||||||
|
|
||||||
|
// ErrEventRejected is the error returned when the state machine cannot process
|
||||||
|
// an event in the state that it is in.
|
||||||
|
var ErrEventRejected = errors.New("event rejected")
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Default represents the default state of the system.
|
||||||
|
Default StateType = ""
|
||||||
|
|
||||||
|
// NoOp represents a no-op event.
|
||||||
|
NoOp EventType = "NoOp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StateType represents an extensible state type in the state machine.
|
||||||
|
type StateType string
|
||||||
|
|
||||||
|
// EventType represents an extensible event type in the state machine.
|
||||||
|
type EventType string
|
||||||
|
|
||||||
|
// EventContext represents the context to be passed to the action implementation.
|
||||||
|
type EventContext interface{}
|
||||||
|
|
||||||
|
// Action represents the action to be executed in a given state.
|
||||||
|
type Action interface {
|
||||||
|
Execute(eventCtx EventContext) EventType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events represents a mapping of events and states.
|
||||||
|
type Events map[EventType]StateType
|
||||||
|
|
||||||
|
// State binds a state with an action and a set of events it can handle.
|
||||||
|
type State struct {
|
||||||
|
Action Action
|
||||||
|
Events Events
|
||||||
|
}
|
||||||
|
|
||||||
|
// States represents a mapping of states and their implementations.
|
||||||
|
type States map[StateType]State
|
||||||
|
|
||||||
|
// StateMachine represents the state machine.
|
||||||
|
type StateMachine struct {
|
||||||
|
// Previous represents the previous state.
|
||||||
|
Previous StateType
|
||||||
|
|
||||||
|
// Current represents the current state.
|
||||||
|
Current StateType
|
||||||
|
|
||||||
|
// States holds the configuration of states and events handled by the state machine.
|
||||||
|
States States
|
||||||
|
|
||||||
|
// mutex ensures that only 1 event is processed by the state machine at any given time.
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNextState returns the next state for the event given the machine's current
|
||||||
|
// state, or an error if the event can't be handled in the given state.
|
||||||
|
func (s *StateMachine) getNextState(event EventType) (StateType, error) {
|
||||||
|
if state, ok := s.States[s.Current]; ok {
|
||||||
|
if state.Events != nil {
|
||||||
|
if next, ok := state.Events[event]; ok {
|
||||||
|
return next, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Default, ErrEventRejected
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendEvent sends an event to the state machine.
|
||||||
|
func (s *StateMachine) SendEvent(event EventType, eventCtx EventContext) error {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Determine the next state for the event given the machine's current state.
|
||||||
|
nextState, err := s.getNextState(event)
|
||||||
|
if err != nil {
|
||||||
|
return ErrEventRejected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify the state definition for the next state.
|
||||||
|
state, ok := s.States[nextState]
|
||||||
|
if !ok || state.Action == nil {
|
||||||
|
// configuration error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transition over to the next state.
|
||||||
|
s.Previous = s.Current
|
||||||
|
s.Current = nextState
|
||||||
|
|
||||||
|
// Execute the next state's action and loop over again if the event returned
|
||||||
|
// is not a no-op.
|
||||||
|
nextEvent := state.Action.Execute(eventCtx)
|
||||||
|
if nextEvent == NoOp {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
event = nextEvent
|
||||||
|
}
|
||||||
|
}
|
128
lotus-soup/statemachine/suspend.go
Normal file
128
lotus-soup/statemachine/suspend.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package statemachine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Running StateType = "running"
|
||||||
|
Suspended StateType = "suspended"
|
||||||
|
|
||||||
|
Halt EventType = "halt"
|
||||||
|
Resume EventType = "resume"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Suspendable interface {
|
||||||
|
Halt()
|
||||||
|
Resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
type HaltAction struct{}
|
||||||
|
|
||||||
|
func (a *HaltAction) Execute(ctx EventContext) EventType {
|
||||||
|
s, ok := ctx.(*Suspender)
|
||||||
|
if !ok {
|
||||||
|
fmt.Println("unable to halt, event context is not Suspendable")
|
||||||
|
return NoOp
|
||||||
|
}
|
||||||
|
s.target.Halt()
|
||||||
|
return NoOp
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResumeAction struct{}
|
||||||
|
|
||||||
|
func (a *ResumeAction) Execute(ctx EventContext) EventType {
|
||||||
|
s, ok := ctx.(*Suspender)
|
||||||
|
if !ok {
|
||||||
|
fmt.Println("unable to resume, event context is not Suspendable")
|
||||||
|
return NoOp
|
||||||
|
}
|
||||||
|
s.target.Resume()
|
||||||
|
return NoOp
|
||||||
|
}
|
||||||
|
|
||||||
|
type Suspender struct {
|
||||||
|
StateMachine
|
||||||
|
target Suspendable
|
||||||
|
log LogFn
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogFn func(fmt string, args ...interface{})
|
||||||
|
|
||||||
|
func NewSuspender(target Suspendable, log LogFn) *Suspender {
|
||||||
|
return &Suspender{
|
||||||
|
target: target,
|
||||||
|
log: log,
|
||||||
|
StateMachine: StateMachine{
|
||||||
|
Current: Running,
|
||||||
|
States: States{
|
||||||
|
Running: State{
|
||||||
|
Action: &ResumeAction{},
|
||||||
|
Events: Events{
|
||||||
|
Halt: Suspended,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Suspended: State{
|
||||||
|
Action: &HaltAction{},
|
||||||
|
Events: Events{
|
||||||
|
Resume: Running,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Suspender) RunEvents(eventSpec string) {
|
||||||
|
s.log("running event spec: %s", eventSpec)
|
||||||
|
for _, et := range parseEventSpec(eventSpec, s.log) {
|
||||||
|
if et.delay != 0 {
|
||||||
|
//s.log("waiting %s", et.delay.String())
|
||||||
|
time.Sleep(et.delay)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if et.event == "" {
|
||||||
|
s.log("ignoring empty event")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.log("sending event %s", et.event)
|
||||||
|
err := s.SendEvent(et.event, s)
|
||||||
|
if err != nil {
|
||||||
|
s.log("error sending event %s: %s", et.event, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type eventTiming struct {
|
||||||
|
delay time.Duration
|
||||||
|
event EventType
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEventSpec(spec string, log LogFn) []eventTiming {
|
||||||
|
fields := strings.Split(spec, "->")
|
||||||
|
out := make([]eventTiming, 0, len(fields))
|
||||||
|
for _, f := range fields {
|
||||||
|
f = strings.TrimSpace(f)
|
||||||
|
words := strings.Split(f, " ")
|
||||||
|
|
||||||
|
// TODO: try to implement a "waiting" state instead of special casing like this
|
||||||
|
if words[0] == "wait" {
|
||||||
|
if len(words) != 2 {
|
||||||
|
log("expected 'wait' to be followed by duration, e.g. 'wait 30s'. ignoring.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d, err := time.ParseDuration(words[1])
|
||||||
|
if err != nil {
|
||||||
|
log("bad argument for 'wait': %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, eventTiming{delay: d})
|
||||||
|
} else {
|
||||||
|
out = append(out, eventTiming{event: EventType(words[0])})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
@ -6,6 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
|
||||||
"github.com/testground/sdk-go/run"
|
"github.com/testground/sdk-go/run"
|
||||||
"github.com/testground/sdk-go/runtime"
|
"github.com/testground/sdk-go/runtime"
|
||||||
)
|
)
|
||||||
@ -30,6 +32,10 @@ func (t *TestEnvironment) DurationParam(name string) time.Duration {
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TestEnvironment) DebugSpew(format string, args... interface{}) {
|
||||||
|
t.RecordMessage(spew.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
// WaitUntilAllDone waits until all instances in the test case are done.
|
// WaitUntilAllDone waits until all instances in the test case are done.
|
||||||
func (t *TestEnvironment) WaitUntilAllDone() {
|
func (t *TestEnvironment) WaitUntilAllDone() {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
Loading…
Reference in New Issue
Block a user