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

Add property machine.docker.local_node_host.external #2402

Merged
merged 1 commit into from
Sep 28, 2016

Conversation

l0rd
Copy link
Contributor

@l0rd l0rd commented Sep 9, 2016

What does this PR do?

Introduce a new property, machine.docker.local_node_host.external (along with the environment variable CHE_DOCKER_MACHINE_HOST_EXTERNAL), that allow to configure the external URL browsers need to use to connect with containers that are not directly pingable. This happens for example with Docker for Mac.

machine.docker.local_node_host.external=localhost

When this property is set the browser will use it to connect to the containers. Note that the wsmaster won't use that value to connect to the containers.

If this property is not set Che behaves as before.

This PR introduce a change to the API: Server class has a couple of new attributes: internalUrl and internalAddress. On the other there no particular change is needed client side and all clients will benefit of this change.

"servers": {
      "4401/tcp": {
                "url": "http://localhost:32906/wsagent/ext", 
                "internalUrl": "http://192.168.65.2:32906/wsagent/ext", 
                "address": "localhost:32906",
                "internalAddress": "192.168.65.2:32906",
                "path": "/wsagent/ext",
                "protocol": "http",
                "ref": "wsagent"
     }
}

This PR takes into account some feedbacks from PR #2004:

  • There are no changes on client side
  • Works on configurations where the wsagent and docker containers are on different hosts

I will not close PR #2004. I want to continue the work to integrate Traefik in the che-launcher there. But traefik/traefik#444 should be fixed first.

What issues does this PR fix or reference?

#1644

Previous behavior

Running Che on Docker for Mac required some networking hacking

New behavior

Remove the need for the networking hack when running Che on Docker for Mac and make running Che as simple as docker run -v /var/run/docker.sock:/var/run/docker.sock codenvy/che-launcher start on all platforms.

PR type

  • Minor change = no change to existing features or docs
  • Major change = changes existing features or docs

Minor change checklist

  • New API required?
  • API updated
  • Tests updated
  • Tests passed

@codenvy-ci
Copy link

Can one of the admins verify this patch?

@TylerJewell
Copy link

Nice work! Excited to have this get into master. I reviewed the changes for the che-launcher and they all look good.

@TylerJewell
Copy link

@garagatyi - this resolves a big inconvenience that users have with Docker for Mac. This eliminates a painful setup of the loopback alias on Macs. Your +1 here will be helpful.

@garagatyi
Copy link

In my opinion changing API for that workaround is not a good way to go. It will add pain sooner or later.
I've just figured out that we can try to play with IPTables in Che container.
For example we can try to redirect traffic inside of Che container from 32000-xxxxx to IP of xhyve machine. And let expose machines ports as localhost. In that case browser will be able to connect to machines on localhost and Che-server will use localhost also. But IPTables rules in che-server container will forward all traffic to another IP.
As I understand we can do that even after start of che-server, so we will be able to do that in che launcher and not change base che-server which works fine in all cases except che in container on mac.
Here is an example that I googled:
iptables -t nat -A OUTPUT -i lo -p tcp --dport 32000:65000 -j DNAT --to <IP OF XHYVE>:32000-65000
But I didn't try it and have no experience with IPTables, so not sure that I found correct (and complete) set of rules for out case.

@l0rd
Copy link
Contributor Author

l0rd commented Sep 10, 2016

@garagatyi can you please be more specific? Where do you want to run this iptable command: OSX, xyve VM, che-server container, agent container? What do you mean when you say "let expose machines ports as localhost"?

@garagatyi
Copy link

Add iptables rules in che-server container.

Configure che-server in such a way that localhost is host in servers provided by Che API.
I think we just should not set CHE_DOCKER_MACHINE_HOST variable to achieve that.

So all traffic from che-server container to localhost with ports 32000-xxxxx will go through IPTables redirection to IP of xhyve where ports of all containers are available.

@l0rd
Copy link
Contributor Author

l0rd commented Sep 11, 2016

