need for docker_facts module

Hi all!

I found an issue with the docker module that I ended up discussing with Paul Durivage. The essential problem is that there is a list of dictionaries per container that only contains the last running container when using “with_sequence” or “with_items” versus “count”, hence making it impossible to utilize information for the entirety of a running fleet of docker containers outside of a task using the docker module.

An idea to deal with this short-coming would be to have a “docker_facts” module for obtaining information from the fleet of Docker containers.

This issue can be better explained with a little code if my short description doesn’t.

The documentation(http://docs.ansible.com/docker_module.html#options) has several examples on how to use the docker module that I tried out. I wanted to get familiar with the basics of this module. The first thing of course was that I wanted to launch simply 4 instances and return some simple information as to what the IPs were:

  • hosts: localhost
    vars:
    start_containers_count: 4
    tasks:
  • name: run docker containers
    docker: image=df02bd73464a name=ssh{{ item }}
    with_sequence: count={{ start_containers_count }}
  • name: print debug
    debug: msg=“docker_containers[{{item}}][‘NetworkSettings’][‘IPAddress’]}}”
    with_items: docker_containers
    with_sequence: count={{ start_containers_count }}

Upon running this, the containers were launched just fine, but the debug didn’t output what I expected it to. For starters, there was only one debug line. Also, it appeared there was only information for the last launched container (172.17.0.5). This made me realize something was up.

TASK: [print debug] ***********************************************************
ESTABLISH CONNECTION FOR USER: patg
ok: [localhost] => (item={u’HostsPath’: u’/var/lib/docker/containers/73b860f98c60a9d03d693643895ef2b5aa2dce7a40c2ebf6f4bc3433cd800921/hosts’, u’Created’: u’2014-04-18T21:43:08.968061193Z’, u’Image’: u’df02bd73464ae121ac3db7788e042d52805c9283b6174511dbf1a6b821ef20c2’, u’Args’: , u’Driver’: u’aufs’, u’HostConfig’: {u’Binds’: None, u’Dns’: None, u’ContainerIDFile’: u’‘, u’PublishAllPorts’: False, u’Links’: None, u’LxcConf’: None, u’PortBindings’: None, u’DnsSearch’: None, u’Privileged’: False, u’VolumesFrom’: None}, u’Id’: u’73b860f98c60a9d03d693643895ef2b5aa2dce7a40c2ebf6f4bc3433cd800921’, u’State’: {u’Ghost’: False, u’Pid’: 20113, u’Running’: True, u’FinishedAt’: u’0001-01-01T00:00:00Z’, u’StartedAt’: u’2014-04-18T21:43:08.989599463Z’, u’ExitCode’: 0}, u’ExecDriver’: u’native-0.1’, u’ResolvConfPath’: u’/var/lib/docker/containers/73b860f98c60a9d03d693643895ef2b5aa2dce7a40c2ebf6f4bc3433cd800921/resolv.conf’, u’Volumes’: {}, u’Path’: u’/usr/local/sbin/sshd.sh’, u’HostnamePath’: u’/var/lib/docker/containers/73b860f98c60a9d03d693643895ef2b5aa2dce7a40c2ebf6f4bc3433cd800921/hostname’, u’Config’: {u’Env’: [u’HOME=/‘, u’PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin’], u’Hostname’: u’73b860f98c60’, u’Entrypoint’: [u’/usr/local/sbin/sshd.sh’], u’PortSpecs’: None, u’Memory’: 0, u’OnBuild’: None, u’OpenStdin’: False, u’User’: u’‘, u’CpuShares’: 0, u’AttachStdout’: False, u’NetworkDisabled’: False, u’WorkingDir’: u’‘, u’Cmd’: None, u’StdinOnce’: False, u’AttachStdin’: False, u’Volumes’: None, u’MemorySwap’: 0, u’Tty’: False, u’AttachStderr’: False, u’Domainname’: u’‘, u’Image’: u’df02bd73464a’, u’ExposedPorts’: {u’22/tcp’: {}}}, u’VolumesRW’: {}, u’NetworkSettings’: {u’Bridge’: u’docker0’, u’PortMapping’: None, u’Gateway’: u’172.17.42.1’, u’IPPrefixLen’: 16, u’IPAddress’: u’172.17.0.5’, u’Ports’: {u’22/tcp’: None}}, u’Name’: u’/ssh4’}) => {
“item”: {
“Args”: ,
“Config”: {
“AttachStderr”: false,
“AttachStdin”: false,
“AttachStdout”: false,
“Cmd”: null,
“CpuShares”: 0,
“Domainname”: “”,
“Entrypoint”: [
“/usr/local/sbin/sshd.sh”
],
“Env”: [
“HOME=/”,
“PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin”
],
“ExposedPorts”: {
“22/tcp”: {}
},
“Hostname”: “73b860f98c60”,
“Image”: “df02bd73464a”,
“Memory”: 0,
“MemorySwap”: 0,
“NetworkDisabled”: false,
“OnBuild”: null,
“OpenStdin”: false,
“PortSpecs”: null,
“StdinOnce”: false,
“Tty”: false,
“User”: “”,
“Volumes”: null,
“WorkingDir”: “”
},
“Created”: “2014-04-18T21:43:08.968061193Z”,
“Driver”: “aufs”,
“ExecDriver”: “native-0.1”,
“HostConfig”: {
“Binds”: null,
“ContainerIDFile”: “”,
“Dns”: null,
“DnsSearch”: null,
“Links”: null,
“LxcConf”: null,
“PortBindings”: null,
“Privileged”: false,
“PublishAllPorts”: false,
“VolumesFrom”: null
},
“HostnamePath”: “/var/lib/docker/containers/73b860f98c60a9d03d693643895ef2b5aa2dce7a40c2ebf6f4bc3433cd800921/hostname”,
“HostsPath”: “/var/lib/docker/containers/73b860f98c60a9d03d693643895ef2b5aa2dce7a40c2ebf6f4bc3433cd800921/hosts”,
“Id”: “73b860f98c60a9d03d693643895ef2b5aa2dce7a40c2ebf6f4bc3433cd800921”,
“Image”: “df02bd73464ae121ac3db7788e042d52805c9283b6174511dbf1a6b821ef20c2”,
“Name”: “/ssh4”,
“NetworkSettings”: {
“Bridge”: “docker0”,
“Gateway”: “172.17.42.1”,
“IPAddress”: “172.17.0.5”,
“IPPrefixLen”: 16,
“PortMapping”: null,
“Ports”: {
“22/tcp”: null
}
},
“Path”: “/usr/local/sbin/sshd.sh”,
“ResolvConfPath”: “/var/lib/docker/containers/73b860f98c60a9d03d693643895ef2b5aa2dce7a40c2ebf6f4bc3433cd800921/resolv.conf”,
“State”: {
“ExitCode”: 0,
“FinishedAt”: “0001-01-01T00:00:00Z”,
“Ghost”: false,
“Pid”: 20113,
“Running”: true,
“StartedAt”: “2014-04-18T21:43:08.989599463Z”
},
“Volumes”: {},
“VolumesRW”: {}
},
“msg”: “172.17.0.5”
}

