@cryptotaxi247 / kubo / commits / 9a6ee6f5e

Improve test node spawning

This commit was moved from ipfs/go-ipfs-http-client@6bb2a287a6f5090792e07f3e3a74871c459b395f

Łukasz Magiera committed Feb 14, 2019 at 17:30 UTC 9a6ee6f5e153b96afda0f4ca1ee893cf28aa4f57
1 file changed +131 -67
client/httpapi/api_test.go
+131 -67
@@ -2,23 +2,23 @@ package httpapi
2
3 import (
4 "context"
5 - "fmt"
5 "io/ioutil"
6 gohttp "net/http"
7 "os"
9 - "path"
8 "strconv"
9 + "sync"
10 "testing"
11
12 "github.com/ipfs/interface-go-ipfs-core"
14 - caopts "github.com/ipfs/interface-go-ipfs-core/options"
13 "github.com/ipfs/interface-go-ipfs-core/tests"
14 local "github.com/ipfs/iptb-plugins/local"
17 - "github.com/ipfs/iptb/cli"
15 "github.com/ipfs/iptb/testbed"
16 "github.com/ipfs/iptb/testbed/interfaces"
17 + ma "github.com/multiformats/go-multiaddr"
18 )
19
20 +const parallelSpeculativeNodes = 15 // 15 seems to work best
21 +
22 func init() {
23 _, err := testbed.RegisterPlugin(testbed.IptbPlugin{
24 From: "<builtin>",
@@ -33,99 +33,163 @@ func init() {
33 }
34 }
35
36 -type NodeProvider struct{}
36 +type NodeProvider struct {
37 + simple <-chan func(context.Context) ([]iface.CoreAPI, error)
38 +}
39 +
40 +func newNodeProvider(ctx context.Context) *NodeProvider {
41 + simpleNodes := make(chan func(context.Context) ([]iface.CoreAPI, error), parallelSpeculativeNodes)
42 +
43 + np := &NodeProvider{
44 + simple: simpleNodes,
45 + }
46 +
47 + // start basic nodes speculatively in parallel
48 + for i := 0; i < parallelSpeculativeNodes; i++ {
49 + go func() {
50 + for {
51 + ctx, cancel := context.WithCancel(ctx)
52 +
53 + snd, err := np.makeAPISwarm(ctx, false, 1)
54 +
55 + res := func(ctx context.Context) ([]iface.CoreAPI, error) {
56 + if err != nil {
57 + return nil, err
58 + }
59 +
60 + go func() {
61 + <-ctx.Done()
62 + cancel()
63 + }()
64 +
65 + return snd, nil
66 + }
67 +
68 + select {
69 + case simpleNodes <- res:
70 + case <-ctx.Done():
71 + return
72 + }
73 + }
74 + }()
75 + }
76 +
77 + return np
78 +}
79 +
80 +func (np *NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]iface.CoreAPI, error) {
81 + if !fullIdentity && n == 1 {
82 + return (<-np.simple)(ctx)
83 + }
84 + return np.makeAPISwarm(ctx, fullIdentity, n)
85 +}
86
38 -func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]iface.CoreAPI, error) {
87 +func (NodeProvider) makeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]iface.CoreAPI, error) {
88
89 dir, err := ioutil.TempDir("", "httpapi-tb-")
90 if err != nil {
91 return nil, err
92 }
93
45 - c := cli.NewCli() //TODO: is there a better way?
94 + tb := testbed.NewTestbed(dir)
95
47 - initArgs := []string{"iptb", "--IPTB_ROOT", dir, "auto", "-type", "localipfs", "-count", strconv.FormatInt(int64(n), 10)}
48 - if err := c.Run(initArgs); err != nil {
96 + specs, err := testbed.BuildSpecs(tb.Dir(), n, "localipfs", nil)
97 + if err != nil {
98 return nil, err
99 }
100
52 - filestoreArgs := []string{"iptb", "--IPTB_ROOT", dir, "run", fmt.Sprintf("[0-%d]", n-1), "--", "ipfs", "config", "--json", "Experimental.FilestoreEnabled", "true"}
53 - if err := c.Run(filestoreArgs); err != nil {
101 + if err := testbed.WriteNodeSpecs(tb.Dir(), specs); err != nil {
102 return nil, err
103 }
104
57 - startArgs := []string{"iptb", "--IPTB_ROOT", dir, "start", "-wait", "--", "--enable-pubsub-experiment", "--offline=" + strconv.FormatBool(n == 1)}
58 - if err := c.Run(startArgs); err != nil {
105 + nodes, err := tb.Nodes()
106 + if err != nil {
107 return nil, err
108 }
109
62 - if n > 1 {
63 - connectArgs := []string{"iptb", "--IPTB_ROOT", dir, "connect", fmt.Sprintf("[1-%d]", n-1), "0"}
64 - if err := c.Run(connectArgs); err != nil {
65 - return nil, err
66 - }
110 + apis := make([]iface.CoreAPI, n)
111 +
112 + wg := sync.WaitGroup{}
113 + zero := sync.WaitGroup{}
114 +
115 + wg.Add(len(nodes))
116 + zero.Add(1)
117 +
118 + for i, nd := range nodes {
119 + go func(i int, nd testbedi.Core) {
120 + defer wg.Done()
121 +
122 + if _, err := nd.Init(ctx, "--empty-repo"); err != nil {
123 + panic(err)
124 + }
125 +
126 + if _, err := nd.RunCmd(ctx, nil, "ipfs", "config", "--json", "Experimental.FilestoreEnabled", "true"); err != nil {
127 + panic(err)
128 + }
129 +
130 + if _, err := nd.Start(ctx, true, "--enable-pubsub-experiment", "--offline="+strconv.FormatBool(n == 1)); err != nil {
131 + panic(err)
132 + }
133 +
134 + if i > 0 {
135 + zero.Wait()
136 + if err := nd.Connect(ctx, nodes[0]); err != nil {
137 + panic(err)
138 + }
139 + } else {
140 + zero.Done()
141 + }
142 +
143 + addr, err := nd.APIAddr()
144 + if err != nil {
145 + panic(err)
146 + }
147 +
148 + maddr, err := ma.NewMultiaddr(addr)
149 + if err != nil {
150 + panic(err)
151 + }
152 +
153 + c := &gohttp.Client{
154 + Transport: &gohttp.Transport{
155 + Proxy: gohttp.ProxyFromEnvironment,
156 + DisableKeepAlives: true,
157 + DisableCompression: true,
158 + },
159 + }
160 + apis[i] = NewApiWithClient(maddr, c)
161 +
162 + // empty node is pinned even with --empty-repo, we don't want that
163 + emptyNode, err := iface.ParsePath("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn")
164 + if err != nil {
165 + panic(err)
166 + }
167 + if err := apis[i].Pin().Rm(ctx, emptyNode); err != nil {
168 + panic(err)
169 + }
170 + }(i, nd)
171 }
172
173 + wg.Wait()
174 +
175 go func() {
176 <-ctx.Done()
177
178 defer os.Remove(dir)
179
180 defer func() {
75 - _ = c.Run([]string{"iptb", "--IPTB_ROOT", dir, "stop"})
181 + for _, nd := range nodes {
182 + _ = nd.Stop(context.Background())
183 + }
184 }()
185 }()
186
79 - apis := make([]iface.CoreAPI, n)
80 -
81 - for i := range apis {
82 - tb := testbed.NewTestbed(path.Join(dir, "testbeds", "default"))
83 -
84 - node, err := tb.Node(i)
85 - if err != nil {
86 - return nil, err
87 - }
88 -
89 - attrNode, ok := node.(testbedi.Attribute)
90 - if !ok {
91 - return nil, fmt.Errorf("node does not implement attributes")
92 - }
93 -
94 - pth, err := attrNode.Attr("path")
95 - if err != nil {
96 - return nil, err
97 - }
98 -
99 - a := ApiAddr(pth)
100 - if a == nil {
101 - return nil, fmt.Errorf("nil addr for node")
102 - }
103 - c := &gohttp.Client{
104 - Transport: &gohttp.Transport{
105 - Proxy: gohttp.ProxyFromEnvironment,
106 - DisableKeepAlives: true,
107 - DisableCompression: true,
108 - },
109 - }
110 - apis[i] = NewApiWithClient(a, c)
111 -
112 - // node cleanup
113 - // TODO: pass --empty-repo somehow (how?)
114 - pins, err := apis[i].Pin().Ls(ctx, caopts.Pin.Type.Recursive())
115 - if err != nil {
116 - return nil, err
117 - }
118 - for _, pin := range pins { //TODO: parallel
119 - if err := apis[i].Pin().Rm(ctx, pin.Path()); err != nil {
120 - return nil, err
121 - }
122 - }
123 -
124 - }
125 -
187 return apis, nil
188 }
189
190 func TestHttpApi(t *testing.T) {
130 - tests.TestApi(&NodeProvider{})(t)
191 + ctx, cancel := context.WithCancel(context.Background())
192 + defer cancel()
193 +
194 + tests.TestApi(newNodeProvider(ctx))(t)
195 }