Ok thanks for clarifying @garagatyi. I understand now. It's a smart solution :-) I will test that tomorrow.

@l0rd
Copy link
Contributor Author

l0rd commented Sep 12, 2016

Bingo! I've struggled to apply the IP tables rules but I've finally found the following command that work if executed inside che-sever container!

apk update
apk install iptables
sysctl -w net.ipv4.conf.eth0.route_localnet=1
iptables -t nat -A OUTPUT -p tcp -d localhost -o lo --dport 32000:65000 -j DNAT --to ${XHYVE_VM_IP}:32000-65000
iptables -t nat -A POSTROUTING -o eth0 -m addrtype --src-type LOCAL --dst-type UNICAST -j MASQUERADE

@l0rd
Copy link
Contributor Author

l0rd commented Sep 12, 2016

I've created a new PR to address the Docker for Mac problem: eclipse-che/che-dockerfiles#12

@garagatyi I will let you choose which one of these 2 PR is more suitable but let me explain why I believe we should keep this one and close eclipse-che/che-dockerfiles#12. Apart some concerns with the iptables solution I also think that changing the Server class, adding an internal url attribute, is not just a patch to solve the Docker for Mac issue but a solution for a recurring problem.

image

In the current model there is only one address to reach the wsagent server. That same address is used by the browser and the wsmaster. In the diagram I've called this address "Post Office". Depending on the platform the "Post Office" can be docker0, the IP of the docker host or just localhost. However in most scenarios wsmaster and wsagent are close and there is a shorter path to reach the wsagent (the red path in the diagram). Most important this red path avoid some scary mountains that can be tricky to cross (firewalls, routers, IPtables etc...). That's the case with Docker for Mac but that's also the case with Ubuntu (#2030) or Kubernetes or Openshift etc...This PR is to add the address of this red path to the attributes of the Server class.

@TylerJewell
Copy link

I tend to agree with Mario that having the internal sokution is more optimal for the longer term. And that we can continue to layer in the iptables solution where necessary.

Great graphic :)

@l0rd
Copy link
Contributor Author

l0rd commented Sep 14, 2016

I've updated the PR. I've added a new ServerProperties class where I moved servers path, internalUrl and internalAddress fields. That's the new Server API:

"servers": {
    "4401/tcp": {
        "ref": "wsagent",
        "protocol": "http",
        "address": "localhost:32929",
        "url": "http://localhost:32929/wsagent/ext",
        "port": "32929",
        "properties": {
            "internalUrl": "http://192.168.65.2:32929/wsagent/ext",
            "internalAddress": "192.168.65.2:32929",
            "path": "/wsagent/ext"
        }
    }
}

@garagatyi please review

@benoitf
Copy link
Contributor

benoitf commented Sep 14, 2016

I like it also
I would say that timeframe for updating json format as we're in process for 5.x release and still in milestone,is the right time. After it would be too late

@TylerJewell
Copy link

I am ok with this approach. I want to target this for M2 - solving this problem simplifies a lot of scenarios for us with Mac.

@TylerJewell TylerJewell added this to the 5.0.0-M2 milestone Sep 14, 2016
@TylerJewell TylerJewell added the kind/enhancement A feature request - must adhere to the feature request template. label Sep 14, 2016
* Internal address of the server in form <b>host:port</b>
* Used by wsmaster to communicate with the server
*/
String getInternalAddress();
Copy link

@garagatyi garagatyi Sep 15, 2016

Choose a reason for hiding this comment

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

Looks like all fields in that object are nullable.

String getPath();

