docker-client icon indicating copy to clipboard operation
docker-client copied to clipboard

ContainerInspect on PortBindings throws NullPointerException on Podman Installation

Open 3XC1T3D opened this issue 2 years ago • 3 comments

BUG REPORT or FEATURE REQUEST?: BUG REPORT

Description

we get a NullPointerException when we inspect a podman containers PortBindings.

How to reproduce

we want to inspect the publish ports of a podman container. In the inspect json of the podman container the PortBinding are listet as follows:

 "PortBindings": {
      "15671/tcp": null,
      "15672/tcp": [
           {
                "HostIp": "0.0.0.0",
                "HostPort": "15672"
           }
      ],

if the port isn't expposed like 15671, a NullPointerException will be thrown

What do you expect

the containerport defined as "null" should be ignored.

What happened instead

containerport defined as "null" is recognized and a NullPointerException is thrown

Software:

  • podman version: 4.1.1
  • Spotify's docker-client version: 6.0.5

Full backtrace

Caused by: jakarta.ws.rs.client.ResponseProcessingException: jakarta.ws.rs.ProcessingException: com.fasterxml.jackson.databind.JsonMappingException: portBindings value
 at [Source: (org.jboss.resteasy.specimpl.AbstractBuiltResponse$InputStreamWrapper); line: 1, column: 3087] (through reference chain: org.mandas.docker.client.messages.ImmutableContainerInfo$Builder["HostConfig"]->org.mandas.docker.client.messages.ImmutableHostConfig$Builder["PortBindings"])
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:203)
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:514)
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocationBuilder.method(ClientInvocationBuilder.java:318)
	at org.mandas.docker.client.DefaultDockerClient.request(DefaultDockerClient.java:2466)
	... 73 more
Caused by: jakarta.ws.rs.ProcessingException: com.fasterxml.jackson.databind.JsonMappingException: portBindings value
 at [Source: (org.jboss.resteasy.specimpl.AbstractBuiltResponse$InputStreamWrapper); line: 1, column: 3087] (through reference chain: org.mandas.docker.client.messages.ImmutableContainerInfo$Builder["HostConfig"]->org.mandas.docker.client.messages.ImmutableHostConfig$Builder["PortBindings"])
	at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:254)
	at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:90)
	at org.jboss.resteasy.specimpl.AbstractBuiltResponse.readEntity(AbstractBuiltResponse.java:256)
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:167)
	... 76 more
Caused by: com.fasterxml.jackson.databind.JsonMappingException: portBindings value
 at [Source: (org.jboss.resteasy.specimpl.AbstractBuiltResponse$InputStreamWrapper); line: 1, column: 3087] (through reference chain: org.mandas.docker.client.messages.ImmutableContainerInfo$Builder["HostConfig"]->org.mandas.docker.client.messages.ImmutableHostConfig$Builder["PortBindings"])
	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:276)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:623)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:611)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:173)
	at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.vanillaDeserialize(BuilderBasedDeserializer.java:293)
	at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:217)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:158)
	at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.vanillaDeserialize(BuilderBasedDeserializer.java:293)
	at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:217)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
	at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:2025)
	at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1175)
	at com.fasterxml.jackson.jakarta.rs.base.ProviderBase.readFrom(ProviderBase.java:776)
	at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.readFrom(AbstractReaderInterceptorContext.java:101)
	at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:80)
	at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:217)
	... 79 more
Caused by: java.lang.NullPointerException: portBindings value
	at java.base/java.util.Objects.requireNonNull(Objects.java:233)
	at org.mandas.docker.client.messages.ImmutableHostConfig$Builder.putAllPortBindings(ImmutableHostConfig.java:7860)
	at org.mandas.docker.client.messages.ImmutableHostConfig$Builder.portBindings(ImmutableHostConfig.java:7843)

3XC1T3D avatar Sep 05 '22 08:09 3XC1T3D

Hi @3XC1T3D ,

(docker-client does not directly support podman, but it may work for most of the cases)

Could you please post the container's creation command here for me to check how this null is inserted into the portBinding construct?

Thanks

dmandalidis avatar Sep 06 '22 04:09 dmandalidis

Hi @dmandalidis , yes so far docker-client works pretty well with the docker-compatible API of Podman. It seems there are only some edge cases where the responses from Podman are not 100% identical to the Docker responses.

This sample code will start the official RabbitMQ container and then cause the mentioned NullPointerException on inspection of the (successfully) started container

    static String podmanEngineUrl = "http://<host>:2375";

    public static void main(String[] args) throws DockerException, InterruptedException {
        try (DefaultDockerClient dc = new ResteasyDockerClientBuilder()
            .uri(URI.create(podmanEngineUrl))
            .build()) {

            List<Integer> exposedPorts = List.of(5672, 4369, 25672, 15692, 15672, 5673);

            Map<String, List<PortBinding>> portBindings = exposedPorts.stream().collect(Collectors.toMap(
                port -> port + "/tcp",
                port -> List.of(PortBinding.of("0.0.0.0", port))
            ));

            HostConfig hostConfig = HostConfig.builder()
                .portBindings(portBindings)
                .build();

            ContainerConfig containerConfig = ContainerConfig.builder()
                .image("rabbitmq:3.9.22-alpine")
                .hostConfig(hostConfig)
                .tty(true)
                // note: if the healthcheck is not given, another NullPointerException will be thrown on inspectContainer()
                .healthcheck(ContainerConfig.Healthcheck.builder()
                    .test(List.of("CMD-SHELL", "${APP_ROOT}/docker-healthcheck.sh")).build())
                .exposedPorts(exposedPorts.stream().map(port -> port + "/tcp").collect(Collectors.toList()))
                .build();

            dc.pull(containerConfig.image());
            ContainerCreation container =
                dc.createContainer(containerConfig, "rabbitmq");

            dc.startContainer(container.id());
            Thread.sleep(2000);
            dc.inspectContainer(container.id()); // this will throw NPE
        }
    }

When we compare "docker inspect rabbitmq" vs. "podman inspect rabbitmq", we see the difference at HostConfig->PortBindings, where non-bound ports are ommitted by Docker, but presented with a "null" value by Podman:

               "PortBindings": {
                    "15672/tcp": [
                         {
                              "HostIp": "0.0.0.0",
                              "HostPort": "15672"
                         }
                    ],
                    "15691/tcp": null,

I think Podman should fix this in a future release to comply with the Docker API, however for the current and older versions of Podman, it also seems easy and safe to handle this in the docker-client: In ImmutableHostConfig.java:7856 adding if (v == null) continue; should be enough.

Note that a very similar NullPointer problem will occur once you comment out the .healthcheck(...) line, since the lack of any healthchecks also causes a slight difference in the inspect json. In addAllLog(ImmutableContainerState.java:1296), the iterated elements collection is null. This might be fixed by a preceding null check in ImmutableContainerState.java:1285 as well.

Thanks!

lukaseckert avatar Sep 06 '22 07:09 lukaseckert

@dmandalidis You might have a look at the commit on my fork (https://github.com/lukaseckert/docker-client/commit/b5e29f5cc6b22dbb98317b52e38ba6aee685a368) which fixes both NullPointerExceptions related to the JSON differences when targeting Podman. With these two little tweaks, we successfully deployed a complex application with many containers on a Podman instance using the docker-client :) Our test involved pulling images, creating networks, and stop/remove/create/start/inspect commands on different containers. (However we did not yet test building images on a remote host or loading and exporting the images)

lukaseckert avatar Sep 06 '22 15:09 lukaseckert

@dmandalidis did you take a look at the pull request?

Best Regards

3XC1T3D avatar Jan 20 '23 22:01 3XC1T3D

@3XC1T3D Sorry, but I wouldn't want to deviate from Docker API. No matter how simple a change might be, it doesn't seem correct to me.

dmandalidis avatar Jan 21 '23 07:01 dmandalidis