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
}