/**
* Internal address of the server in form <b>host:port</b>
Copy link

@garagatyi garagatyi Sep 15, 2016

Choose a reason for hiding this comment

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

Line break by entering 'enter' key doesn't work in javadocs. Please add dot and <p/> or <br/>

@@ -25,25 +25,35 @@
String getRef();

/**
* Address of the server in form <b>host:port</b>
* External address of the server in form <b>host:port</b>
Copy link

@garagatyi garagatyi Sep 15, 2016

Choose a reason for hiding this comment

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

Line break by entering 'enter' key doesn't work in javadocs. Please add dot and <p/> or <br/>

import org.eclipse.che.api.core.model.machine.ServerProperties;

/**
* Describe development machine server instance.
Copy link

@garagatyi garagatyi Sep 15, 2016

Choose a reason for hiding this comment

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

Line break by entering 'enter' key doesn't work in javadocs. Please add <p/> or <br/>

private final Map<String, ServerConfImpl> serversConf;

@Inject
public DockerInstanceRuntimeInfo(@Assisted ContainerInfo containerInfo,
@Assisted String containerHost,
@Assisted String containerExternalHostname,

Choose a reason for hiding this comment

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

Not named assisted injection doesn't work when several arguments have the same class

Choose a reason for hiding this comment

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

Also please check what will happen if someone provide null value as one of these parameters. I believe nullable annotation should be declared somewhere.

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've changed to named assisted injection and checked if containerExternalHostname is null and set it to containerHost in that case.

*/
String getAddress();

/**
* Port of the server, e.g. 8080

Choose a reason for hiding this comment

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

I'm not sure should this port contain 8080 or 8080/tcp.

Choose a reason for hiding this comment

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

And it is external port. Please explain that in javadocs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's 8080, the protocol is not there:

"port": "32929"

public String getPort() { return port; }

@Override
public ServerProperties getProperties() { return properties; };

Choose a reason for hiding this comment

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

Please add equals/hashcode/toString methods

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

public String getInternalUrl() {
return internalUrl;
}
}

Choose a reason for hiding this comment

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

Please add equals/hashcode/toString methods

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

private final String internalAddress;
private final String internalUrl;

public DevMachineServerProperties(ServerProperties dto) {

Choose a reason for hiding this comment

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

Argument doesn't have to be DTO object. Please rename parameter to not confuse developers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


/**
* Describe development machine server instance.
* {@link ServerProperties}

Choose a reason for hiding this comment

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

Curly brackets are used when link is inlined into description. When it is used as a separate tag it has the same syntax as @author

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

private final Map<String, ServerConfImpl> serversConf;

@Inject
public DockerInstanceRuntimeInfo(@Assisted ContainerInfo containerInfo,
@Assisted String containerHost,
@Assisted String containerExternalHostname,

Choose a reason for hiding this comment

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

Also please check what will happen if someone provide null value as one of these parameters. I believe nullable annotation should be declared somewhere.

Copy link

@garagatyi garagatyi left a comment

Choose a reason for hiding this comment

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

Overall solution is good. Code requires some cleanups and fixes.

null,
null));
externalPort,
new ServerPropertiesImpl(null, internalHostname + ":" + externalPort, null)));

Choose a reason for hiding this comment

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

Please ensure that null value won't be treated as string "null"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After fixup externalHostname, internalHostname and externalPorts can never be null.

dockerNodeHost != null ? dockerNodeHost : (System.getenv(CHE_DOCKER_MACHINE_HOST) != null ?
System.getenv(CHE_DOCKER_MACHINE_HOST) :
containerHost),
dockerNodeExternalHostname != null ? dockerNodeInternalHostname : (

Choose a reason for hiding this comment

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

I believe that new policy of environment variables vs property usage in Che is that environment variable has priority over property.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Env variables are now evaluated first.

"32103",
new ServerPropertiesImpl("some/path4",
CONTAINER_HOST + ":32103",
null)));

Choose a reason for hiding this comment

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

Please add tests for all new use cases, e.g. internal/external addresses are configired with properties, env variables, both null, both not-null, some is null other not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a couple of tests

verify(descriptor).getProperties();
verify(descriptor2).getInternalUrl();
}

}

Choose a reason for hiding this comment

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

Please add trailing new line at the end of file

Choose a reason for hiding this comment

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

Please add trailing new line at the end of file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@@ -10,24 +10,8 @@
*******************************************************************************/
package org.eclipse.che.api.machine.server;

import org.eclipse.che.api.core.model.machine.MachineLimits;

Choose a reason for hiding this comment

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

Wildcard imports are forbidden in Che

Choose a reason for hiding this comment

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

Wildcard imports are forbidden in Che

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for forgetting that. I've fixed it.

}

public ServerImpl(Server server) {
this(server.getRef(), server.getProtocol(), server.getAddress(), server.getPath(), server.getUrl());
this(server.getRef(), server.getProtocol(), server.getAddress(), server.getUrl(), server.getPort(), (ServerPropertiesImpl) server.getProperties());

Choose a reason for hiding this comment

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

It would be better to avoid casting here as it prevents class cast exception. Please rewrite first constructor instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@@ -24,14 +24,7 @@
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
import org.eclipse.che.api.environment.server.MachineProcessManager;
import org.eclipse.che.api.environment.server.MachineServiceLinksInjector;
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;

Choose a reason for hiding this comment

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

Please do not use wildcard imports

Choose a reason for hiding this comment

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

Please do not use wildcard imports

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@Override
public String getInternalUrl() {
return descriptor.getInternalUrl();
}

Choose a reason for hiding this comment

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

Please add equals/hashcode/toString methods

Choose a reason for hiding this comment

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

This class still needs equals/hashcode/toString methods

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

At least add it in classes added by you.

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've added for both.

* This address is used by the browser to communicate with the server and
* corresponds to the internal address if not explicitly set.
* Can be set using property machine.docker.local_node_host.external or
* environment variable CHE_DOCKER_MACHINE_HOST_EXTERNAL
*/
String getAddress();

Choose a reason for hiding this comment

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

Now this method returns both host and port of the server. Are you going to change the behavior in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No I'm not:

"address": "localhost:32929"

Is that ok?

@garagatyi
Copy link

Looks like I'm not fully understand new server object. I'll take a look again tomorrow and ask you if something is not clear.

@l0rd
Copy link
Contributor Author

l0rd commented Sep 16, 2016

@garagatyi I've commited the fixes you have suggested:

  • Javadoc improved
  • Removed port field in Server class
  • Annotated all fields in ServerProperties as nullable
  • Overwritten methods equals, hashcode and toString for classes DevMachineServer and DevMachineServerProperties
  • Fixed unnamed assisted injections
  • Added some unit tests
  • Restored the license header (I'll submit a distinct PR for that)
  • Environment variables have now higher priority then properties

@Override
public String getInternalUrl() {
return descriptor.getInternalUrl();
}

Choose a reason for hiding this comment

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

This class still needs equals/hashcode/toString methods

verify(descriptor).getProperties();
verify(descriptor2).getInternalUrl();
}

}

Choose a reason for hiding this comment

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

Please add trailing new line at the end of file

@@ -10,24 +10,8 @@
*******************************************************************************/
package org.eclipse.che.api.machine.server;

import org.eclipse.che.api.core.model.machine.MachineLimits;

Choose a reason for hiding this comment

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

Wildcard imports are forbidden in Che

@@ -24,14 +24,7 @@
import org.eclipse.che.api.core.rest.shared.dto.ServiceError;
import org.eclipse.che.api.environment.server.MachineProcessManager;
import org.eclipse.che.api.environment.server.MachineServiceLinksInjector;
import org.eclipse.che.api.machine.server.model.impl.CommandImpl;

Choose a reason for hiding this comment

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

Please do not use wildcard imports

