-
Notifications
You must be signed in to change notification settings - Fork 286
/
main.go
331 lines (283 loc) · 8.88 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// The ipfs-cluster-follow application.
package main
import (
"fmt"
"os"
"os/signal"
"os/user"
"path/filepath"
"syscall"
"github.com/ipfs-cluster/ipfs-cluster/api/rest/client"
"github.com/ipfs-cluster/ipfs-cluster/cmdutils"
"github.com/ipfs-cluster/ipfs-cluster/version"
"github.com/multiformats/go-multiaddr"
"github.com/pkg/errors"
semver "github.com/blang/semver"
cli "github.com/urfave/cli/v2"
)
const (
// ProgramName of this application
programName = "ipfs-cluster-follow"
clusterNameFlag = "clusterName"
logLevel = "info"
)
// Default location for the configurations and data
var (
// DefaultFolder is the name of the cluster folder
DefaultFolder = ".ipfs-cluster-follow"
// DefaultPath is set on init() to $HOME/DefaultFolder
// and holds all the ipfs-cluster data
DefaultPath string
// The name of the configuration file inside DefaultPath
DefaultConfigFile = "service.json"
// The name of the identity file inside DefaultPath
DefaultIdentityFile = "identity.json"
DefaultGateway = "127.0.0.1:8080"
)
var (
commit string
configPath string
identityPath string
signalChan = make(chan os.Signal, 20)
)
// Description provides a short summary of the functionality of this tool
var Description = fmt.Sprintf(`
%s helps running IPFS Cluster follower peers.
Follower peers subscribe to a Cluster controlled by a set of "trusted
peers". They collaborate in pinning items as dictated by the trusted peers and
do not have the power to make Cluster-wide modifications to the pinset.
Follower peers cannot access information nor trigger actions in other peers.
%s can be used to follow different clusters by launching it
with different options. Each Cluster has an identity, a configuration
and a datastore associated to it, which are kept under
"~/%s/<cluster_name>".
For feedback, bug reports or any additional information, visit
https://github.com/ipfs-cluster/ipfs-cluster.
EXAMPLES:
List configured follower peers:
$ %s
Display information for a follower peer:
$ %s <clusterName> info
Initialize a follower peer:
$ %s <clusterName> init <example.url>
Launch a follower peer (will stay running):
$ %s <clusterName> run
List items in the pinset for a given cluster:
$ %s <clusterName> list
Getting help and usage info:
$ %s --help
$ %s <clusterName> --help
$ %s <clusterName> info --help
$ %s <clusterName> init --help
$ %s <clusterName> run --help
$ %s <clusterName> list --help
`,
programName,
programName,
DefaultFolder,
programName,
programName,
programName,
programName,
programName,
programName,
programName,
programName,
programName,
programName,
programName,
)
func init() {
// Set build information.
if build, err := semver.NewBuildVersion(commit); err == nil {
version.Version.Build = []string{"git" + build}
}
// We try guessing user's home from the HOME variable. This
// allows HOME hacks for things like Snapcraft builds. HOME
// should be set in all UNIX by the OS. Alternatively, we fall back to
// usr.HomeDir (which should work on Windows etc.).
home := os.Getenv("HOME")
if home == "" {
usr, err := user.Current()
if err != nil {
panic(fmt.Sprintf("cannot get current user: %s", err))
}
home = usr.HomeDir
}
DefaultPath = filepath.Join(home, DefaultFolder)
// This will abort the program on signal. We close the signal channel
// when launching the peer so that we can do an orderly shutdown in
// that case though.
go func() {
signal.Notify(
signalChan,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGHUP,
)
_, ok := <-signalChan // channel closed.
if !ok {
return
}
os.Exit(1)
}()
}
func main() {
app := cli.NewApp()
app.Name = programName
app.Usage = "IPFS Cluster Follower"
app.UsageText = fmt.Sprintf("%s [global options] <clusterName> [subcommand]...", programName)
app.Description = Description
//app.Copyright = "© Protocol Labs, Inc."
app.Version = version.Version.String()
app.Flags = []cli.Flag{
&cli.StringFlag{
Name: "config, c",
Value: DefaultPath,
Usage: "path to the followers configuration and data `FOLDER`",
EnvVars: []string{"IPFS_CLUSTER_PATH"},
},
}
app.Action = func(c *cli.Context) error {
if !c.Args().Present() {
return listClustersCmd(c)
}
clusterName := c.Args().Get(0)
clusterApp := cli.NewApp()
clusterApp.Name = fmt.Sprintf("%s %s", programName, clusterName)
clusterApp.HelpName = clusterApp.Name
clusterApp.Usage = fmt.Sprintf("Follower peer management for \"%s\"", clusterName)
clusterApp.UsageText = fmt.Sprintf("%s %s [subcommand]", programName, clusterName)
clusterApp.Action = infoCmd
clusterApp.HideVersion = true
clusterApp.Flags = []cli.Flag{
&cli.StringFlag{ // pass clusterName to subcommands
Name: clusterNameFlag,
Value: clusterName,
Hidden: true,
},
}
clusterApp.Commands = []*cli.Command{
{
Name: "info",
Usage: "displays information for this peer",
ArgsUsage: "",
Description: fmt.Sprintf(`
This command display useful information for "%s"'s follower peer.
`, clusterName),
Action: infoCmd,
},
{
Name: "init",
Usage: "initializes the follower peer",
ArgsUsage: "<template_URL>",
Description: fmt.Sprintf(`
This command initializes a follower peer for the cluster named "%s". You
will need to pass the peer configuration URL. The command will generate a new
peer identity and leave things ready to run "%s %s run".
An error will be returned if a configuration folder for a cluster peer with
this name already exists. If you wish to re-initialize from scratch, delete
this folder first.
`, clusterName, programName, clusterName),
Action: initCmd,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "gateway",
Value: DefaultGateway,
Usage: "gateway URL",
EnvVars: []string{"IPFS_GATEWAY"},
Hidden: true,
},
},
},
{
Name: "run",
Usage: "runs the follower peer",
ArgsUsage: "",
Description: fmt.Sprintf(`
This commands runs a "%s" cluster follower peer. The peer should have already
been initialized with "init" alternatively the --init flag needs to be
passed.
Before running, ensure that you have connectivity and that the IPFS daemon is
running.
You can obtain more information about this follower peer by running
"%s %s" (without any arguments).
The peer will stay running in the foreground until manually stopped.
`, clusterName, programName, clusterName),
Action: runCmd,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "init",
Usage: "initialize cluster peer with the given URL before running",
},
&cli.StringFlag{
Name: "gateway",
Value: DefaultGateway,
Usage: "gateway URL",
EnvVars: []string{"IPFS_GATEWAY"},
Hidden: true,
},
},
},
{
Name: "list",
Usage: "list items in the peers' pinset",
ArgsUsage: "",
Description: `
This commands lists all the items pinned by this follower cluster peer on IPFS.
If the peer is currently running, it will display status information for each
pin (such as PINNING). If not, it will just display the current list of pins
as obtained from the internal state on disk.
`,
Action: listCmd,
},
}
return clusterApp.RunAsSubcommand(c)
}
app.Run(os.Args)
}
// build paths returns the path to the configuration folder,
// the identity.json and the service.json files.
func buildPaths(c *cli.Context, clusterName string) (string, string, string) {
absPath, err := filepath.Abs(c.String("config"))
if err != nil {
cmdutils.ErrorOut("error getting absolute path for %s: %s", err, clusterName)
os.Exit(1)
}
// ~/.ipfs-cluster-follow/clusterName
absPath = filepath.Join(absPath, clusterName)
// ~/.ipfs-cluster-follow/clusterName/service.json
configPath = filepath.Join(absPath, DefaultConfigFile)
// ~/.ipfs-cluster-follow/clusterName/indentity.json
identityPath = filepath.Join(absPath, DefaultIdentityFile)
return absPath, configPath, identityPath
}
func socketAddress(absPath, clusterName string) (multiaddr.Multiaddr, error) {
socket := fmt.Sprintf("/unix/%s", filepath.Join(absPath, "api-socket"))
ma, err := multiaddr.NewMultiaddr(socket)
if err != nil {
return nil, errors.Wrapf(err, "error parsing socket: %s", socket)
}
return ma, nil
}
// returns an REST API client. Points to the socket address unless
// CLUSTER_RESTAPI_HTTPLISTENMULTIADDRESS is set, in which case it uses it.
func getClient(absPath, clusterName string) (client.Client, error) {
var endp multiaddr.Multiaddr
var err error
if endpStr := os.Getenv("CLUSTER_RESTAPI_HTTPLISTENMULTIADDRESS"); endpStr != "" {
endp, err = multiaddr.NewMultiaddr(endpStr)
if err != nil {
return nil, errors.Wrapf(err, "error parsing the value of CLUSTER_RESTAPI_HTTPLISTENMULTIADDRESS: %s", endpStr)
}
} else {
endp, err = socketAddress(absPath, clusterName)
}
if err != nil {
return nil, err
}
cfg := client.Config{
APIAddr: endp,
}
return client.NewDefaultClient(&cfg)
}