Skip to content

Add integration tests #4

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

Merged
merged 20 commits into from
Mar 3, 2020
Merged

Add integration tests #4

merged 20 commits into from
Mar 3, 2020

Conversation

siegfried
Copy link
Contributor

The tests require a tun0 device with address 10.10.10.1/24 in the OS. On Debian, you can set it up like this:

# ip tuntap add dev tun0 mode tun
# ip address add 10.10.10.1/24 dev tun0
# ip link set tun0 up

Please let me know if this is acceptable.

Currently the two tests can only pass one because the Iface doesn't close the device file. Could it be a bug?

* Test payload
* Improve iface.send()
@siegfried siegfried requested a review from vorner January 31, 2020 11:46
@siegfried
Copy link
Contributor Author

siegfried commented Jan 31, 2020

@vorner Do you think it's better to configure the tun device in the test? I didn't do that because those commands should be run as root. And do you know how to configure the device in the CI environment?

@vorner
Copy link
Owner

vorner commented Jan 31, 2020

Sorry, I don't have the time to look at it right now. Hopefully over the weekend I might find some.

@siegfried
Copy link
Contributor Author

@vorner No problem. Take your time. Thanks.

Make the line shorter.
Copy link
Owner

@vorner vorner left a comment

Choose a reason for hiding this comment

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

You're right, the missing Drop implementation closing the file descriptor is a bug. Do you want to fix it, or should I?

As for the commands, I'd probably say that the test should do them and start without a pre-created tun. The Iface constructor creates a new tuntap device if it doesn't exist ‒ see the examples. Furthermore, you might want to allow the kernel to include a sequence number in the name (eg. testtun%d), so the tests running in parallel won't disrupt each other.

I think the CI will have to be configured to allow to run with privileged docker or in non-docker environment. I think I've seen some documentation in travis that allows such thing.

A little style thing: as these are more of integration tests than unit tests, would you move them into a separate file in the tests directory?

As for the etherparse crate, thanks for bringing it to my attention. I've usually used pnet-packet for such things, but this one seems more lightweight. I'll definitely want to have a look at it.

src/lib.rs Outdated
@@ -221,3 +221,10 @@ impl IntoRawFd for Iface {
self.fd.into_raw_fd()
}
}

impl Drop for Iface {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@vorner I tried to fixed it. But the compiler says:

error[E0509]: cannot move out of type `Iface`, which implements the `Drop` trait
   --> src/lib.rs:221:9
    |
221 |         self.fd.into_raw_fd()
    |         ^^^^^^^
    |         |
    |         cannot move out of here
    |         move occurs because `self.fd` has type `std::fs::File`, which does not implement the `Copy` trait

@siegfried
Copy link
Contributor Author

I think the CI will have to be configured to allow to run with privileged docker or in non-docker environment. I think I've seen some documentation in travis that allows such thing.

I couldn't create tuntap device in Docker. I'm running this on a VM.

@siegfried
Copy link
Contributor Author

As for the commands, I'd probably say that the test should do them and start without a pre-created tun. The Iface constructor creates a new tuntap device if it doesn't exist ‒ see the examples. Furthermore, you might want to allow the kernel to include a sequence number in the name (eg. testtun%d), so the tests running in parallel won't disrupt each other.

To create a TunTap device with Iface might not be a common operation. It assumes to be run as root, which could lead to more security issues. I think a better practice is to let root user assign the devices like ip tuntap add dev ${interface name} mode ${mode} user ${user} group ${group} to the users.
Normally you cannot test a device when you don't have it on your machine. I think this is the case.
Also, the iproute2 command line tools cannot work on other systems.

@siegfried
Copy link
Contributor Author

siegfried commented Feb 2, 2020

You're right, the missing Drop implementation closing the file descriptor is a bug. Do you want to fix it, or should I?

Sorry, it's not a bug. You just reminded me that the tests were run in parallels. With --test-threads=1 all tests passed. Might be better to avoid parallel tests on functions with side effects. And this crate won't be big anyway.

@siegfried siegfried changed the title [WIP] Add integration tests Add integration tests Feb 3, 2020
@vorner
Copy link
Owner

vorner commented Feb 7, 2020

Sorry, it's not a bug. You just reminded me that the tests were run in parallels.

Ah, I see. The fd is not int, but File, because I wanted to the File to take care of closing.

For the test, I agree that it makes sense to not make it work in parallel and run them sequentially. It would be nice, though, to be able to just do cargo test when developing (possibly by root) and not remember to write the parameter. I usually add a global mutex and the test lock it while running.

The same reason (running on random developer's computer) would still apply to generating the name from within the test, somehow dynamically as opposed to using hardcoded tun0 (and stealing some VPN running there by accident). Or at least use something else, like tun-test-1. Furthermore, it seems most programs that use some tun device set it up automatically ‒ eg. OpenVPN never asked me to set up the tun device for it. Some programs start as root and then drop the privileges.

I do find an argument that in practice you might want to use a pre-created one, nevertheless, these are tests and convenience of running (and cleanup) probably wins over security.

The tun in docker ‒ it should be possible, but you need to run the docker in privileged mode.

(Sorry for somewhat chaotic answer, I still didn't find enough time to think about it thoroughly)

@siegfried
Copy link
Contributor Author

For the test, I agree that it makes sense to not make it work in parallel and run them sequentially. It would be nice, though, to be able to just do cargo test when developing (possibly by root) and not remember to write the parameter. I usually add a global mutex and the test lock it while running.

How did you do that? Is a crate required? rust-lang/rust#43155

@vorner
Copy link
Owner

vorner commented Feb 8, 2020

How did you do that? Is a crate required? rust-lang/rust#43155

Yes, something like once-cell or lazy_static (I personally prefer the first, it has nicer API).

Copy link
Owner

@vorner vorner left a comment

Choose a reason for hiding this comment

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

Sorry for nitpicking that much and that it takes such a long time, but I kind of can't help it 😇. I'm starting to more or less like the current version, there are still some details.

Some slightly bigger topics:

  • I'm still not entirely happy about the manual setup of the tun, and that it's tun0 which might be actually used on a developer computer. I won't insist on tests setting it themselves, but at least:
    • Use some different name, please, one that's less likely to be taken. Maybe tun-test-0.
    • It would be nice if someone run this on their own computer (not on CI), they would get a hint/the right error message. As a suggestion, I'd say add another test case called system_setup_correctly that would check the correct tun exists and if not, it would error out with explaining message and hint what to do (or maybe at least reference a comment in the code how to set it up). I wouldn't fancy issues opened stating tests fail only to find out someone didn't set up environment for the test.
  • We'll need to somehow clean up the git history before merging. Let's leave it with adding commints during the review, but some kind of rewrite/rebase will have to happen. Will you want to do it yourself, or will you leave it up to me?

Cargo.toml Outdated
@@ -31,3 +31,4 @@ tokio-core = { version = "~0.1", optional = true }
[dev-dependencies]
version-sync = "~0.5"
etherparse = "0.9.0"
serial_test = "0.3.2"
Copy link
Owner

Choose a reason for hiding this comment

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

Hey, this is cool :-). Didn't know about that one.

I've noticed version 0.4 came out yesterday, maybe this could be used.

Anyway, looking at the dependencies, I know this is only a style point (valid for the etherparse above too):

  • I prefer to prefix them with the tilde (~). This is the same as no prefix, but it is cleaner in stating the version required is not exact, but only semver-compatible.
  • I omit the last digit that's not needed, to give the resolver as much freedom as possible. For etherparse, the 0 at the end is not needed for sure. Do you need some specific changes/bugfixes in serial_test 0.3.2, or any 0.3 would do? (or 0.4)?

assert_eq!(_ip_header.source, [10, 10, 10, 1]);
assert_eq!(_ip_header.destination, [10, 10, 10, 2]);
assert_eq!(_udp_header.source_port, 2424);
assert_eq!(_udp_header.destination_port, 4242);
Copy link
Owner

Choose a reason for hiding this comment

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

Looking at this again, why are the variable names with _? Using them in assert counts as use, so you shouldn't get an unused variable lint, and especially in tests, checking them is the intended use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I got compiler warnings for having variable names like ip_header. _ prefix was the compiler proposed. After upgraded to 1.40 the warnings were gone.

@siegfried
Copy link
Contributor Author

Sorry, I'm a little busy recently. I will make the changes when I'm available. Thanks for reviewing.

@siegfried
Copy link
Contributor Author

And I'm glad you are picky because I'm new in Rust, so don't worry about it.

@siegfried
Copy link
Contributor Author

siegfried commented Feb 24, 2020

Some slightly bigger topics:

  • I'm still not entirely happy about the manual setup of the tun, and that it's tun0 which might be actually used on a developer computer. I won't insist on tests setting it themselves, but at least:

    • Use some different name, please, one that's less likely to be taken. Maybe tun-test-0.
    • It would be nice if someone run this on their own computer (not on CI), they would get a hint/the right error message. As a suggestion, I'd say add another test case called system_setup_correctly that would check the correct tun exists and if not, it would error out with explaining message and hint what to do (or maybe at least reference a comment in the code how to set it up). I wouldn't fancy issues opened stating tests fail only to find out someone didn't set up environment for the test.

Come to think about it again, I still don't feel running sudo cargo test on the developing machine every time is a good idea (at least I don't want to do it on my machine), so I created two shell scripts to create and to destroy the test device, and add some descriptions in README. Please let me know how you think of it. I can also make this more automatic by wrapping cargo test in a Makefile.

  • We'll need to somehow clean up the git history before merging. Let's leave it with adding commints during the review, but some kind of rewrite/rebase will have to happen. Will you want to do it yourself, or will you leave it up to me?

Is it possible to squash all the commits into one when you merge it? (I thought I saw this feature somewhere) Otherwise I can rebase it if you are not available.

@siegfried siegfried requested a review from vorner February 24, 2020 04:53
Copy link
Owner

@vorner vorner left a comment

Choose a reason for hiding this comment

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

Is it possible to squash all the commits into one when you merge it?

Yes, that's a possibility. It couldn't be used if you wanted to preserve multiple commits with related changes but somewhat separate, but I think in this case one commit is good.

Should I use the commit message of the first one?

Anyway, I'm happy with the current version. There's this one language correction which I might as well do myself when merging it. So, I'll just wait for you to confirm that you're OK with the current state too and then go ahead.


sudo ip tuntap add dev tun10 mode tun user $(whoami)
sudo ip address add 10.10.10.1/24 dev tun10
sudo ip link set tun10 up
Copy link
Owner

Choose a reason for hiding this comment

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

This requires sudo to be available, as opposed to some other ways of setting this up (eg. running it directly as root). But I guess it's OK to live with it this way; people with really unusual systems might at least use the script as an inspiration.

@siegfried
Copy link
Contributor Author

I think it's ok to use the message of the first commit. Thanks.

@vorner vorner merged commit e150911 into vorner:master Mar 3, 2020
@vorner
Copy link
Owner

vorner commented Mar 3, 2020

Thank you. And sorry it took so long.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants