nats.java icon indicating copy to clipboard operation
nats.java copied to clipboard

Cannot resolve domain in client in k8s

Open z0mb1ek opened this issue 3 years ago • 12 comments

I has search domain at my /etc/resolv.conf:

cat /etc/resolv.conf 
search dev.svc.cluster.local svc.cluster.local cluster.local local
nameserver 10.222.0.10
options ndots:5

On 2.16.8 version it worked well, i resolved nats:4222 on my service in kubernetes. But when i upgrade to 2.16.9 it starts raising exception:

exceptionOccurred, Exception: java.io.IOException: java.lang.IllegalArgumentException: port out of range:-1

i see this commits https://github.com/nats-io/nats.java/pull/847/files

could that be the reason?

z0mb1ek avatar Apr 08 '23 21:04 z0mb1ek

Hostname resolution was added, it can be turned off via Options.Builder().noResolveHostnames()

Do you happen to have a stacktrace for that exception, maybe we can find a way to address it.

scottf avatar Apr 09 '23 11:04 scottf

Yes, with this option work well now. Please explain why it is needed?

Caused by: java.io.IOException: Unable to connect to NATS servers: [nats://nats:4222]
	at io.nats.client.impl.NatsConnection.connect(NatsConnection.java:240)
	at io.nats.client.impl.NatsImpl.createConnection(NatsImpl.java:29)
	at io.nats.client.Nats.createConnection(Nats.java:303)
	at io.nats.client.Nats.connect(Nats.java:210)
Screenshot 2023-04-09 at 21 49 53

there are no more logs

z0mb1ek avatar Apr 09 '23 17:04 z0mb1ek

It's needed because hostname resolution was added and made as the default. It is possible that there is a way for resolution to work properly, which is why I needed the stack trace for your original configuration. Maybe you can extend then override ErrorListenerLoggerImpl and print the stack trace instead of the default which is this:

    public void exceptionOccurred(final Connection conn, final Exception exp) {
        LOGGER.severe(() -> supplyMessage("exceptionOccurred", conn, null, null, "Exception: ", exp));
    }

scottf avatar Apr 10 '23 13:04 scottf

here they are:

java.io.IOException: java.lang.IllegalArgumentException: port out of range:-1
	at io.nats.client.impl.SocketDataPort.connect(SocketDataPort.java:99)
	at io.nats.client.impl.SocketDataPort.connect(SocketDataPort.java:52)
	at io.nats.client.impl.NatsConnection.tryToConnect(NatsConnection.java:421)
	at io.nats.client.impl.NatsConnection.connect(NatsConnection.java:203)
	at io.nats.client.impl.NatsImpl.createConnection(NatsImpl.java:29)
	at io.nats.client.Nats.createConnection(Nats.java:303)
	at io.nats.client.Nats.connect(Nats.java:210)
	at tech.livecom.streams.config.NatsConfig.testBean(NatsConfig.kt:110)
	at tech.livecom.streams.config.NatsConfig$$SpringCGLIB$$0.CGLIB$testBean$2(<generated>)
	at tech.livecom.streams.config.NatsConfig$$SpringCGLIB$$2.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258)
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
	at tech.livecom.streams.config.NatsConfig$$SpringCGLIB$$0.testBean(<generated>)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:578)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:139)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:491)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1332)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1162)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:917)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584)
	at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:66)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:310)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1304)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1293)
	at tech.livecom.streams.ApplicationKt.main(Application.kt:15)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:578)
	at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:95)
	at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
	at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65)
Caused by: java.lang.IllegalArgumentException: port out of range:-1
	at java.base/java.net.InetSocketAddress.checkPort(InetSocketAddress.java:152)
	at java.base/java.net.InetSocketAddress.<init>(InetSocketAddress.java:233)
	at io.nats.client.impl.SocketDataPort.connect(SocketDataPort.java:77)
	... 41 more

z0mb1ek avatar Apr 11 '23 02:04 z0mb1ek

So that didn't help. I guess I need to talk to our Kubernetes guy to figure out how exactly ip addresses are resolved and why it doesn't fail. I guess for now I can try to determine if port is -1 after resolve and toss that resolution.

