pytest-dev / pytest-testinfra

Testinfra test your infrastructures
https://testinfra.readthedocs.io
Apache License 2.0
2.37k stars 355 forks source link

Using Docker backend on a remote Docker host/container #408

Open Kilo59 opened 5 years ago

Kilo59 commented 5 years ago

I'd like to be able to use the testinfra convenience methods provided by the Docker backend on a REMOTE Docker container.

Is this possible?

I know that I could use ssh or paramiko connection to connect to the remote Docker host but I would have to "manually" docker exec into my container. I would not be able to use the testinfra convenience methods (.file, .package etc.) on the container itself.

ecks commented 5 years ago

Hi @Kilo59,

From what I understand you have a docker.service running on a different machine from your docker client? How do you normally execute on your Remote docker container? You specify --host=://some_ip:port as part of your arguments?

If that is the case, take a look at https://github.com/philpep/testinfra/blob/master/testinfra/backend/docker.py#L31, all that's needed to be done is add the --host parameter if it exists.

Correct me if I'm wrong in my assumptions.

Kilo59 commented 5 years ago

@ecks Yes. The docker container I am interested in is not on the same machine as my testinfra test code.

Right now I use the paramiko testinfra backend and then use host2.run("docker exec my_container ...") to execute commands on remote host to get into the remote container.

|Host 1|  ---paramiko_backend--->  |Host 2|
  |                                   |
testinfra_test                    my_container

But the unit/system I'm testing is the container (not the container host), so it would be much more convenient if I could somehow chain the docker backend ontop of the paramiko/ssh connection.

|Host 1|                               |Host 2|
  |                                      |
testinfra_test  ---docker_backend--->  my_container

all that's needed to be done is add the --host parameter if it exists.

I don't quite understand what you mean by this. Are you saying I can do something like this?...

py.test --ssh-config=/path/to/ssh_config --hosts=://some_ip:port//docker://[user@]docker_id_or_name

Sorry for the confusion. I appreciate the help! 😄

ecks commented 5 years ago

Officially there is no support in testinfra for what you're trying to do, However there are a couple of ways you can approach it.

By default dockerd daemon listens on a unix socket which is only accessible from a local computer. However you can add -H parameter to expose it on a public port. Take a look at https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-host-port-or-a-unix-socket. If you are in a closed environment and are able to have exposed ports that would be the easiest option, you just need to tell the docker client which host and port to connect to and open up an entry in your firewall if you have one. In that case you are bypassing the ssh connection and testinfa is talking directly to host_2.

If you have docker >= 18.09, it looks like you can access the dockerd daemon directly over ssh, which as far as I understand is exactly what you're trying to do. What you would need to do is add -H ssh://user@host_2 to your docker command, or better yet, define DOCKER_HOST to ssh://user@host_2. Will also need to setup passwordless login. Take a look at https://medium.com/lucjuggery/docker-tips-access-the-docker-daemon-via-ssh-97cd6b44a53.

Whichever option you decide on, I believe if you just export DOCKER_HOST in your terminal (through .bashrc or equivalent), you should be good to go since the docker backend uses shell=True when it executes commands, so all your shell variables should be evaluated. Let me know if that works for you.

philpep commented 5 years ago

Hi, I think such feature is not so hard to have (just chaining testinfra "backends") and looks very useful (ssh+docker, ssh+ssh, ssh+kubernetes, ssh+salt etc). I wonder how to describe this in configuration and command line ? hosts are described as urls, so a non-url (or non url-encoded) separator could be used.

ecks commented 5 years ago

hmm yeah that sounds like it would be pretty useful. I could potentially work on a PR for that. I took a look at the source and looks like urlparse is used to parse the URI. So I don't think the symbol '+' is parsed, and if you do something like

urlparse("ssh+docker://user@host_2+user@docker_id")

in netloc the value would be user2@host1+user1@host2, which we can split on. What are your thoughts, does that URI look pretty readable?

philpep commented 5 years ago

Hi @ecks , I'll be happy to review a PR from you, thanks!

I think the + character can be used but I'd rather use it to separate the "whole" connection string, like:

ssh://user@host_2+docker://user@docker_id

Then we can just split on + and parse both connection strings. Then write some funny code to handle the "backend" chaining :)

ssbarnea commented 4 years ago

In fact molecule is able to deal with remove docker hosts without any problems by using docker-py instead of the docker-cli, which can also be missing from your host. The only thing you need to define to make it work is something like DOCKER_HOST=ssh://root@mydocker.example.com and it will work nicely.

Test infra needs the same behavior in order to still be relevand for using containers for testing because you cannot always install docker on the same platform.

static33rus commented 2 years ago

Any news? I also need this enhancement and I can't use workaround @ecks offered above, because my host_1 using Windows OS and I would like to connect to docker on host_2 (Linux). Host_1 is just a client without docker daemon installed on it. Of course, I can use docker API, but i would like to be able to use convenience methods (.file, .package etc.) on the container itself

ssbarnea commented 2 years ago

Just a hint, maybe sponsoring one of the project owners could persuade them to spend more of their personal time with the project. I kinda stopped using testinfra recently but I hope others may be able to help in one way or another.