Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interface'ifying things #61

Merged
merged 7 commits into from
Aug 15, 2018
Merged

interface'ifying things #61

merged 7 commits into from
Aug 15, 2018

Conversation

frrist
Copy link
Member

@frrist frrist commented Jul 16, 2018

What's in the PR?

  • Core interface changes
  • Plugin system (localipfs & dockeripfs)
    • The plugin implementation is baebones, improvements to the *ipfs plugins are desirable.
  • Reworked CLI
  • Increased Test Coverage

main.go Outdated
killCmd,
restartCmd,
setCmd,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is set going?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully nowhere. A lot of code is being stripped to just limit the total amount of code that needs to be rewritten to get things working. Both setCmd and getCmd will be coming back in, as well as the flags I stripped from init in some way.

With the latest updates though the IPFS local plugin actually works. Some code massaging needs to take place as a few things are a bit ruff around the edges.

  • Proper plugin loading
  • Better command output handling

@whyrusleeping
Copy link
Member

@travisperson could you take a look at incorporating these changes too while youre at it? 08f0993

It separates the init process into two steps (optionally) then makes the normal init process explicitly do both. This will make certain setups easier to play with in the future.

@travisperson
Copy link
Member

@whyrusleeping ah nice. Ya should be easy to incorporate.

testbed/spec.go Outdated
}

plugs, err := ioutil.ReadDir(dir)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: don't put spaces between creating and checking an error

testbed/spec.go Outdated

PluginName := *(PluginNameSym.(*string))

plugins[PluginName] = iptbplugin{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably want to check if a plugin by this name exists already

@whyrusleeping
Copy link
Member

We should make sure its possible to build iptb by default with support for some 'default plugins'. I want to not have to worry about where my ipfs plugin is located

@travisperson
Copy link
Member

Building in some plugins is a great idea, I updated the current "plugins" to get built into the binary and allow for them to be overridden.

Also exposed them as a plugin package for easy hacking / building your own 🔨

return []string{attrId, attrPath, attrBwIn, attrBwOut}
}

func GetAttrDesc(attr string) (string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicate code?

type GetAttrDescFunc func(attr string) (string, error)

// TestbedNode specifies the interface to a process controlled by iptb
type TestbedNode interface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This interface is getting pretty big, I wonder if we should break it up, and make some of the methods optional (by specialization, or similar)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return types and parameters are a little off, but a rough idea:
https://gist.github.com/frrist/01405d6b36aae85108fad80c2c40d3fb

)

func init() {
NewNode = func(dir string, extras map[string]interface{}) (testbedi.TestbedNode, error) {
Copy link
Member

@whyrusleeping whyrusleeping Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to assign these like this, it should work the same if you just define it as:

func NewNode(dir string, extras map[string]interface{}) (testbedi.TestbedNode, error) {
...
...
}

main.go Outdated
"fmt"
"io"
"net/http"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: whitespace

main.go Outdated
Action: func(c *cli.Context) error {
if c.Int("count") == 0 {
fmt.Printf("please specify number of nodes: '%s init -n 10'\n", os.Args[0])
os.Exit(1)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets return errors for these and have the exit here

return nil
}

err = os.RemoveAll(dir)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can just return os.RemoveALl(dir) here


for i := 0; i < count; i++ {
dir := path.Join(base, fmt.Sprint(i))
err := os.MkdirAll(dir, 0775)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: combine with if

return nil, err
}

var spec *NodeSpec
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I don't think this is necessary

main.go Outdated
Usage: "stream events from a given node",
Action: func(c *cli.Context) error {
if len(c.Args()) != 1 {
return fmt.Errorf("'iptb logs' accepts at exactly 1 argument")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iptb logs events

main.go Outdated
@@ -598,3 +822,34 @@ var logsCmd = cli.Command{
return nil
},
}

var eventsCmd = cli.Command{
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this!

return nil, fmt.Errorf("error getting env: %s", err)
}

cmd := exec.Command(l.repobuilder, "init")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can use exec.CommandContext here

defer wait.Done()
_, err := nd.Init(context.TODO())
if err != nil {
panic(err)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should try and avoid panics, could use an error channel here. We could also just do this in a loop without a go-routine.

main.go Outdated
if err != nil {
return err
}

out, err := nd.RunCmd(c.Args()[1:]...)
out, err := nd.RunCmd(context.TODO(), c.Args()[1:]...)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can pass a context with a timeout here

return fmt.Errorf("node is already running")
}

cmd := exec.Command("docker", "run", "-d", "-v", l.dir+":/data/ipfs", l.image)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting future work here would be to use the docker api package: https://docs.docker.com/develop/sdk/#sdk-and-api-quickstart

@whyrusleeping
Copy link
Member

Before merging, let's make sure that the tests all pass, and that if we use this in the go-ipfs sharness tests, they all still pass

@travisperson travisperson force-pushed the feat/interface-plugins branch 2 times, most recently from 289baf9 to fd43b32 Compare July 26, 2018 19:51
commands/attr.go Outdated

tb := testbed.NewTestbed(path.Join(flagRoot, "benches", flagTestbed))

if flagSave {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we first attempt to set the attribute on the node before persisting it to the nodespec file?

commands/fast.go Outdated
}

runCmd := func(node testbedi.Core) (testbedi.TBOutput, error) {
return node.Init(context.TODO(), ([]string{})...)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can remove ([]string{})...

},
},
Before: func(c *cli.Context) error {
if present := isTerminatorPresent(c); present {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's leave a comment describing what terminator means

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment on isTerminatorPresent as this is used on any command which takes a node range.

results[i] = Result{
Node: n,
Output: out,
Error: errors.Wrapf(err, "node[%d]", n),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this this be incorrect if list is a range like [4-8]?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep!

@travisperson travisperson force-pushed the feat/interface-plugins branch 3 times, most recently from e82eb61 to 451df6b Compare August 1, 2018 00:59
@frrist frrist changed the title [WIP] interface'ifying things interface'ifying things Aug 1, 2018
@frrist frrist added the kind/enhancement A net-new feature or improvement to an existing feature label Aug 1, 2018
@travisperson
Copy link
Member

To make sure everything was working correctly I did a quick update to go-ipfs sharness tests as well using the new cli.

You can see those changes here https://github.com/ipfs/go-ipfs/compare/master...travisperson:update-iptb?expand=1

Copy link
Member

@Stebalien Stebalien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly random suggestions and interface comments. Overall, this looks like a reasonable design.

README.md Outdated
dump-stack get a stack dump from the given daemon
help, h show a list of subcommands, or help for a specific subcommand
fast create default bench and initialize
bench create, remove, list bench setups
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"manage test (?) bench setups". I assume these aren't benchmarks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya, not benchmarks, just different setups. I'll try to make that clear. There is some discussion around the introduction of bench in issue #62. Basically this stemmed from having both a init and a create command, and trying to make it less ambiguous as to which to run.

The thought was to place the concept of "create" under a category, and a suggestion was made to call them benches, like having a toolbench where you would work on something.

In the current implementation init no longer makes the node specification. Instead it's a direct map to the Init method on the node interface. This allows iptb to have it's own configuration (defining number of nodes, types, etc), and still exposing the direct call to the interface.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bench is really unfortunate name, I think pretty much anything would be better, given its strong association with benchmarks

README.md Outdated
connect connect two nodes together
dump-stack get a stack dump from the given daemon
help, h show a list of subcommands, or help for a specific subcommand
fast create default bench and initialize
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"auto"? Or maybe just a iptb init --create[=default] --start flags? "fast" makes me think you want IPTB to run fast, or something.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now there is iptb bench create [other flags] --init. The fast command was added (name mostly a placeholder, though auto might be a good choice) to bring back the functionality of iptb init, as currently it maps directly to the node interface method Init. It would be nice to keep that exposed as a directly way to invoke that method without overloading it too much. This could also be fixed by changing out the Core#Init method name something else, and keeping init as the quick and easy way to get started with iptb.

Basically iptb fast is almost a direct mapping to iptb bench create --init, it just doesn't accept the idea of additional configuration through --attr flags.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for sth else than fast, I do like the idea of using default somehow

for _, f := range from {
for _, t := range to {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the timeout be applied to the entire command?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I'd also consider doing this in parallel anyways.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, but this can happen in a follow on PR

Category: "CORE",
Name: "connect",
Usage: "connect nodes together",
ArgsUsage: "<nodes> <nodes>",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also expect this command to take 1 arg (connecting all the nodes) and 0 args (connecting all nodes). Is there already another command for that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, nothing like that yet. Sounds like a good addition though 👍

TotalOut int
}

func GetBW(l testbedi.Libp2p) (*BW, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well take a context for all of these functions up-front (unless I'm missing something about how they're supposed to be used).

return NodesFromSpecs(specs)
}

func InitNodes(nodes []testbedi.Core, args ...string) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just pass in the context.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dead code actually, deleting...

testbed/spec.go Outdated
}

// SetAttr sets an attribute on the NodeSpec
func (ns *NodeSpec) SetAttr(attr string, val interface{}) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Attribute interface takes/returns a string value. I assume these should match.


/// Core Interface

func (l *Dockeripfs) Init(ctx context.Context, agrs ...string) (testbedi.Output, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/agrs/args.

return nil
}

func SwarmAddr(lt ListenType, addr string, port int) string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should really just use normal multiaddrs. This'll be a pain to maintain.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya, actually that is a good idea. I just changed it so that the plugin itself can take an apiaddr / swarmaddr, and the user can just throw in what ever they want.

They now just have defaults, and we can later be a little smarter and parse into and rewrite from the config to change individual values.

local
apiaddr => /ip4/127.0.0.1/tcp/0
swarmaddr => /ip4/127.0.0.1/tcp/0

local
apiaddr => /ip4/0.0.0.0/tcp/5001
swarmaddr => /ip4/0.0.0.0/tcp/4001

Note: we can also add profiles and such

Libp2p
// Allows a node to run any initialization it may require
// Ex: Installing additional dependencies / setting up configuration
Init(ctx context.Context, args ...string) (Output, error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that "args" are additional command line arguments, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct

Copy link
Member

@dignifiedquire dignifiedquire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing major, mostly small things that could make things a bit nicer.

One thing I noticed, while the interfaces are nicely documented a lot of the rest of the code doesn't have comments on methods, which makes it a bit harder to read & understand, than necessary.

Overall this is a big improvement 💯

README.md Outdated
@@ -1,22 +1,19 @@
# IPTB

`iptb` is a program used to create and manage a cluster of sandboxed IPFS nodes locally on your computer. Spin up 1000s of nodes! It exposes various options, such as different bootstrapping patterns. `iptb` makes testing IPFS networks easy!
`iptb` is a program used to create and manage a cluster of sandboxed IPFS nodes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the idea was to allow for other things than IPFS as well, so maybe make this description a bit more generic?

README.md Outdated
connect connect two nodes together
dump-stack get a stack dump from the given daemon
help, h show a list of subcommands, or help for a specific subcommand
fast create default bench and initialize
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for sth else than fast, I do like the idea of using default somehow

README.md Outdated
dump-stack get a stack dump from the given daemon
help, h show a list of subcommands, or help for a specific subcommand
fast create default bench and initialize
bench create, remove, list bench setups
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bench is really unfortunate name, I think pretty much anything would be better, given its strong association with benchmarks

README.md Outdated
```

### Install

`iptb` uses [gx](https://github.com/whyrusleeping/gx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make it such that it also works without gx, pretty please?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do that as long as we drop the ipfs plugins from being built into the binary by default due to its heavy use of gx. Currently the go-libp2p package needs to be versioned.

var ConnectCmd = cli.Command{
Category: "CORE",
Name: "connect",
Usage: "connect nodes together",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to have a little more text here, with some examples. I was really unclear on how this works, especially with ranges, until I saw a demo from @whyrusleeping

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I propose we address this in a follow on PR since similar changes will likely be required.

}

func (l *Localipfs) Shell(ctx context.Context, nodes []testbedi.Core) error {
shell := os.Getenv("SHELL")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice if these env variables are global constants, such that other plugins could reuse them

type Attribute interface {
Core
// GetAttr returns the value of attr
GetAttr(attr string) (string, error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is inconsistent, with both go best practices and the metric interface, and should be just Attr and SetAttr.

// Ex: Installing additional dependencies / setting up configuration
Init(ctx context.Context, args ...string) (Output, error)
// Starts the node
Start(ctx context.Context, wait bool, args ...string) (Output, error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing explanation of what wait does

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it needs to be removed actually. At the moment it doesn't serve much of a purpose. We can add it back if we find that it's actually needed for something.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I guess start has a purpose still. It can be to wait for the node to be reachable (api online). Stop is the one that doesn't really fit at the moment. It was added per a mention in #10, but I can't seem to think why it would be needed.

Shell(ctx context.Context, ns []Core) error

// Writes a log line to stdout
Infof(format string, args ...interface{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does a plugin need to implement this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking that this one could probably be left off.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya, I think we talked about this previously, it can be dropped. Original thought was to make it easier to use the interface in other applications (embedded iptb), and provide logging.

// Examples localipfs, dockeripfs, etc.
Type() string

String() string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is String supposed to do/where is it useful?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String is a builtin go interface. It defines how this object gets printed if someone passes it to a Printf type function

'

test_expect_success "iptb stop works" '
../bin/iptb stop
../bin/iptb stop && sleep 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could have iptb sit around and wait for the processes to be really dead

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at least make an issue for it, fine to fix later

Copy link
Member

@whyrusleeping whyrusleeping left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very large PR, i'm not going to be able to review in-depth, but i trust you two to do the right thing. Let's get this merged, and try to integrate things upwards, fixing any issues discovered along the way.

@travisperson
Copy link
Member

@Stebalien @dignifiedquire @whyrusleeping

Is there any other feedback that should block this PR? There is additional work I want to do to the plugins. I can remove that code from this PR, or simply follow up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/enhancement A net-new feature or improvement to an existing feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants