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

Allow scaling services that have port binding of the form host_ip::container_port #1659

Merged
merged 1 commit into from
Oct 23, 2015

Conversation

viranch
Copy link

@viranch viranch commented Jul 6, 2015

Compose gives up scaling if a service has a port binding of the form: 192.168.1.2::80 (bind to a specific host IP but a random host port, for the container port 80) with the error:

Service "foo" cannot be scaled because it specifies a port on the host. If multiple containers for this service were created, the port would clash.

Remove the ":" from the port definition in docker-compose.yml so Docker can choose a random port for each container.

Compose wrongly assumes that a host port is specified because there's a : in the port binding.

This PR fixes this.

@aanand
Copy link

aanand commented Jul 6, 2015

Thanks.

  1. Rather than inspecting the string to see if there's an external port specified, it'd be better call split_port() and inspect its output.
  2. It'd be good to have some unit tests for can_be_scaled.

@@ -679,7 +679,8 @@ def labels(self, one_off=False):

def can_be_scaled(self):
for port in self.options.get('ports', []):
if ':' in str(port):
external = split_port(port)[1]
if external and not (type(external) == tuple and external[1] == None):
Copy link

Choose a reason for hiding this comment

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

I think that if you're checking that type(external) == tuple, then checking it for truthiness is unnecessary. And I think isinstance is better than comparing types. So this can just be:

if isinstance(external, tuple) and external[1] == None:

Copy link

Choose a reason for hiding this comment

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

Oh, and PEP 8 says to use is None, not == None.

Comparisons to singletons like None should always be done with is or is not , never the equality operators

Copy link
Author

Choose a reason for hiding this comment

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

then checking it for truthiness is unnecessary

You mean external[1] == None is not required? I think it is, external[1] corresponds to host port, if its specified can_be_scaled should fail, else succeed.

@viranch viranch force-pushed the hostip-random-port branch 4 times, most recently from f02b5ab to b60ee3a Compare July 8, 2015 19:26
# TODO: this function should ideally move to docker.utils.ports
# get rid of it once its accepted into docker-py
def has_external_port(external_binding):
if external_binding is None:
Copy link
Author

Choose a reason for hiding this comment

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

We don't need to check this at all IMO because docker-py's split_port never returns a list with None elements as the 2nd return value (it's either a just None, or a list of strings or tuples).

Copy link

Choose a reason for hiding this comment

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

If it's going to be a public function in docker-py, it needs to be able to handle all valid port bindings, not just the subset created by split_port().

Copy link
Author

Choose a reason for hiding this comment

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

If we do what I have proposed in https://github.com/docker/compose/pull/1659/files#r34191487 then we don't need this has_external_port() function at all, because my proposed function simplifies readability. The whole reason for having this function in the first place was it was a little complex from readability standpoint.

@viranch
Copy link
Author

viranch commented Jul 8, 2015

I think we might also need (unit?) tests covering port ranges for can_be_scaled(), given docker-py's ports util supports it.

@viranch
Copy link
Author

viranch commented Aug 1, 2015

@aanand Ping :) Let me know about my comments on the diff, I'll rebase from master given the refactor of can_be_scaled() in 9ffe69a

@viranch viranch force-pushed the hostip-random-port branch from b60ee3a to 454b52f Compare August 8, 2015 08:24
@viranch
Copy link
Author

viranch commented Aug 8, 2015

Rebased with latest master, seems like the CI build failed due to docker errors.

+ docker run --rm -t --privileged --volume=/var/jenkins_home/workspace/Compose-PRs/.git:/code/.git --name compose-prs1012 --volume=/var/run/docker.sock:/var/run/docker.sock -e DOCKER_VERSIONS=all -e TAG=compose:454b52f --entrypoint=script/ci compose:454b52f --verbose
Running lint checks
Running tests against Docker 1.7.1
Waiting for Docker to start...
Docker failed to start
time="2015-08-08T08:26:11.302291821Z" level=warning msg="Udev sync is not supported. This will lead to unexpected behavior, data loss and errors. For more information, see https://docs.docker.com/reference/commandline/cli/#daemon-storage-driver-option" 
time="2015-08-08T08:26:11.303233892Z" level=error msg="There are no more loopback devices available." 
time="2015-08-08T08:26:11.303417028Z" level=fatal msg="Error starting daemon: error initializing graphdriver: loopback mounting failed" 
Build step 'Execute shell' marked build as failure

@dnephin
Copy link

dnephin commented Aug 27, 2015

Resolves #1102

'foo',
image='foo',
ports=["127.0.0.1:1000:2000"])
self.assertEqual(service.specifies_host_port(), True)
Copy link

Choose a reason for hiding this comment

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

Minor nit, now that we're using py.test we actually get better error messages if you use assert service.specifies_host_port() or assert not ....

Would you mind changing the assertions to use bare assert ?

Copy link
Author

Choose a reason for hiding this comment

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

Rest of the service_test.py still uses self.assertEqual, I don't want to break the convention. What you're suggesting could go in as a separate PR that updates all tests IMO.

@viranch viranch closed this Oct 17, 2015
@viranch viranch force-pushed the hostip-random-port branch from 520d29a to ae3c66b Compare October 17, 2015 18:47
@viranch
Copy link
Author

viranch commented Oct 17, 2015

Oops, bad push closed this, reopening.

@viranch viranch reopened this Oct 17, 2015
@viranch viranch force-pushed the hostip-random-port branch from c00e710 to 9158a1f Compare October 17, 2015 18:53
…host port

Signed-off-by: Viranch Mehta <viranch.mehta@gmail.com>
@viranch viranch force-pushed the hostip-random-port branch from 9158a1f to 258c8bc Compare October 17, 2015 19:10
@viranch viranch changed the title Allow service to specify host IP with random host port in port bindings Allow scaling services that have port binding of the form host_ip::container_port Oct 17, 2015
@viranch viranch changed the title Allow scaling services that have port binding of the form host_ip::container_port Allow scaling services that have port binding of the form host_ip::container_port Oct 17, 2015
@viranch
Copy link
Author

viranch commented Oct 17, 2015

Rebased with latest master.

viranch added a commit to viranch/compose that referenced this pull request Oct 18, 2015
@dnephin
Copy link

dnephin commented Oct 19, 2015

LGTM

@dnephin dnephin self-assigned this Oct 19, 2015
@mnowster
Copy link

LGTM. Thanks @viranch 👍

mnowster added a commit that referenced this pull request Oct 23, 2015
Allow scaling services that have port binding of the form `host_ip::container_port`
@mnowster mnowster merged commit 5d60fbe into docker:master Oct 23, 2015
@dnephin dnephin added this to the next release milestone Nov 12, 2015
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.

5 participants