I decided to print out the docker_containers list (which each member is a dictionary representing each container in the fleet) and use an explicit loop for all containers and noticed it was the same dictionary every time, containing the last container launched:

  • name: print debug
    debug: msg=“{{docker_containers}}”
    with_sequence: count={{ start_containers_count }}

Produced, 4 times:

ok: [localhost] => (item=4) => {
“item”: “4”,
“msg”: “[{u’HostsPath’: u’/var/lib/docker/containers/6359a341981a818294e5866b3e2a8b51360bfab40e8811ae826c1c3b2eae3e6c/hosts’, u’Created’: u’2014-04-19T12:56:34.173476164Z’, u’Image’: u’df02bd73464ae121ac3db7788e042d52805c9283b6174511dbf1a6b821ef20c2’, u’Args’: , u’Driver’: u’aufs’, u’HostConfig’: {u’PortBindings’: None, u’Links’: None, u’LxcConf’: None, u’ContainerIDFile’: u’‘, u’Binds’: None, u’PublishAllPorts’: False, u’Dns’: None, u’DnsSearch’: None, u’Privileged’: False, u’VolumesFrom’: None}, u’VolumesRW’: {}, u’State’: {u’Ghost’: False, u’Pid’: 25329, u’Running’: True, u’FinishedAt’: u’0001-01-01T00:00:00Z’, u’StartedAt’: u’2014-04-19T12:56:34.198602546Z’, u’ExitCode’: 0}, u’ExecDriver’: u’native-0.1’, u’ResolvConfPath’: u’/var/lib/docker/containers/6359a341981a818294e5866b3e2a8b51360bfab40e8811ae826c1c3b2eae3e6c/resolv.conf’, u’Volumes’: {}, u’Path’: u’/usr/local/sbin/sshd.sh’, u’HostnamePath’: u’/var/lib/docker/containers/6359a341981a818294e5866b3e2a8b51360bfab40e8811ae826c1c3b2eae3e6c/hostname’, u’Config’: {u’Env’: [u’HOME=/‘, u’PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin’], u’Hostname’: u’6359a341981a’, u’Entrypoint’: [u’/usr/local/sbin/sshd.sh’], u’PortSpecs’: None, u’Memory’: 0, u’OnBuild’: None, u’OpenStdin’: False, u’User’: u’‘, u’AttachStderr’: False, u’AttachStdout’: False, u’NetworkDisabled’: False, u’WorkingDir’: u’‘, u’Cmd’: None, u’StdinOnce’: False, u’AttachStdin’: False, u’Volumes’: None, u’MemorySwap’: 0, u’Tty’: False, u’CpuShares’: 0, u’Domainname’: u’‘, u’Image’: u’df02bd73464a’, u’ExposedPorts’: {u’22/tcp’: {}}}, u’Id’: u’6359a341981a818294e5866b3e2a8b51360bfab40e8811ae826c1c3b2eae3e6c’, u’NetworkSettings’: {u’Bridge’: u’docker0’, u’PortMapping’: None, u’Gateway’: u’172.17.42.1’, u’IPPrefixLen’: 16, u’IPAddress’: u’172.17.0.5’, u’Ports’: {u’22/tcp’: None}}, u’Name’: u’/ssh4’}]”
}

Now, if i use “count”, as in the following:

  • hosts: localhost
    vars:
    start_containers_count: 4
    tasks:
  • name: run docker containers
    docker: image=df02bd73464a count={{ start_containers_count }}
  • name: print debug
    debug: msg=“{{item[‘NetworkSettings’][‘IPAddress’]}}”
    with_items: docker_containers

This worked as I wanted it to in the beginning, except that when using “count”, I cannot name the containers as I did in the above examples because you cannot use “count” and “name” together.

The reason for this is that when “count” is used, _ansible_facts() is only called once and obtains a dictionary with members representing dictionaries for each container, whereas if “with_sequence” is used, _ansible_facts() is called for each iteration of the loop (obvious in retrospect).

What this results in is “docker_container” having the last launched container entry.

Which brings me back to the initial idea from Paul – to have a “docker_facts” module that would make is possible to obtain any information a user needs about the fleet of containers. This would make it possible to loop through all the running containers regardless of whether iterative strategy I want.

Anyhow - that is the idea!

Regards,

Patrick

Slight correction:

The reason for this is that when “count” is used, _ansible_facts() is only called once and obtains a dictionary with members representing dictionaries for each container

Should be:

The reason for this is that when “count” is used, _ansible_facts() is only called once and obtains a list with members representing dictionaries for each container

I quite agree here too. You conversation with Paul sparked a conversation at work with Paul and I.

I think we are in agreement that the state of the docker modules in ansible is pretty poor at the moment.

The modules don’t work the way other modules work such as returning ansible_facts instead of a regular key that could easily be used with register (and the fact that it doesn’t return the info you actually want)

Additionally, in devel, the modules are unusable due to us accepting a pull request for functionality that hadn’t even been accepted by docker-py yet:

https://github.com/ansible/ansible/pull/6956
https://github.com/ansible/ansible/pull/6991
https://github.com/dotcloud/docker-py/pull/200

docker and docker_images are also both broken with newer versions of docker-py due to pull() returning empty strings with stream=True.

I believe we need someone to take over ownership of these modules and rewrite them. I don’t think this can be done in a backwards compatible way, but I do believe Michael mentioned that due to things changing in docker so quickly anyway that he was ok with making backwards incompatible changes in these modules.

Things that I think need to happen:

  1. Rewrite the docker module to only return information about containers it just operated on.
  2. Add docker_facts module
  3. Add docker inventory plugin (pull request already in), docker_facts should operate like this inventory plugin
  4. Update the docker and docker_image modules to resolve the issue where newer versions of docker-py can return empty strings when pulling an image with stream=True
  5. Add state=pull to docker_image (A pull request was submitted and may be open, but I think it is out of date)
  6. Probably some more things too.

"The essential problem is that there is a list of dictionaries per container that only contains the last running container when using “with_sequence” or “with_items” versus “count”, "

This is not an issue with “with_sequence”. Rather, it’s that register doesn’t do what you think it does when used with a loop. Beside the point. The solution is to make an “exact_count” type logic in the system as done with EC2 and Rackspace.

I’m totally fine with rewrites – I’ve also been talking a little bit with Docker folks, it would be nice in this era of API instability if we could get someone to take “lead” of them until we know what direction their API will take. Right now, everything is changing very very fast and breaking what seems like daily. If it weren’t such a popular and interesting project I would have with-held it core for another six months :slight_smile:

“Additionally, in devel, the modules are unusable due to us accepting a pull request for functionality that hadn’t even been accepted by docker-pt yet”

Let me know what SHA you would like to reverts and I’ll make it so.

Greetings again!

I do have a basic working docker_facts module. It still needs work and I’ll get it out soon after having someone review what I’ve done. I just wanted to let people know so there was a duplicate effort.

So, here it is:

https://github.com/ansible/ansible/pull/7242

I’m quite happy with it as it gives me any info I need for my containers or images. There may be even more tidbits of information to add to it.

Thanks for the input – the code is all pep8 and pyflakes happy.

Regards,

Patrick

This seems to have gone quiet? Any news on the module or plans to merge it? I feel this is something that would be very useful right now.

Hi!

Part of me wonders if people think I’m on a leper colony :slight_smile:

I asked a few people if they could review it and I think everyone is up to their eyeballs, I suppose.

I would love to get this in. I’m working on a talk for OSCON where I actually use this module.

I would be glad to do whatever possible to help get this moving along. I’m super busy, but this module is important to me and I’ll make time.

Regards,

Patrick

Greetings!

Monty - could I ask a great favor to review docker_facts? I’m trying to get a hold of Paul Durivage, who was the one who suggested I write this a year or so ago as well.

Thanks! BTW - I’m at OSCON this week if you are around.

Patrick

Sure thing! I’m on vacation in Panama this week, so it’ll likely be next week … But happy to