scottf avatar Apr 11 '23 12:04 scottf

If I build a snapshot from a branch, would you be able to test? I'll add some debug to the hostname resolution and we'll see what exactly the resolver does.

scottf avatar Apr 11 '23 12:04 scottf

yes, i can test it

z0mb1ek avatar Apr 11 '23 12:04 z0mb1ek

Can you try this:

import io.nats.client.Connection;
import io.nats.client.Nats;
import io.nats.client.Options;
import io.nats.client.api.ServerInfo;
import io.nats.client.support.NatsUri;

import java.net.InetAddress;
import java.net.URISyntaxException;
import java.net.UnknownHostException;

public class TestResolve {
    public static void main(String[] args) {

        // simple resolve
        try {
            NatsUri nuri = new NatsUri("connect.ngs.global");
            System.out.println(nuri);
            System.out.println("OLD: " + nuri.reHost("10.111.7.11://10.111.7.11:4222"));
            System.out.println("NEW: " + reHost(nuri, "10.111.7.11://10.111.7.11:4222"));
            resolve(nuri);
        } catch (URISyntaxException e) {
            System.out.println(e);
        }

        // resolve based on server discovery from actual connection
        Options options = new Options.Builder()
            .server(Options.DEFAULT_URL)
            .build();

        try (Connection nc = Nats.connect(options)) {
            ServerInfo si = nc.getServerInfo();
            for (String url : si.getConnectURLs()) {
                NatsUri nuri = new NatsUri(url);
                System.out.println(nuri + " ---> " + nuri.getUri().getScheme() + " " + nuri.getUri().getPort());
                if (nuri.hostIsIpAddress()) {
                    System.out.println("    already an ip address.");
                }
                else {
                    resolve(nuri);
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void resolve(NatsUri nuri) {
        InetAddress last;
        try {
            InetAddress[] addresses = InetAddress.getAllByName(nuri.getHost());
            for (InetAddress a : addresses) {
                try {
                    NatsUri rehosted = reHost(nuri, a.getHostAddress());
                    System.out.println("    " + a.getHostAddress() + " | OK | " + rehosted);
                }
                catch (URISyntaxException u) {
                    System.out.println("    " + a.getHostAddress() + " | EX | " + u);
                }
            }
        }
        catch (UnknownHostException e) {
            System.out.println("UHE | " + e);
        }
    }

    public static NatsUri reHost(NatsUri nuri, String newHost) throws URISyntaxException {
        URI uri = nuri.getUri();
        int at = newHost.indexOf("://");  // 10.111.7.11://10.111.7.11:4222
        if (at != -1) {
            newHost = newHost.substring(0, at);
        }
        String newUrl = (uri.getRawUserInfo() == null)
            ? uri.getScheme() + "://" + newHost + ":" + uri.getPort()
            : uri.getScheme() + "://" + uri.getRawUserInfo() + "@" + newHost + ":" + uri.getPort();
        return new NatsUri(newUrl, uri.getScheme());
    }
}

scottf avatar Apr 11 '23 18:04 scottf

Logs: Screenshot 2023-04-14 at 03 28 06

z0mb1ek avatar Apr 13 '23 23:04 z0mb1ek

I'm going to make some assumptions, please correct me if I'm wrong:

  1. what is being resolved is nats://localhost:4222
  2. Given InetAddress a, a.getHostAddress() is returning something like 10.111.7.11://10.111.7.11:4222
  3. So when rehost, I'm getting nats://10.111.7.11://10.111.7.11:4222:4222

I updated the test code and have a fix. It makes the assumption that if the getHostAddress has the :// that I can just ignore that part.

scottf avatar Apr 14 '23 12:04 scottf

@z0mb1ek Apologies I was out of office for the last 2 weeks for a personal issue. If you can confirm the last reply item 2, I can make a fix.

scottf avatar May 01 '23 21:05 scottf

@scottf sorry, i did not see your reply. It starts working. I can ask my devops what has changed in dns resolving

z0mb1ek avatar May 01 '23 21:05 z0mb1ek