feat: persist limits to Swarm.ResourceMgr.Limits (#8901)
* feat: persist limit changes to config This changes the "ipfs swarm limit" command so that when limit changes are applied via the command line, they are persisted to the repo config, so that they remain in effect when the daemon restarts. Any existing limit.json can be dropped into the IPFS config easily using something like: cat ~/.ipfs/config | jq ".Swarm.ResourceMgr.Limits = $(cat limit.json)" | sponge ~/.ipfs/config This also upgrades to Resource Manager v0.3.0, which exports the config schema so that we don't have to maintain our own copy of it. Co-authored-by: Marcin Rataj <lidel@lidel.org>
Gus Eggert committed
Apr 28, 2022 at 15:27 UTC
74aff245d259546b65f214b413bc30172fd74a7d
6 files changed
+198
-114
config/swarm.go
+4
-42
@@ -1,5 +1,7 @@
1
package config
2
3
+import rcmgr "github.com/libp2p/go-libp2p-resource-manager"
4
+
5
type SwarmConfig struct {
6
// AddrFilters specifies a set libp2p addresses that we should never
7
// dial or receive connections from.
@@ -137,10 +139,8 @@ type ConnMgr struct {
139
// <https://github.com/libp2p/go-libp2p-resource-manager#readme>
140
type ResourceMgr struct {
141
// Enables the Network Resource Manager feature
140
- Enabled Flag `json:",omitempty"`
141
-
142
- /* TODO: decide if and how we want to expose limits in our config
143
- Limits *ResourceMgrScopeConfig `json:",omitempty"` */
142
+ Enabled Flag `json:",omitempty"`
143
+ Limits *rcmgr.BasicLimiterConfig `json:",omitempty"`
144
}
145
146
const (
@@ -150,41 +150,3 @@ const (
150
ResourceMgrProtocolScopePrefix = "proto:"
151
ResourceMgrPeerScopePrefix = "peer:"
152
)
153
-
154
-/* TODO: decide if and how we want to expose limits in our config
155
-type ResourceMgrLimitsConfig struct {
156
- System *ResourceMgrScopeConfig `json:",omitempty"`
157
- Transient *ResourceMgrScopeConfig `json:",omitempty"`
158
-
159
- ServiceDefault *ResourceMgrScopeConfig `json:",omitempty"`
160
- ServicePeerDefault *ResourceMgrScopeConfig `json:",omitempty"`
161
- Service map[string]ResourceMgrScopeConfig `json:",omitempty"`
162
- ServicePeer map[string]ResourceMgrScopeConfig `json:",omitempty"`
163
-
164
- ProtocolDefault *ResourceMgrScopeConfig `json:",omitempty"`
165
- ProtocolPeerDefault *ResourceMgrScopeConfig `json:",omitempty"`
166
- Protocol map[string]ResourceMgrScopeConfig `json:",omitempty"`
167
- ProtocolPeer map[string]ResourceMgrScopeConfig `json:",omitempty"`
168
-
169
- PeerDefault *ResourceMgrScopeConfig `json:",omitempty"`
170
- Peer map[string]ResourceMgrScopeConfig `json:",omitempty"`
171
-
172
- Conn *ResourceMgrScopeConfig `json:",omitempty"`
173
- Stream *ResourceMgrScopeConfig `json:",omitempty"`
174
-}
175
-*/
176
-
177
-// libp2p Network Resource Manager config for a scope
178
-type ResourceMgrScopeConfig struct {
179
- Dynamic bool `json:",omitempty"`
180
- // set if Dynamic is false
181
- Memory int64 `json:",omitempty"`
182
- // set if Dynamic is true
183
- MemoryFraction float64 `json:",omitempty"`
184
- MinMemory int64 `json:",omitempty"`
185
- MaxMemory int64 `json:",omitempty"`
186
-
187
- Streams, StreamsInbound, StreamsOutbound int
188
- Conns, ConnsInbound, ConnsOutbound int
189
- FD int
190
-}
core/commands/swarm.go
+4
-4
@@ -23,6 +23,7 @@ import (
23
cmds "github.com/ipfs/go-ipfs-cmds"
24
inet "github.com/libp2p/go-libp2p-core/network"
25
"github.com/libp2p/go-libp2p-core/peer"
26
+ rcmgr "github.com/libp2p/go-libp2p-resource-manager"
27
ma "github.com/multiformats/go-multiaddr"
28
madns "github.com/multiformats/go-multiaddr-dns"
29
mamask "github.com/whyrusleeping/multiaddr-filter"
@@ -380,8 +381,7 @@ It is possible to use this command to inspect and tweak limits at runtime:
381
$ vi limit.json
382
$ ipfs swarm limit system limit.json
383
383
-Changes made via command line are discarded on node shutdown.
384
-For permanent limits set Swarm.ResourceMgr.Limits in the $IPFS_PATH/config file.
384
+Changes made via command line are persisted in the Swarm.ResourceMgr.Limits field of the $IPFS_PATH/config file.
385
`},
386
Arguments: []cmds.Argument{
387
cmds.StringArg("scope", true, false, "scope of the limit"),
@@ -401,7 +401,7 @@ For permanent limits set Swarm.ResourceMgr.Limits in the $IPFS_PATH/config file.
401
402
// set scope limit to new values (when limit.json is passed as a second arg)
403
if req.Files != nil {
404
- var newLimit config.ResourceMgrScopeConfig
404
+ var newLimit rcmgr.BasicLimitConfig
405
it := req.Files.Entries()
406
if it.Next() {
407
file := files.FileFromEntry(it)
@@ -411,7 +411,7 @@ For permanent limits set Swarm.ResourceMgr.Limits in the $IPFS_PATH/config file.
411
if err := json.NewDecoder(file).Decode(&newLimit); err != nil {
412
return errors.New("failed to decode JSON as ResourceMgrScopeConfig")
413
}
414
- return libp2p.NetSetLimit(node.ResourceManager, scope, newLimit)
414
+ return libp2p.NetSetLimit(node.ResourceManager, node.Repo, scope, newLimit)
415
}
416
if err := it.Err(); err != nil {
417
return fmt.Errorf("error opening limit JSON file: %w", err)
core/node/libp2p/rcmgr.go
+69
-37
@@ -2,7 +2,6 @@ package libp2p
2
3
import (
4
"context"
5
- "errors"
5
"fmt"
6
"os"
7
"path/filepath"
@@ -27,7 +26,6 @@ var NoResourceMgrError = fmt.Errorf("missing ResourceMgr: make sure the daemon i
26
27
func ResourceManager(cfg config.SwarmConfig) func(fx.Lifecycle, repo.Repo) (network.ResourceManager, Libp2pOpts, error) {
28
return func(lc fx.Lifecycle, repo repo.Repo) (network.ResourceManager, Libp2pOpts, error) {
30
- var limiter *rcmgr.BasicLimiter
29
var manager network.ResourceManager
30
var opts Libp2pOpts
31
@@ -47,25 +45,18 @@ func ResourceManager(cfg config.SwarmConfig) func(fx.Lifecycle, repo.Repo) (netw
45
46
repoPath, err := config.PathRoot()
47
if err != nil {
50
- return nil, opts, fmt.Errorf("error opening IPFS_PATH: %w", err)
48
+ return nil, opts, fmt.Errorf("opening IPFS_PATH: %w", err)
49
}
50
53
- // Create limiter:
54
- // - parse $IPFS_PATH/limits.json if exists
55
- // - use defaultLimits from rcmgr_defaults.go
51
defaultLimits := adjustedDefaultLimits(cfg)
57
- limitFilePath := filepath.Join(repoPath, NetLimitDefaultFilename)
58
- limitFile, err := os.Open(limitFilePath)
59
- switch {
60
- case err == nil:
61
- defer limitFile.Close()
62
- limiter, err = rcmgr.NewLimiterFromJSON(limitFile, defaultLimits)
63
- if err != nil {
64
- return nil, opts, fmt.Errorf("error parsing libp2p limit file: %w", err)
65
- }
66
- case errors.Is(err, os.ErrNotExist):
67
- limiter = rcmgr.NewStaticLimiter(defaultLimits)
68
- default:
52
+
53
+ var limits rcmgr.BasicLimiterConfig
54
+ if cfg.ResourceMgr.Limits != nil {
55
+ limits = *cfg.ResourceMgr.Limits
56
+ }
57
+
58
+ limiter, err := rcmgr.NewLimiter(limits, defaultLimits)
59
+ if err != nil {
60
return nil, opts, err
61
}
62
@@ -80,9 +71,8 @@ func ResourceManager(cfg config.SwarmConfig) func(fx.Lifecycle, repo.Repo) (netw
71
72
manager, err = rcmgr.NewResourceManager(limiter, ropts...)
73
if err != nil {
83
- return nil, opts, fmt.Errorf("error creating libp2p resource manager: %w", err)
74
+ return nil, opts, fmt.Errorf("creating libp2p resource manager: %w", err)
75
}
85
-
76
} else {
77
log.Debug("libp2p resource manager is disabled")
78
manager = network.NullResourceManager
@@ -196,14 +186,13 @@ func NetStat(mgr network.ResourceManager, scope string) (NetStatOut, error) {
186
}
187
}
188
199
-func NetLimit(mgr network.ResourceManager, scope string) (config.ResourceMgrScopeConfig, error) {
200
- var result config.ResourceMgrScopeConfig
189
+func NetLimit(mgr network.ResourceManager, scope string) (rcmgr.BasicLimitConfig, error) {
190
+ var result rcmgr.BasicLimitConfig
191
getLimit := func(s network.ResourceScope) error {
192
limiter, ok := s.(rcmgr.ResourceScopeLimiter)
193
if !ok { // NullResourceManager
194
return NoResourceMgrError
195
}
206
-
196
limit := limiter.Limit()
197
switch l := limit.(type) {
198
case *rcmgr.StaticLimit:
@@ -280,7 +269,8 @@ func NetLimit(mgr network.ResourceManager, scope string) (config.ResourceMgrScop
269
}
270
}
271
283
-func NetSetLimit(mgr network.ResourceManager, scope string, limit config.ResourceMgrScopeConfig) error {
272
+// NetSetLimit sets new ResourceManager limits for the given scope. The limits take effect immediately, and are also persisted to the repo config.
273
+func NetSetLimit(mgr network.ResourceManager, repo repo.Repo, scope string, limit rcmgr.BasicLimitConfig) error {
274
setLimit := func(s network.ResourceScope) error {
275
limiter, ok := s.(rcmgr.ResourceScopeLimiter)
276
if !ok { // NullResourceManager
@@ -324,45 +314,87 @@ func NetSetLimit(mgr network.ResourceManager, scope string, limit config.Resourc
314
return nil
315
}
316
317
+ cfg, err := repo.Config()
318
+ if err != nil {
319
+ return fmt.Errorf("reading config to set limit: %w", err)
320
+ }
321
+
322
+ if cfg.Swarm.ResourceMgr.Limits == nil {
323
+ cfg.Swarm.ResourceMgr.Limits = &rcmgr.BasicLimiterConfig{}
324
+ }
325
+ configLimits := cfg.Swarm.ResourceMgr.Limits
326
+
327
+ var setConfigFunc func()
328
switch {
329
case scope == config.ResourceMgrSystemScope:
329
- err := mgr.ViewSystem(func(s network.ResourceScope) error {
330
+ err = mgr.ViewSystem(func(s network.ResourceScope) error {
331
return setLimit(s)
332
})
332
- return err
333
+ setConfigFunc = func() { configLimits.System = &limit }
334
335
case scope == config.ResourceMgrTransientScope:
335
- err := mgr.ViewTransient(func(s network.ResourceScope) error {
336
+ err = mgr.ViewTransient(func(s network.ResourceScope) error {
337
return setLimit(s)
338
})
338
- return err
339
+ setConfigFunc = func() { configLimits.Transient = &limit }
340
341
case strings.HasPrefix(scope, config.ResourceMgrServiceScopePrefix):
341
- svc := scope[4:]
342
- err := mgr.ViewService(svc, func(s network.ServiceScope) error {
342
+ svc := strings.TrimPrefix(scope, config.ResourceMgrServiceScopePrefix)
343
+ err = mgr.ViewService(svc, func(s network.ServiceScope) error {
344
return setLimit(s)
345
})
345
- return err
346
+ setConfigFunc = func() {
347
+ if configLimits.Service == nil {
348
+ configLimits.Service = map[string]rcmgr.BasicLimitConfig{}
349
+ }
350
+ configLimits.Service[svc] = limit
351
+ }
352
353
case strings.HasPrefix(scope, config.ResourceMgrProtocolScopePrefix):
348
- proto := scope[6:]
349
- err := mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error {
354
+ proto := strings.TrimPrefix(scope, config.ResourceMgrProtocolScopePrefix)
355
+ err = mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error {
356
return setLimit(s)
357
})
352
- return err
358
+ setConfigFunc = func() {
359
+ if configLimits.Protocol == nil {
360
+ configLimits.Protocol = map[string]rcmgr.BasicLimitConfig{}
361
+ }
362
+ configLimits.Protocol[proto] = limit
363
+ }
364
365
case strings.HasPrefix(scope, config.ResourceMgrPeerScopePrefix):
355
- p := scope[5:]
356
- pid, err := peer.Decode(p)
366
+ p := strings.TrimPrefix(scope, config.ResourceMgrPeerScopePrefix)
367
+ var pid peer.ID
368
+ pid, err = peer.Decode(p)
369
if err != nil {
370
return fmt.Errorf("invalid peer ID: %q: %w", p, err)
371
}
372
err = mgr.ViewPeer(pid, func(s network.PeerScope) error {
373
return setLimit(s)
374
})
363
- return err
375
+ setConfigFunc = func() {
376
+ if configLimits.Peer == nil {
377
+ configLimits.Peer = map[string]rcmgr.BasicLimitConfig{}
378
+ }
379
+ configLimits.Peer[p] = limit
380
+ }
381
382
default:
383
return fmt.Errorf("invalid scope %q", scope)
384
}
385
+
386
+ if err != nil {
387
+ return fmt.Errorf("setting new limits on resource manager: %w", err)
388
+ }
389
+
390
+ if cfg.Swarm.ResourceMgr.Limits == nil {
391
+ cfg.Swarm.ResourceMgr.Limits = &rcmgr.BasicLimiterConfig{}
392
+ }
393
+ setConfigFunc()
394
+
395
+ if err := repo.SetConfig(cfg); err != nil {
396
+ return fmt.Errorf("writing new limits to repo config: %w", err)
397
+ }
398
+
399
+ return nil
400
}
go.mod
+1
-1
@@ -82,7 +82,7 @@ require (
82
github.com/libp2p/go-libp2p-pubsub-router v0.5.0
83
github.com/libp2p/go-libp2p-quic-transport v0.16.1
84
github.com/libp2p/go-libp2p-record v0.1.3
85
- github.com/libp2p/go-libp2p-resource-manager v0.1.5
85
+ github.com/libp2p/go-libp2p-resource-manager v0.3.0
86
github.com/libp2p/go-libp2p-routing-helpers v0.2.3
87
github.com/libp2p/go-libp2p-swarm v0.10.2
88
github.com/libp2p/go-libp2p-testing v0.8.0
go.sum
+2
-1
@@ -910,8 +910,9 @@ github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7
910
github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk=
911
github.com/libp2p/go-libp2p-record v0.1.3 h1:R27hoScIhQf/A8XJZ8lYpnqh9LatJ5YbHs28kCIfql0=
912
github.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4=
913
-github.com/libp2p/go-libp2p-resource-manager v0.1.5 h1:7J6t9KLFS0MxXDTfqA6rwfVCZl/yLQnXW5LpZjHAANI=
913
github.com/libp2p/go-libp2p-resource-manager v0.1.5/go.mod h1:wJPNjeE4XQlxeidwqVY5G6DLOKqFK33u2n8blpl0I6Y=
914
+github.com/libp2p/go-libp2p-resource-manager v0.3.0 h1:2+cYxUNi33tcydsVLt6K5Fv2E3OTiVeafltecAj15E0=
915
+github.com/libp2p/go-libp2p-resource-manager v0.3.0/go.mod h1:K+eCkiapf+ey/LADO4TaMpMTP9/Qde/uLlrnRqV4PLQ=
916
github.com/libp2p/go-libp2p-routing v0.0.1/go.mod h1:N51q3yTr4Zdr7V8Jt2JIktVU+3xBBylx1MZeVA6t1Ys=
917
github.com/libp2p/go-libp2p-routing-helpers v0.2.3 h1:xY61alxJ6PurSi+MXbywZpelvuU4U4p/gPTxjqCqTzY=
918
github.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw=
test/sharness/t0139-swarm-rcmgr.sh
+118
-29
@@ -9,58 +9,147 @@ test_init_ipfs
9
# swarm limit|stats should fail in offline mode
10
11
test_expect_success 'disconnected: swarm limit requires running daemon' '
12
- test_expect_code 1 ipfs swarm limit system 2> actual &&
13
- test_should_contain "missing ResourceMgr" actual
12
+ test_expect_code 1 ipfs swarm limit system 2> actual &&
13
+ test_should_contain "missing ResourceMgr" actual
14
'
15
test_expect_success 'disconnected: swarm stats requires running daemon' '
16
- test_expect_code 1 ipfs swarm stats all 2> actual &&
17
- test_should_contain "missing ResourceMgr" actual
16
+ test_expect_code 1 ipfs swarm stats all 2> actual &&
17
+ test_should_contain "missing ResourceMgr" actual
18
'
19
20
# swarm limit|stats should fail in online mode by default
21
-# because Resource Manager is opt-in for now
21
+# because Resource Manager is opt-in
22
test_launch_ipfs_daemon
23
24
test_expect_success 'ResourceMgr disabled by default: swarm limit requires Swarm.ResourceMgr.Enabled' '
25
- test_expect_code 1 ipfs swarm limit system 2> actual &&
26
- test_should_contain "missing ResourceMgr" actual
25
+ test_expect_code 1 ipfs swarm limit system 2> actual &&
26
+ test_should_contain "missing ResourceMgr" actual
27
'
28
test_expect_success 'ResourceMgr disabled by default: swarm stats requires Swarm.ResourceMgr.Enabled' '
29
- test_expect_code 1 ipfs swarm stats all 2> actual &&
30
- test_should_contain "missing ResourceMgr" actual
29
+ test_expect_code 1 ipfs swarm stats all 2> actual &&
30
+ test_should_contain "missing ResourceMgr" actual
31
'
32
33
-# swarm limit|stat should work when Swarm.ResourceMgr.Enabled
33
test_kill_ipfs_daemon
35
-test_expect_success "test_config_set succeeds" "
36
- ipfs config --json Swarm.ResourceMgr.Enabled true
34
+
35
+test_expect_success "setting an invalid limit should result in a failure" "
36
+ test_expect_code 1 ipfs config --json Swarm.ResourceMgr.Limits.System.Conns 'asdf' 2> actual &&
37
+ test_should_contain 'failed to unmarshal' actual
38
+"
39
+
40
+# swarm limit|stat should work when Swarm.ResourceMgr.Enabled
41
+test_expect_success "test enabling resource manager" "
42
+ ipfs config --json Swarm.ResourceMgr.Enabled true &&
43
+ ipfs config --json Swarm.ResourceMgr &&
44
+ jq -e '.Swarm.ResourceMgr.Enabled == true' < \"$IPFS_PATH/config\"
45
"
46
+
47
test_launch_ipfs_daemon
48
49
+test_expect_success "test setting system conns limit" "
50
+ ipfs config --json Swarm.ResourceMgr.Enabled true &&
51
+ ipfs config --json Swarm.ResourceMgr.Limits.System.Conns 99999
52
+"
53
+
54
# every scope has the same fields, so we only inspect System
55
test_expect_success 'ResourceMgr enabled: swarm limit' '
42
- ipfs swarm limit system --enc=json | tee json &&
43
- jq -e .Conns < json &&
44
- jq -e .ConnsInbound < json &&
45
- jq -e .ConnsOutbound < json &&
46
- jq -e .FD < json &&
47
- jq -e .Memory < json &&
48
- jq -e .Streams < json &&
49
- jq -e .StreamsInbound < json &&
50
- jq -e .StreamsOutbound < json
56
+ ipfs swarm limit system --enc=json | tee json &&
57
+ jq -e .Conns < json &&
58
+ jq -e .ConnsInbound < json &&
59
+ jq -e .ConnsOutbound < json &&
60
+ jq -e .FD < json &&
61
+ jq -e .Memory < json &&
62
+ jq -e .Streams < json &&
63
+ jq -e .StreamsInbound < json &&
64
+ jq -e .StreamsOutbound < json
65
'
66
67
# every scope has the same fields, so we only inspect System
68
test_expect_success 'ResourceMgr enabled: swarm stats' '
55
- ipfs swarm stats all --enc=json | tee json &&
56
- jq -e .System.Memory < json &&
57
- jq -e .System.NumConnsInbound < json &&
58
- jq -e .System.NumConnsOutbound < json &&
59
- jq -e .System.NumFD < json &&
60
- jq -e .System.NumStreamsInbound < json &&
61
- jq -e .System.NumStreamsOutbound < json &&
62
- jq -e .Transient.Memory < json
69
+ ipfs swarm stats all --enc=json | tee json &&
70
+ jq -e .System.Memory < json &&
71
+ jq -e .System.NumConnsInbound < json &&
72
+ jq -e .System.NumConnsOutbound < json &&
73
+ jq -e .System.NumFD < json &&
74
+ jq -e .System.NumStreamsInbound < json &&
75
+ jq -e .System.NumStreamsOutbound < json &&
76
+ jq -e .Transient.Memory < json
77
'
78
79
+# shut down the daemon, set a limit in the config, and verify that it's applied
80
test_kill_ipfs_daemon
81
+
82
+test_expect_success "set system conn limit" "
83
+ ipfs config --json Swarm.ResourceMgr.Limits.System.Conns 99999
84
+"
85
+
86
+test_launch_ipfs_daemon
87
+
88
+test_expect_success 'ResourceMgr enabled: swarm limit' '
89
+ ipfs swarm limit system --enc=json | tee json &&
90
+ jq -e ".Conns == 99999" < json
91
+'
92
+
93
+test_expect_success 'Set system memory limit while the daemon is running' '
94
+ ipfs swarm limit system | jq ".Memory = 99998" > system.json &&
95
+ ipfs swarm limit system system.json
96
+'
97
+
98
+test_expect_success 'The new system limits were written to the config' '
99
+ jq -e ".Swarm.ResourceMgr.Limits.System.Memory == 99998" < "$IPFS_PATH/config"
100
+'
101
+
102
+test_expect_success 'The new system limits are in the swarm limit output' '
103
+ ipfs swarm limit system --enc=json | jq -e ".Memory == 99998"
104
+'
105
+
106
+# now test all the other scopes
107
+test_expect_success 'Set limit on transient scope' '
108
+ ipfs swarm limit transient | jq ".Memory = 88888" > transient.json &&
109
+ ipfs swarm limit transient transient.json &&
110
+ jq -e ".Swarm.ResourceMgr.Limits.Transient.Memory == 88888" < "$IPFS_PATH/config" &&
111
+ ipfs swarm limit transient --enc=json | tee limits &&
112
+ jq -e ".Memory == 88888" < limits
113
+'
114
+
115
+test_expect_success 'Set limit on service scope' '
116
+ ipfs swarm limit svc:foo | jq ".Memory = 77777" > service-foo.json &&
117
+ ipfs swarm limit svc:foo service-foo.json --enc=json &&
118
+ jq -e ".Swarm.ResourceMgr.Limits.Service.foo.Memory == 77777" < "$IPFS_PATH/config" &&
119
+ ipfs swarm limit svc:foo --enc=json | tee limits &&
120
+ jq -e ".Memory == 77777" < limits
121
+'
122
+
123
+test_expect_success 'Set limit on protocol scope' '
124
+ ipfs swarm limit proto:foo | jq ".Memory = 66666" > proto-foo.json &&
125
+ ipfs swarm limit proto:foo proto-foo.json --enc=json &&
126
+ jq -e ".Swarm.ResourceMgr.Limits.Protocol.foo.Memory == 66666" < "$IPFS_PATH/config" &&
127
+ ipfs swarm limit proto:foo --enc=json | tee limits &&
128
+ jq -e ".Memory == 66666" < limits
129
+'
130
+
131
+# any valid peer id
132
+PEER_ID=QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN
133
+
134
+test_expect_success 'Set limit on peer scope' '
135
+ ipfs swarm limit peer:$PEER_ID | jq ".Memory = 66666" > peer-$PEER_ID.json &&
136
+ ipfs swarm limit peer:$PEER_ID peer-$PEER_ID.json --enc=json &&
137
+ jq -e ".Swarm.ResourceMgr.Limits.Peer.${PEER_ID}.Memory == 66666" < "$IPFS_PATH/config" &&
138
+ ipfs swarm limit peer:$PEER_ID --enc=json | tee limits &&
139
+ jq -e ".Memory == 66666" < limits
140
+'
141
+
142
+test_expect_success 'Get limit for peer scope with an invalid peer ID' '
143
+ test_expect_code 1 ipfs swarm limit peer:foo 2> actual &&
144
+ test_should_contain "invalid peer ID" actual
145
+'
146
+
147
+test_expect_success 'Set limit for peer scope with an invalid peer ID' '
148
+ echo "{\"Memory\": 99}" > invalid-peer-id.json &&
149
+ test_expect_code 1 ipfs swarm limit peer:foo invalid-peer-id.json 2> actual &&
150
+ test_should_contain "invalid peer ID" actual
151
+'
152
+
153
+test_kill_ipfs_daemon
154
+
155
test_done