mauricioklein / docker-compose-api

A Docker Compose parser for Ruby
MIT License
33 stars 25 forks source link

Error starting compose stack - undefined method `running' for nil:NilClass (NoMethodError) #30

Open CpuID opened 8 years ago

CpuID commented 8 years ago

Description

When starting the stack with docker-compose-api, and containers from a previous execution are still running, an error occurs after attempting a clean restart.

How to reproduce

compose = DockerCompose.load("./docker-compose.yml", true)
compose.kill
compose.delete
sleep 1
compose.start

What do you expect

The containers to be killed off, and fresh ones started respectively.

What happened instead

An exception was raised, undefined method 'running?' for nil:NilClass (NoMethodError)

Software:

/home/nathan/.rvm/gems/ruby-2.1.6@infrastructure/bundler/gems/docker-compose-api-1171d7c6f88b/lib/docker-compose/models/compose_container.rb:192:in `block in start': undefined method `running?' for nil:NilClass (NoMethodError)
    from /home/nathan/.rvm/gems/ruby-2.1.6@infrastructure/bundler/gems/docker-compose-api-1171d7c6f88b/lib/docker-compose/models/compose_container.rb:190:in `each'
    from /home/nathan/.rvm/gems/ruby-2.1.6@infrastructure/bundler/gems/docker-compose-api-1171d7c6f88b/lib/docker-compose/models/compose_container.rb:190:in `start'
    from /home/nathan/.rvm/gems/ruby-2.1.6@infrastructure/bundler/gems/docker-compose-api-1171d7c6f88b/lib/docker-compose/models/compose.rb:116:in `block in call_container_method'
    from /home/nathan/.rvm/gems/ruby-2.1.6@infrastructure/bundler/gems/docker-compose-api-1171d7c6f88b/lib/docker-compose/models/compose.rb:115:in `each'
    from /home/nathan/.rvm/gems/ruby-2.1.6@infrastructure/bundler/gems/docker-compose-api-1171d7c6f88b/lib/docker-compose/models/compose.rb:115:in `call_container_method'
    from /home/nathan/.rvm/gems/ruby-2.1.6@infrastructure/bundler/gems/docker-compose-api-1171d7c6f88b/lib/docker-compose/models/compose.rb:70:in `start'
...
CpuID commented 8 years ago

Doing some extra debug now to drop in here to try help find a solution. There are 5 containers in my docker-compose.yml. It looks like there are some calls to add_dependency that contain empty objects.

CpuID commented 8 years ago

Dropped the following debug into here:

      links.each do |service, label|
        puts "Link: #{service}. Label: #{label}"
        dependency_container = @containers[service]
        puts "dependency_container: #{dependency_container}"
        container.add_dependency(dependency_container)
      end 

Got the below result:

Link: redis. Label: redis
dependency_container: #<ComposeContainer:0x000000024f54a0>
Link: elasticsearch. Label: elasticsearch
dependency_container: #<ComposeContainer:0x000000024f6aa8>
Link: redis. Label: redis
dependency_container: #<ComposeContainer:0x000000024f54a0>
Link: /logstash_elasticsearch_41. Label: /logstash_logstash_40/elasticsearch
dependency_container: 
Link: /logstash_redis_38. Label: /logstash_logstash_40/redis
dependency_container: 
Link: /logstash_redis_38. Label: /logstash_filebeat_37/redis
dependency_container: 

It looks like it's trying to find the dependency containers for 2 different formats of links...?

Docker version 1.10.2

CpuID commented 8 years ago

The docker-compose.yml in use - minimal redaction, and all unrelated to the issue at hand (all links are there).

CpuID commented 8 years ago

One potential solution, requires peer review:

https://github.com/mauricioklein/docker-compose-api/blob/master/lib/docker-compose/models/compose.rb#L57 becomes container.add_dependency(dependency_container) unless dependency_container.nil?

It looks as though when it loads the running containers + the config'ed containers, its attempting to add dependencies twice, in 2 different formats. The config'ed container format seems sufficient?

CpuID commented 8 years ago

Hmm one downside to the above, I end up with 2 of one of my containers, specifically logstash :(

CONTAINER ID        IMAGE                              COMMAND                  CREATED             STATUS              PORTS                              NAMES
c22b3a575c41        1fa6a3979958                       "/docker-entrypoint.s"   3 minutes ago       Up 3 minutes                                           logstash_logstash_44
f6e53546f8a7        2f4591bdf8e44c7482cbae66969849a1   "/docker-entrypoint.s"   3 minutes ago       Up 3 minutes                                           logstash_logstash_48
mauricioklein commented 8 years ago

Hello @CpuID

Thanks for submitting this issue and related details.

I'll be checking the problem soon and will work on a fix as soon as possible.

Thanks again and sorry about the bug.

mauricioklein commented 8 years ago

@CpuID I think I found the problem:

After deleted, containers should no longer exist in compose entry, since they don't exist anymore. A call to start was causing the reported error, because dependencies were deleted and, then, the system was trying to restart a nil container.

I've opened the PR #32 with the necessary fix.

Could you please review it?

Since it's approved, I'll merge and release a new version with the bugfix.

Thanks in advance and sorry about the bug.

mauricioklein commented 8 years ago

Bug fixed by PR#32.

Version 1.1.2 generated with related bugfix.

Thanks for the bug submission!

CpuID commented 8 years ago

@mauricioklein I think this may need to be reopened, as PR#32 has some slightly adverse effects (when used in my current use case).

With 1.1.2, if I perform the below:

compose = DockerCompose.load("./docker-compose.yml", true)
compose.kill
compose.delete
sleep 1
compose.start

No containers will start at all. If I try this instead:

compose = DockerCompose.load("./docker-compose.yml")
sleep 1
compose.start

Containers start fine as expected. The purpose of the first codeblock is to allow idempotency and cleanup if there were stale containers left behind from a previous run.

I tried another iteration just as a test:

compose1 = DockerCompose.load("./docker-compose.yml", true)
compose2 = DockerCompose.load("./docker-compose.yml")
compose1.kill
compose1.delete
sleep 1
compose2.start

This works as expected, but is obviously not ideal.

mauricioklein commented 8 years ago

@CpuID I think this was a missunderstood from my part, and I apologize about that.

The first behavior is working exactly I expected, despite of this is not in conformity with original Docker Compose.

After some research, the correct fix would be to recreate the deleted containers before starting them again.

I'm reopening this bug and, as soon I have a fix, I'll let you know.

Thanks in advance!

CpuID commented 8 years ago

@mauricioklein great thanks :)