@@ -145,6 +145,7 @@ public MachineRuntimeInfoImpl getRuntime() {
try {
final ContainerInfo containerInfo = docker.inspectContainer(container);
machineRuntime = new MachineRuntimeInfoImpl(dockerMachineFactory.createMetadata(containerInfo,
null,
Copy link

@garagatyi garagatyi Sep 19, 2016

Choose a reason for hiding this comment

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

For me it looks like this argument is always null. Did I miss something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Feel free to add more details to your questions if that was not what you meant. I think you are wondering how Server.url is ever set if this parameter is always null. It should be set using property machine.docker.local_node_host.external but it looks as it's always null.

Property machine.docker.local_node_host.external is injected in LocalDockerModule objects that are binded to DockerInstanceRuntimeInfo in LocalDockerModule.

Therefore method DockerInstance.getRuntime() is not used by the API to get the machine runtime. That explains why Server.url is correctly returned by the API.

Choose a reason for hiding this comment

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

Let me explain how I understand what is happening with these changes. Please correct me if I'm mistaken.
DockerInstance always pass null as external host. DockerInstanceRuntimeInfo doesn't change that. LocalDockerInstanceRuntimeInfo uses assisted injection, so this null is passed here too.
So I don't see how another than null value can be accepted by DockerInstanceRuntimeInfo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As discussed on Slack I've cleaned-up LocalDockerInstanceRuntimeInfo class and made it easier to read.

@@ -36,14 +42,15 @@
String getProtocol();

/**
* Path to access the server.
* Url of the server, e.g.&nbsp;http://localhost:8080
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 think you mean "regular space". If I add a regular space and I try to generate the Javadoc I have the following result:

image

If I use &nbsp; instead the sentence is not broken:

image

Choose a reason for hiding this comment

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

OK, interesting

@l0rd l0rd force-pushed the ws-url-fix-reloaded branch from 6dafbc5 to a35ccae Compare September 19, 2016 08:49
import org.eclipse.che.api.machine.server.model.impl.MachineSourceImpl;
import org.eclipse.che.api.machine.server.model.impl.ServerImpl;
import org.eclipse.che.api.machine.server.model.impl.SnapshotImpl;
import org.eclipse.che.api.machine.server.model.impl.*;
Copy link
Contributor

Choose a reason for hiding this comment

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

still need to remove wildcard import

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

yep it was pending request (I forgot to validate it)

@l0rd l0rd force-pushed the ws-url-fix-reloaded branch from a35ccae to f6dfd7b Compare September 19, 2016 13:52
@@ -145,6 +145,7 @@ public MachineRuntimeInfoImpl getRuntime() {
try {
final ContainerInfo containerInfo = docker.inspectContainer(container);
machineRuntime = new MachineRuntimeInfoImpl(dockerMachineFactory.createMetadata(containerInfo,
null,

Choose a reason for hiding this comment

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

Let me explain how I understand what is happening with these changes. Please correct me if I'm mistaken.
DockerInstance always pass null as external host. DockerInstanceRuntimeInfo doesn't change that. LocalDockerInstanceRuntimeInfo uses assisted injection, so this null is passed here too.
So I don't see how another than null value can be accepted by DockerInstanceRuntimeInfo.

@l0rd l0rd force-pushed the ws-url-fix-reloaded branch from f6dfd7b to f549903 Compare September 19, 2016 23:07
@riuvshin riuvshin modified the milestones: 5.0.0-M3, 5.0.0-M2 Sep 20, 2016
@bmicklea bmicklea modified the milestones: 5.0.0-M3, 5.0.0-M4 Sep 20, 2016
@garagatyi
Copy link

@musienko-maxim Please do a QA cycle for this PR

@garagatyi
Copy link

ci-build

@codenvy-ci
Copy link

@garagatyi
Copy link

QA team has approved this PR. We will merge it when M3 is released

This property allows communications beetween browser and containers that are on different networks (eg. Docker for Mac or NAT)

Signed-off-by: Mario Loriedo <mloriedo@redhat.com>
@l0rd l0rd force-pushed the ws-url-fix-reloaded branch from 9f1d920 to c2ad879 Compare September 28, 2016 08:54
@l0rd l0rd merged commit dabb749 into eclipse-che:master Sep 28, 2016
JPinkney pushed a commit to JPinkney/che that referenced this pull request Aug 17, 2017
This property allows communications beetween browser and containers that are on different networks (eg. Docker for Mac or NAT)

Signed-off-by: Mario Loriedo <mloriedo@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/enhancement A feature request - must adhere to the feature request template.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants