This is an automated email from the ASF dual-hosted git repository.
FrankChen021 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 46e8f93eb83 feat: Add advertisedPlaintextPort to DruidNode for sidecar
proxy support (#19598)
46e8f93eb83 is described below
commit 46e8f93eb83132fd516ff7eb208125ed00fc6c5c
Author: Sadananda Aithal <[email protected]>
AuthorDate: Thu Oct 1 18:58:13 2026 +0530
feat: Add advertisedPlaintextPort to DruidNode for sidecar proxy support
(#19598)
* Add advertisedPlaintextPort to DruidNode for sidecar proxy support
Allow Druid nodes to advertise a different plaintext port (e.g. an envoy
sidecar port) for peer discovery while binding Jetty to the actual
service port. This enables east-west mTLS via a sidecar proxy without
changing Druid's bind configuration.
The new `advertisedPlaintextPort` JSON property on DruidNode controls
the port advertised during peer discovery. When unset (or <= 0), it
falls back to `plaintextPort` preserving existing behaviour.
* Fix DruidNode constructor source compatibility
Restore the nine-argument DruidNode(..., labels) overload that was
dropped when advertisedPlaintextPort was added to the @JsonCreator
constructor, and update the new advertisedPlaintextPort tests to use
the ten-argument form. Without this the server module did not compile.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Use advertisedPlaintextPort for all peer-facing paths and document it
Address review feedback on #19598:
- DruidNode.getPortToUse() (and so getUriToUse()) returns the advertised
port when TLS is disabled, matching getHostAndPortToUse(). This covers
basic-security cache notifications, catalog sync, the sys server
properties fetch, Consul registration, Router->Broker selection and
shuffle partition locations.
- BaseDynamicConfigSyncer builds broker locations via
ServiceLocation.fromDruidNode().
- ThreadingTaskRunner and SingleTaskBackgroundRunner report task
locations on the advertised port.
- ForkingTaskRunner forces druid.advertisedPlaintextPort=-1 for forked
peons, since they listen on their own dynamic ports and must not
inherit the Middle Manager's advertised port.
- Document druid.advertisedPlaintextPort next to druid.plaintextPort.
* Report advertised port in Kubernetes task pod locations
Task pods launched by the Kubernetes task runner are reached by pod IP
from KubernetesPeonLifecycle.getTaskLocation(), not service discovery,
so druid.advertisedPlaintextPort never applies to them and the location
always used DruidK8sConstants.PORT.
- Add druid.indexer.runner.advertisedPlaintextPort to the k8s and
multik8s runner configs. When positive, task locations report it
instead of the peon's own port, e.g. a sidecar proxy in the task pod.
- Document the property and how it relates to druid.advertisedPlaintextPort.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
docs/configuration/index.md | 23 ++++
docs/development/extensions-core/k8s-jobs.md | 1 +
docs/querying/sql-metadata-tables.md | 2 +-
.../consul/discovery/ConsulServiceIdsTest.java | 6 +
.../k8s/overlord/KubernetesPeonLifecycle.java | 20 +++-
.../overlord/KubernetesPeonLifecycleFactory.java | 11 +-
.../k8s/overlord/KubernetesTaskRunnerConfig.java | 17 ++-
.../KubernetesTaskRunnerEffectiveConfig.java | 8 ++
.../k8s/overlord/KubernetesTaskRunnerFactory.java | 3 +-
.../overlord/KubernetesTaskRunnerStaticConfig.java | 17 ++-
.../MultipleKubernetesTaskRunnerConfig.java | 6 +-
.../MultipleKubernetesTaskRunnerFactory.java | 6 +-
.../k8s/overlord/KubernetesPeonLifecycleTest.java | 74 ++++++++++++
.../KubernetesTaskRunnerStaticConfigTest.java | 14 +++
.../MultipleKubernetesTaskRunnerConfigTest.java | 12 ++
.../druid/indexing/overlord/ForkingTaskRunner.java | 2 +
.../overlord/SingleTaskBackgroundRunner.java | 2 +-
.../indexing/overlord/ThreadingTaskRunner.java | 2 +-
.../indexing/overlord/ForkingTaskRunnerTest.java | 66 +++++++++++
.../overlord/SingleTaskBackgroundRunnerTest.java | 13 +++
.../indexing/overlord/ThreadingTaskRunnerTest.java | 50 +++++++++
.../java/org/apache/druid/rpc/ServiceLocation.java | 2 +-
.../java/org/apache/druid/server/DruidNode.java | 71 ++++++++++--
.../druid/server/http/BaseDynamicConfigSyncer.java | 7 +-
.../org/apache/druid/rpc/ServiceLocationTest.java | 18 +++
.../org/apache/druid/server/DruidNodeTest.java | 125 +++++++++++++++++++++
.../http/CoordinatorDynamicConfigSyncerTest.java | 42 ++++++-
.../router/TieredBrokerHostSelectorTest.java | 7 +-
28 files changed, 592 insertions(+), 35 deletions(-)
diff --git a/docs/configuration/index.md b/docs/configuration/index.md
index 4eff8506d41..f46f919e454 100644
--- a/docs/configuration/index.md
+++ b/docs/configuration/index.md
@@ -167,6 +167,22 @@ Druid interacts with ZooKeeper through a set of standard
path configurations. We
Although not recommended but both HTTP and HTTPS connectors can be enabled at
a time and respective ports are configurable using `druid.plaintextPort`
and `druid.tlsPort` properties on each service. Please see `Configuration`
section of individual services to check the valid and default values for these
ports.
+#### Advertised plaintext port
+
+By default, a service advertises the same plaintext port it listens on. Set
`druid.advertisedPlaintextPort` when other services must reach it on a
different port, for example when a sidecar proxy such as Envoy terminates mTLS
on one port and forwards traffic to Jetty on `druid.plaintextPort`.
+
+- `druid.plaintextPort` remains the port that Jetty binds to and listens on.
+- `druid.advertisedPlaintextPort` is the port that the service publishes
through service discovery and that other services use to reach it over
plaintext. This covers internal RPC between services, Router to Broker
proxying, dynamic configuration sync, lookup and security cache notifications,
catalog sync, `sys.server_properties`, task locations reported by Indexers, and
Consul registration.
+- If `druid.advertisedPlaintextPort` is unset or non-positive, it falls back
to `druid.plaintextPort`.
+- It only applies when `druid.enablePlaintextPort` is `true`. When
`druid.enableTlsPort` is `true`, services still prefer `druid.tlsPort` to reach
each other.
+- The `plaintext_port` column of `sys.servers` and the `plaintextPort` field
of the `/druid/coordinator/v1/cluster` API continue to report
`druid.plaintextPort`.
+
+You can't use a port in `druid.host` for this purpose, because a port in
`druid.host` must match `druid.plaintextPort`.
+
+Peons forked by a Middle Manager don't support
`druid.advertisedPlaintextPort`. Each Peon listens on its own port from
`druid.indexer.runner.startPort`, `druid.indexer.runner.endPort`, or
`druid.indexer.runner.ports`, so a single advertised port can't identify an
individual Peon. The Middle Manager always starts Peons with
`druid.advertisedPlaintextPort` unset, so each Peon advertises its own port.
This applies even if the property is inherited from the Middle Manager or set
through `dru [...]
+
+Task pods launched by the [Kubernetes task
runner](../development/extensions-core/k8s-jobs.md) aren't reached through
service discovery. The Overlord reports each task's location from the pod IP
and a fixed port, so `druid.advertisedPlaintextPort` has no effect on them. To
reach task pods through a sidecar proxy, set
[`druid.indexer.runner.advertisedPlaintextPort`](../development/extensions-core/k8s-jobs.md#properties)
on the Overlord instead.
+
#### Jetty server TLS configuration
Druid uses Jetty as an embedded web server. To learn more about TLS/SSL,
certificates, and related concepts in Jetty, including explanations of the
configuration settings below, see "Configuring SSL/TLS KeyStores" in the [Jetty
Operations Guide](https://www.eclipse.org/jetty/documentation.php).
@@ -718,6 +734,7 @@ These Coordinator static configurations can be defined in
the `coordinator/runti
|`druid.host`|The host for the current service. This is used to advertise the
current service location as reachable from another service and should generally
be specified such that `http://${druid.host}/` could actually talk to this
service.|`InetAddress.getLocalHost().getCanonicalHostName()`|
|`druid.bindOnHost`|Indicating whether the service's internal jetty server
bind on `druid.host`. Default is false, which means binding to all
interfaces.|false|
|`druid.plaintextPort`|This is the port to actually listen on; unless port
mapping is used, this will be the same port as is on `druid.host`|8081|
+|`druid.advertisedPlaintextPort`|Plaintext port that other services use to
reach this service, if different from `druid.plaintextPort`, for example when a
sidecar proxy listens in front of Jetty. Unset or non-positive values fall back
to `druid.plaintextPort`. See [Advertised plaintext
port](#advertised-plaintext-port).|`druid.plaintextPort`|
|`druid.tlsPort`|TLS port for HTTPS connector, if
[druid.enableTlsPort](../operations/tls-support.md) is set then this config
will be used. If `druid.host` contains port then that port will be ignored.
This should be a non-negative integer.|8281|
|`druid.service`|The name of the service. This is used as a dimension when
emitting metrics and alerts to differentiate between the various
services.|`druid/coordinator`|
|`druid.labels`|Optional JSON object of key-value pairs that define custom
labels for the server. These labels are displayed in the web console under the
"Services" tab. Example: `druid.labels={"location":"Airtrunk"}` or
`druid.labels.location=Airtrunk`|`null`|
@@ -967,6 +984,7 @@ These Overlord static configurations can be defined in the
`overlord/runtime.pro
|`druid.host`|The host for the current service. This is used to advertise the
current service location as reachable from another service and should generally
be specified such that `http://${druid.host}/` could actually talk to this
service.|`InetAddress.getLocalHost().getCanonicalHostName()`|
|`druid.bindOnHost`|Indicating whether the service's internal jetty server
bind on `druid.host`. Default is false, which means binding to all
interfaces.|false|
|`druid.plaintextPort`|This is the port to actually listen on; unless port
mapping is used, this will be the same port as is on `druid.host`.|8090|
+|`druid.advertisedPlaintextPort`|Plaintext port that other services use to
reach this service, if different from `druid.plaintextPort`, for example when a
sidecar proxy listens in front of Jetty. Unset or non-positive values fall back
to `druid.plaintextPort`. See [Advertised plaintext
port](#advertised-plaintext-port).|`druid.plaintextPort`|
|`druid.tlsPort`|TLS port for HTTPS connector, if
[druid.enableTlsPort](../operations/tls-support.md) is set then this config
will be used. If `druid.host` contains port then that port will be ignored.
This should be a non-negative Integer.|8290|
|`druid.service`|The name of the service. This is used as a dimension when
emitting metrics and alerts to differentiate between the various
services.|`druid/overlord`|
|`druid.labels`|Optional JSON object of key-value pairs that define custom
labels for the server. These labels are displayed in the web console under the
"Services" tab. Example: `druid.labels={"location":"Airtrunk"}` or
`druid.labels.location=Airtrunk`|`null`|
@@ -1319,6 +1337,7 @@ These Middle Manager and Peon configurations can be
defined in the `middleManage
|`druid.host`|The host for the current service. This is used to advertise the
current service location as reachable from another service and should generally
be specified such that `http://${druid.host}/` could actually talk to this
service|`InetAddress.getLocalHost().getCanonicalHostName()`|
|`druid.bindOnHost`|Indicating whether the service's internal jetty server
bind on `druid.host`. Default is false, which means binding to all
interfaces.|false|
|`druid.plaintextPort`|This is the port to actually listen on; unless port
mapping is used, this will be the same port as is on `druid.host`|8091|
+|`druid.advertisedPlaintextPort`|Plaintext port that other services use to
reach this service, if different from `druid.plaintextPort`, for example when a
sidecar proxy listens in front of Jetty. Unset or non-positive values fall back
to `druid.plaintextPort`. Ignored for Peons forked by a Middle Manager, which
always advertise their own port. See [Advertised plaintext
port](#advertised-plaintext-port).|`druid.plaintextPort`|
|`druid.tlsPort`|TLS port for HTTPS connector, if
[druid.enableTlsPort](../operations/tls-support.md) is set then this config
will be used. If `druid.host` contains port then that port will be ignored.
This should be a non-negative Integer.|8291|
|`druid.service`|The name of the service. This is used as a dimension when
emitting metrics and alerts to differentiate between the various
services|`druid/middlemanager`|
|`druid.labels`|Optional JSON object of key-value pairs that define custom
labels for the server. These labels are displayed in the web console under the
"Services" tab. Example: `druid.labels={"location":"Airtrunk"}` or
`druid.labels.location=Airtrunk`|`null`|
@@ -1446,6 +1465,7 @@ For most types of tasks, `SegmentWriteOutMediumFactory`
can be configured per-ta
|`druid.host`|The host for the current process. This is used to advertise the
current processes location as reachable from another process and should
generally be specified such that `http://${druid.host}/` could actually talk to
this process|`InetAddress.getLocalHost().getCanonicalHostName()`|
|`druid.bindOnHost`|Indicating whether the process's internal jetty server
bind on `druid.host`. Default is false, which means binding to all
interfaces.|false|
|`druid.plaintextPort`|This is the port to actually listen on; unless port
mapping is used, this will be the same port as is on `druid.host`|8091|
+|`druid.advertisedPlaintextPort`|Plaintext port that other services use to
reach this service, if different from `druid.plaintextPort`, for example when a
sidecar proxy listens in front of Jetty. Unset or non-positive values fall back
to `druid.plaintextPort`. See [Advertised plaintext
port](#advertised-plaintext-port).|`druid.plaintextPort`|
|`druid.tlsPort`|TLS port for HTTPS connector, if
[druid.enableTlsPort](../operations/tls-support.md) is set then this config
will be used. If `druid.host` contains port then that port will be ignored.
This should be a non-negative Integer.|8283|
|`druid.service`|The name of the service. This is used as a dimension when
emitting metrics and alerts to differentiate between the various
services|`druid/indexer`|
|`druid.labels`|Optional JSON object of key-value pairs that define custom
labels for the server. These labels are displayed in the web console under the
"Services" tab. Example: `druid.labels={"location":"Airtrunk"}` or
`druid.labels.location=Airtrunk`|`null`|
@@ -1544,6 +1564,7 @@ These Historical configurations can be defined in the
`historical/runtime.proper
|`druid.host`|The host for the current service. This is used to advertise the
current service location as reachable from another service and should generally
be specified such that `http://${druid.host}/` could actually talk to this
service|`InetAddress.getLocalHost().getCanonicalHostName()`|
|`druid.bindOnHost`|Indicating whether the service's internal jetty server
bind on `druid.host`. Default is false, which means binding to all
interfaces.|false|
|`druid.plaintextPort`|This is the port to actually listen on; unless port
mapping is used, this will be the same port as is on `druid.host`|8083|
+|`druid.advertisedPlaintextPort`|Plaintext port that other services use to
reach this service, if different from `druid.plaintextPort`, for example when a
sidecar proxy listens in front of Jetty. Unset or non-positive values fall back
to `druid.plaintextPort`. See [Advertised plaintext
port](#advertised-plaintext-port).|`druid.plaintextPort`|
|`druid.tlsPort`|TLS port for HTTPS connector, if
[druid.enableTlsPort](../operations/tls-support.md) is set then this config
will be used. If `druid.host` contains port then that port will be ignored.
This should be a non-negative Integer.|8283|
|`druid.service`|The name of the service. This is used as a dimension when
emitting metrics and alerts to differentiate between the various
services|`druid/historical`|
|`druid.labels`|Optional JSON object of key-value pairs that define custom
labels for the server. These labels are displayed in the web console under the
"Services" tab. Example: `druid.labels={"location":"Airtrunk"}` or
`druid.labels.location=Airtrunk`|`null`|
@@ -1665,6 +1686,7 @@ These Broker configurations can be defined in the
`broker/runtime.properties` fi
|`druid.host`|The host for the current process. This is used to advertise the
current processes location as reachable from another process and should
generally be specified such that `http://${druid.host}/` could actually talk to
this process|`InetAddress.getLocalHost().getCanonicalHostName()`|
|`druid.bindOnHost`|Indicating whether the process's internal jetty server
bind on `druid.host`. Default is false, which means binding to all
interfaces.|false|
|`druid.plaintextPort`|This is the port to actually listen on; unless port
mapping is used, this will be the same port as is on `druid.host`|8082|
+|`druid.advertisedPlaintextPort`|Plaintext port that other services use to
reach this service, if different from `druid.plaintextPort`, for example when a
sidecar proxy listens in front of Jetty. Unset or non-positive values fall back
to `druid.plaintextPort`. See [Advertised plaintext
port](#advertised-plaintext-port).|`druid.plaintextPort`|
|`druid.tlsPort`|TLS port for HTTPS connector, if
[druid.enableTlsPort](../operations/tls-support.md) is set then this config
will be used. If `druid.host` contains port then that port will be ignored.
This should be a non-negative Integer.|8282|
|`druid.service`|The name of the service. This is used as a dimension when
emitting metrics and alerts to differentiate between the various
services|`druid/broker`|
|`druid.labels`|Optional JSON object of key-value pairs that define custom
labels for the server. These labels are displayed in the web console under the
"Services" tab. Example: `druid.labels={"location":"Airtrunk"}` or
`druid.labels.location=Airtrunk`|`null`|
@@ -2333,6 +2355,7 @@ Supported query contexts:
|`druid.host`|The host for the current process. This is used to advertise the
current processes location as reachable from another process and should
generally be specified such that `http://${druid.host}/` could actually talk to
this process|`InetAddress.getLocalHost().getCanonicalHostName()`|
|`druid.bindOnHost`|Indicating whether the process's internal jetty server
bind on `druid.host`. Default is false, which means binding to all
interfaces.|false|
|`druid.plaintextPort`|This is the port to actually listen on; unless port
mapping is used, this will be the same port as is on `druid.host`|8888|
+|`druid.advertisedPlaintextPort`|Plaintext port that other services use to
reach this service, if different from `druid.plaintextPort`, for example when a
sidecar proxy listens in front of Jetty. Unset or non-positive values fall back
to `druid.plaintextPort`. See [Advertised plaintext
port](#advertised-plaintext-port).|`druid.plaintextPort`|
|`druid.tlsPort`|TLS port for HTTPS connector, if
[druid.enableTlsPort](../operations/tls-support.md) is set then this config
will be used. If `druid.host` contains port then that port will be ignored.
This should be a non-negative Integer.|9088|
|`druid.service`|The name of the service. This is used as a dimension when
emitting metrics and alerts to differentiate between the various
services|`druid/router`|
|`druid.labels`|Optional JSON object of key-value pairs that define custom
labels for the server. These labels are displayed in the web console under the
"Services" tab. Example: `druid.labels={"location":"Airtrunk"}` or
`druid.labels.location=Airtrunk`|`null`|
diff --git a/docs/development/extensions-core/k8s-jobs.md
b/docs/development/extensions-core/k8s-jobs.md
index 242d17a9924..2906ea75fa3 100644
--- a/docs/development/extensions-core/k8s-jobs.md
+++ b/docs/development/extensions-core/k8s-jobs.md
@@ -1033,6 +1033,7 @@ the Overlord pod from
`druid.indexer.runner.overlordNamespace` or `druid.indexer
| `druid.indexer.runner.logSaveTimeout` | `Duration` | The peon executing the
ingestion task makes a best effort to persist the pod logs from `k8s` to
persistent task log storage. The timeout ensures that `k8s` connection issues
do not cause the pod to hang indefinitely thereby blocking Overlord operations.
If the timeout occurs before the logs are saved, those logs will not be
available in Druid. | `PT300S` | NO |
| `druid.indexer.runner.useK8sSharedInformers` | `boolean` | Whether to use
shared informers to watch for pod/job changes. This is more efficient on the
Kubernetes API server, but may use more memory in the Overlord. | `false` | No |
| `druid.indexer.runner.k8sSharedInformerResyncPeriod` | `Duration` | When
using shared informers, controls how frequently the informers resync with the
Kubernetes API server. This prevents change events from being missed, keeping
the informer cache clean and accurate. | `PT300S` | No |
+| `druid.indexer.runner.advertisedPlaintextPort` | `Integer` | Plaintext port
reported in task locations, if other services must reach task pods on a port
other than the one the peon listens on, for example when a sidecar proxy in the
task pod listens in front of the peon. Task pods aren't reached through service
discovery, so `druid.advertisedPlaintextPort` doesn't apply to them. Unset or
non-positive values use the peon's own port. | `8100` | No |
| `druid.indexer.runner.clusterSelector.type` | `String` (`roundrobin`,
`random`, `leastTask`) | Only applicable when `druid.indexer.runner.type` is
`multik8s`. Strategy used to select which enabled Kubernetes cluster receives
the next task. | `roundrobin` | No |
| `druid.indexer.runner.clusters[N].name` | `String` | Only applicable when
`druid.indexer.runner.type` is `multik8s`. Human-readable name for cluster `N`.
When set, Druid adds this value to task context tags as `k8s_cluster`. | `null`
| No |
| `druid.indexer.runner.clusters[N].taskNamespace` | `String` | Only
applicable when `druid.indexer.runner.type` is `multik8s`. Kubernetes namespace
where task pods and jobs run for cluster `N`. | - | Yes |
diff --git a/docs/querying/sql-metadata-tables.md
b/docs/querying/sql-metadata-tables.md
index 055305d81e5..81d3f6bdb26 100644
--- a/docs/querying/sql-metadata-tables.md
+++ b/docs/querying/sql-metadata-tables.md
@@ -228,7 +228,7 @@ Servers table lists all discovered servers in the cluster.
|------|-----|-----|
|server|VARCHAR|Server name in the form host:port|
|host|VARCHAR|Hostname of the server|
-|plaintext_port|BIGINT|Unsecured port of the server, or -1 if plaintext
traffic is disabled|
+|plaintext_port|BIGINT|Unsecured port the server listens on
(`druid.plaintextPort`), or -1 if plaintext traffic is disabled. The port in
`server` may differ if
[`druid.advertisedPlaintextPort`](../configuration/index.md#advertised-plaintext-port)
is set.|
|tls_port|BIGINT|TLS port of the server, or -1 if TLS is disabled|
|server_type|VARCHAR|Type of Druid service. Possible values include:
COORDINATOR, OVERLORD, BROKER, ROUTER, HISTORICAL, MIDDLE_MANAGER or PEON.|
|tier|VARCHAR|Distribution tier see
[druid.server.tier](../configuration/index.md#historical-general-configuration).
Only valid for HISTORICAL type, for other types it's null|
diff --git
a/extensions-contrib/consul-extensions/src/test/java/org/apache/druid/consul/discovery/ConsulServiceIdsTest.java
b/extensions-contrib/consul-extensions/src/test/java/org/apache/druid/consul/discovery/ConsulServiceIdsTest.java
index aa41963f994..92d7c461bcc 100644
---
a/extensions-contrib/consul-extensions/src/test/java/org/apache/druid/consul/discovery/ConsulServiceIdsTest.java
+++
b/extensions-contrib/consul-extensions/src/test/java/org/apache/druid/consul/discovery/ConsulServiceIdsTest.java
@@ -59,5 +59,11 @@ public class ConsulServiceIdsTest
String kvKey = ConsulServiceIds.nodeKvKey(config, serviceId);
Assertions.assertEquals("druid/nodes/druid-peon-host-8080", kvKey);
+
+ DruidNode advertisedNode = new DruidNode("service", "host", false, 8080,
null, null, true, false, null, 9080);
+ Assertions.assertEquals(
+ "druid-peon-host-9080",
+ ConsulServiceIds.serviceId(config, new
DiscoveryDruidNode(advertisedNode, role, null))
+ );
}
}
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycle.java
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycle.java
index d15fa396323..70af7cbda27 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycle.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycle.java
@@ -100,6 +100,7 @@ public class KubernetesPeonLifecycle
private final TaskStateListener stateListener;
private final SettableFuture<Boolean> taskStartedSuccessfullyFuture;
private final long logSaveTimeoutMs;
+ private final int plaintextPort;
@MonotonicNonNull
private LogWatch logWatch;
@@ -116,6 +117,23 @@ public class KubernetesPeonLifecycle
long logSaveTimeoutMs
)
{
+ this(task, taskId, kubernetesClient, taskLogs, mapper, stateListener,
logSaveTimeoutMs, null);
+ }
+
+ protected KubernetesPeonLifecycle(
+ Task task,
+ K8sTaskId taskId,
+ KubernetesPeonClient kubernetesClient,
+ TaskLogs taskLogs,
+ ObjectMapper mapper,
+ TaskStateListener stateListener,
+ long logSaveTimeoutMs,
+ @Nullable Integer advertisedPlaintextPort
+ )
+ {
+ this.plaintextPort = advertisedPlaintextPort != null &&
advertisedPlaintextPort > 0
+ ? advertisedPlaintextPort
+ : DruidK8sConstants.PORT;
this.task = task;
this.taskId = taskId;
this.kubernetesClient = kubernetesClient;
@@ -300,7 +318,7 @@ public class KubernetesPeonLifecycle
}
taskLocationRef.set(TaskLocation.create(
podStatus.getPodIP(),
- DruidK8sConstants.PORT,
+ plaintextPort,
DruidK8sConstants.TLS_PORT,
Boolean.parseBoolean(pod.getMetadata().getAnnotations().getOrDefault(DruidK8sConstants.TLS_ENABLED,
"false")),
pod.getMetadata() != null ? pod.getMetadata().getName() : ""
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycleFactory.java
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycleFactory.java
index 8bd7db2ebf3..30a78961f92 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycleFactory.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycleFactory.java
@@ -25,24 +25,30 @@ import org.apache.druid.k8s.overlord.common.K8sTaskId;
import org.apache.druid.k8s.overlord.common.KubernetesPeonClient;
import org.apache.druid.tasklogs.TaskLogs;
+import javax.annotation.Nullable;
+
public class KubernetesPeonLifecycleFactory implements PeonLifecycleFactory
{
private final KubernetesPeonClient client;
private final TaskLogs taskLogs;
private final ObjectMapper mapper;
private final long logSaveTimeoutMs;
+ @Nullable
+ private final Integer advertisedPlaintextPort;
public KubernetesPeonLifecycleFactory(
KubernetesPeonClient client,
TaskLogs taskLogs,
ObjectMapper mapper,
- long logSaveTimeoutMs
+ long logSaveTimeoutMs,
+ @Nullable Integer advertisedPlaintextPort
)
{
this.client = client;
this.taskLogs = taskLogs;
this.mapper = mapper;
this.logSaveTimeoutMs = logSaveTimeoutMs;
+ this.advertisedPlaintextPort = advertisedPlaintextPort;
}
@Override
@@ -55,7 +61,8 @@ public class KubernetesPeonLifecycleFactory implements
PeonLifecycleFactory
taskLogs,
mapper,
stateListener,
- logSaveTimeoutMs
+ logSaveTimeoutMs,
+ advertisedPlaintextPort
);
}
}
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerConfig.java
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerConfig.java
index d164db06298..84c516d8daf 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerConfig.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerConfig.java
@@ -23,6 +23,7 @@ import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import org.joda.time.Period;
+import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
@@ -96,6 +97,12 @@ public interface KubernetesTaskRunnerConfig
*/
boolean isAllowTaskPodTemplateSelection();
+ /**
+ * Plaintext port reported in task pod {@code TaskLocation}s, e.g. a sidecar
proxy port; null uses {@code DruidK8sConstants.PORT}.
+ */
+ @Nullable
+ Integer getAdvertisedPlaintextPort();
+
static Builder builder()
{
return new Builder();
@@ -127,6 +134,7 @@ public interface KubernetesTaskRunnerConfig
private boolean useK8sSharedInformers;
private Period k8sSharedInformerResyncPeriod;
private boolean allowTaskPodTemplateSelection;
+ private Integer advertisedPlaintextPort;
public Builder()
{
@@ -277,6 +285,12 @@ public interface KubernetesTaskRunnerConfig
return this;
}
+ public Builder withAdvertisedPlaintextPort(Integer advertisedPlaintextPort)
+ {
+ this.advertisedPlaintextPort = advertisedPlaintextPort;
+ return this;
+ }
+
public KubernetesTaskRunnerStaticConfig build()
{
return new KubernetesTaskRunnerStaticConfig(
@@ -303,7 +317,8 @@ public interface KubernetesTaskRunnerConfig
this.taskJoinTimeout,
this.useK8sSharedInformers,
this.k8sSharedInformerResyncPeriod,
- this.allowTaskPodTemplateSelection
+ this.allowTaskPodTemplateSelection,
+ this.advertisedPlaintextPort
);
}
}
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java
index 227b78d7e84..068670286d7 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerEffectiveConfig.java
@@ -24,6 +24,7 @@ import
org.apache.druid.k8s.overlord.execution.KubernetesTaskRunnerDynamicConfig
import org.apache.druid.k8s.overlord.execution.PodTemplateSelectStrategy;
import org.joda.time.Period;
+import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
@@ -195,6 +196,13 @@ public class KubernetesTaskRunnerEffectiveConfig
implements KubernetesTaskRunner
return staticConfig.isAllowTaskPodTemplateSelection();
}
+ @Override
+ @Nullable
+ public Integer getAdvertisedPlaintextPort()
+ {
+ return staticConfig.getAdvertisedPlaintextPort();
+ }
+
public PodTemplateSelectStrategy getPodTemplateSelectStrategy()
{
if (dynamicConfigSupplier == null || dynamicConfigSupplier.get() == null
|| dynamicConfigSupplier.get().getPodTemplateSelectStrategy() == null) {
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerFactory.java
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerFactory.java
index 325f16eed72..0a753f6b213 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerFactory.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerFactory.java
@@ -111,7 +111,8 @@ public class KubernetesTaskRunnerFactory implements
TaskRunnerFactory<Kubernetes
peonClient,
taskLogs,
smileMapper,
-
kubernetesTaskRunnerConfig.getLogSaveTimeout().toStandardDuration().getMillis()
+
kubernetesTaskRunnerConfig.getLogSaveTimeout().toStandardDuration().getMillis(),
+ kubernetesTaskRunnerConfig.getAdvertisedPlaintextPort()
),
emitter,
configManager
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerStaticConfig.java
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerStaticConfig.java
index b8ab038ca94..97cfe3f969c 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerStaticConfig.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerStaticConfig.java
@@ -29,6 +29,7 @@ import org.apache.commons.lang3.ObjectUtils;
import org.joda.time.Period;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
@@ -156,6 +157,11 @@ public class KubernetesTaskRunnerStaticConfig implements
KubernetesTaskRunnerCon
// Allow per-task pod template selection via the task context.
private boolean allowTaskPodTemplateSelection = false;
+ @JsonProperty
+ @Max(0xffff)
+ // task pods are reached by pod IP, not service discovery, so
druid.advertisedPlaintextPort can't apply to them
+ private Integer advertisedPlaintextPort = null;
+
public KubernetesTaskRunnerStaticConfig()
{
}
@@ -184,7 +190,8 @@ public class KubernetesTaskRunnerStaticConfig implements
KubernetesTaskRunnerCon
Period taskJoinTimeout,
boolean useK8sSharedInformers,
Period k8sSharedInformerResyncPeriod,
- boolean allowTaskPodTemplateSelection
+ boolean allowTaskPodTemplateSelection,
+ @Nullable Integer advertisedPlaintextPort
)
{
this.namespace = namespace;
@@ -274,6 +281,7 @@ public class KubernetesTaskRunnerStaticConfig implements
KubernetesTaskRunnerCon
allowTaskPodTemplateSelection,
this.allowTaskPodTemplateSelection
);
+ this.advertisedPlaintextPort = advertisedPlaintextPort;
}
@Override
@@ -421,4 +429,11 @@ public class KubernetesTaskRunnerStaticConfig implements
KubernetesTaskRunnerCon
{
return allowTaskPodTemplateSelection;
}
+
+ @Override
+ @Nullable
+ public Integer getAdvertisedPlaintextPort()
+ {
+ return advertisedPlaintextPort;
+ }
}
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerConfig.java
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerConfig.java
index e6da1cb39fe..38d8c0a6624 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerConfig.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerConfig.java
@@ -148,7 +148,8 @@ public class MultipleKubernetesTaskRunnerConfig extends
KubernetesTaskRunnerStat
@JsonProperty("taskJoinTimeout") Period taskJoinTimeout,
@JsonProperty("useK8sSharedInformers") Boolean useK8sSharedInformers,
@JsonProperty("k8sSharedInformerResyncPeriod") Period
k8sSharedInformerResyncPeriod,
- @JsonProperty("allowTaskPodTemplateSelection") Boolean
allowTaskPodTemplateSelection
+ @JsonProperty("allowTaskPodTemplateSelection") Boolean
allowTaskPodTemplateSelection,
+ @JsonProperty("advertisedPlaintextPort") Integer advertisedPlaintextPort
)
{
super(
@@ -175,7 +176,8 @@ public class MultipleKubernetesTaskRunnerConfig extends
KubernetesTaskRunnerStat
taskJoinTimeout,
useK8sSharedInformers != null && useK8sSharedInformers,
k8sSharedInformerResyncPeriod,
- allowTaskPodTemplateSelection != null && allowTaskPodTemplateSelection
+ allowTaskPodTemplateSelection != null && allowTaskPodTemplateSelection,
+ advertisedPlaintextPort
);
Preconditions.checkNotNull(clusters);
Preconditions.checkState(
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerFactory.java
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerFactory.java
index 46a9008a986..e90c3bd84ae 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerFactory.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/main/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerFactory.java
@@ -179,7 +179,8 @@ public class MultipleKubernetesTaskRunnerFactory implements
TaskRunnerFactory<Ta
peonClient,
taskLogs,
smileMapper,
-
effectiveConfig.getLogSaveTimeout().toStandardDuration().getMillis()
+
effectiveConfig.getLogSaveTimeout().toStandardDuration().getMillis(),
+ effectiveConfig.getAdvertisedPlaintextPort()
),
emitter,
sharedExecutor,
@@ -467,7 +468,8 @@ public class MultipleKubernetesTaskRunnerFactory implements
TaskRunnerFactory<Ta
this.runnerConfig.getTaskJoinTimeout(),
this.runnerConfig.isUseK8sSharedInformers(),
this.runnerConfig.getK8sSharedInformerResyncPeriod(),
- this.runnerConfig.isAllowTaskPodTemplateSelection()
+ this.runnerConfig.isAllowTaskPodTemplateSelection(),
+ this.runnerConfig.getAdvertisedPlaintextPort()
);
}
}
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycleTest.java
b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycleTest.java
index 7df86556bb4..ba8806928c8 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycleTest.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesPeonLifecycleTest.java
@@ -31,6 +31,7 @@ import org.apache.druid.indexer.TaskLocation;
import org.apache.druid.indexer.TaskStatus;
import org.apache.druid.indexing.common.TestUtils;
import org.apache.druid.indexing.common.task.Task;
+import org.apache.druid.k8s.overlord.common.DruidK8sConstants;
import org.apache.druid.k8s.overlord.common.JobResponse;
import org.apache.druid.k8s.overlord.common.K8sTaskId;
import org.apache.druid.k8s.overlord.common.K8sTestUtils;
@@ -956,6 +957,79 @@ public class KubernetesPeonLifecycleTest extends
EasyMockSupport
verifyAll();
}
+ @Test
+ public void
test_getTaskLocation_withAdvertisedPlaintextPort_returnsAdvertisedPort()
+ throws NoSuchFieldException, IllegalAccessException
+ {
+ KubernetesPeonLifecycle peonLifecycle = new KubernetesPeonLifecycle(
+ task,
+ k8sTaskId,
+ kubernetesClient,
+ taskLogs,
+ mapper,
+ stateListener,
+ LOG_SAVE_TIMEOUT.toStandardDuration().getMillis(),
+ 9443
+ );
+ setPeonLifecycleState(peonLifecycle,
KubernetesPeonLifecycle.State.RUNNING);
+
+ Pod pod = new PodBuilder()
+ .withNewMetadata()
+ .withName(ID)
+ .endMetadata()
+ .withNewStatus()
+ .withPodIP("ip")
+ .endStatus()
+ .build();
+
+
EasyMock.expect(kubernetesClient.getPeonPod(k8sTaskId.getK8sJobName())).andReturn(Optional.of(pod));
+
+ replayAll();
+
+ TaskLocation location = peonLifecycle.getTaskLocation();
+
+ Assertions.assertEquals("ip", location.getHost());
+ Assertions.assertEquals(9443, location.getPort());
+ Assertions.assertEquals(-1, location.getTlsPort());
+ Assertions.assertEquals(ID, location.getK8sPodName());
+
+ verifyAll();
+ }
+
+ @Test
+ public void
test_getTaskLocation_withNonPositiveAdvertisedPlaintextPort_returnsDefaultPort()
+ throws NoSuchFieldException, IllegalAccessException
+ {
+ KubernetesPeonLifecycle peonLifecycle = new KubernetesPeonLifecycle(
+ task,
+ k8sTaskId,
+ kubernetesClient,
+ taskLogs,
+ mapper,
+ stateListener,
+ LOG_SAVE_TIMEOUT.toStandardDuration().getMillis(),
+ -1
+ );
+ setPeonLifecycleState(peonLifecycle,
KubernetesPeonLifecycle.State.RUNNING);
+
+ Pod pod = new PodBuilder()
+ .withNewMetadata()
+ .withName(ID)
+ .endMetadata()
+ .withNewStatus()
+ .withPodIP("ip")
+ .endStatus()
+ .build();
+
+
EasyMock.expect(kubernetesClient.getPeonPod(k8sTaskId.getK8sJobName())).andReturn(Optional.of(pod));
+
+ replayAll();
+
+ Assertions.assertEquals(DruidK8sConstants.PORT,
peonLifecycle.getTaskLocation().getPort());
+
+ verifyAll();
+ }
+
@Test
public void test_getTaskLocation_saveTaskLocation()
throws NoSuchFieldException, IllegalAccessException
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerStaticConfigTest.java
b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerStaticConfigTest.java
index 7c0338d1db6..9754da7653c 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerStaticConfigTest.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/KubernetesTaskRunnerStaticConfigTest.java
@@ -80,6 +80,18 @@ public class KubernetesTaskRunnerStaticConfigTest
Assertions.assertEquals(ImmutableMap.of(), config.getLabels());
Assertions.assertEquals(ImmutableMap.of(), config.getAnnotations());
Assertions.assertEquals(Integer.valueOf(Integer.MAX_VALUE),
config.getCapacity());
+ Assertions.assertNull(config.getAdvertisedPlaintextPort());
+ }
+
+ @Test
+ public void test_deserializable_withAdvertisedPlaintextPort() throws
IOException
+ {
+ KubernetesTaskRunnerStaticConfig config = new
DefaultObjectMapper().readValue(
+ "{\"namespace\": \"namespace\", \"advertisedPlaintextPort\": 9443}",
+ KubernetesTaskRunnerStaticConfig.class
+ );
+
+ Assertions.assertEquals(Integer.valueOf(9443),
config.getAdvertisedPlaintextPort());
}
@Test
@@ -102,6 +114,7 @@ public class KubernetesTaskRunnerStaticConfigTest
.withLabels(ImmutableMap.of("key", "value"))
.withAnnotations(ImmutableMap.of("key", "value"))
.withCapacity(1)
+ .withAdvertisedPlaintextPort(9443)
.build();
Assertions.assertEquals("namespace", config.getNamespace());
@@ -119,5 +132,6 @@ public class KubernetesTaskRunnerStaticConfigTest
Assertions.assertEquals(ImmutableMap.of("key", "value"),
config.getLabels());
Assertions.assertEquals(ImmutableMap.of("key", "value"),
config.getAnnotations());
Assertions.assertEquals(Integer.valueOf(1), config.getCapacity());
+ Assertions.assertEquals(Integer.valueOf(9443),
config.getAdvertisedPlaintextPort());
}
}
diff --git
a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerConfigTest.java
b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerConfigTest.java
index 3dcfeb610d8..d7f0317a239 100644
---
a/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerConfigTest.java
+++
b/extensions-core/kubernetes-overlord-extensions/src/test/java/org/apache/druid/k8s/overlord/MultipleKubernetesTaskRunnerConfigTest.java
@@ -220,6 +220,18 @@ public class MultipleKubernetesTaskRunnerConfigTest
Assertions.assertTrue(config.isAllowTaskPodTemplateSelection());
}
+ @Test
+ public void test_fromProperties_withAdvertisedPlaintextPort()
+ {
+ final Properties props = new Properties();
+ props.setProperty("druid.indexer.runner.clusters[0].taskNamespace",
"namespace1");
+ props.setProperty("druid.indexer.runner.advertisedPlaintextPort", "9443");
+
+ final MultipleKubernetesTaskRunnerConfig config =
MultipleKubernetesTaskRunnerConfig.fromProperties(props);
+
+ Assertions.assertEquals(Integer.valueOf(9443),
config.getAdvertisedPlaintextPort());
+ }
+
@Test
public void
test_fromProperties_withoutSelectionStrategy_defaultsToRoundRobin()
{
diff --git
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java
index 606f5152063..01b480a0516 100644
---
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java
+++
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ForkingTaskRunner.java
@@ -355,6 +355,8 @@ public class ForkingTaskRunner
command.addSystemProperty("druid.host", childHost);
command.addSystemProperty("druid.plaintextPort",
childPort);
+ // Forked peons listen on their own port; never let
them advertise an inherited one.
+
command.addSystemProperty("druid.advertisedPlaintextPort", -1);
command.addSystemProperty("druid.tlsPort",
tlsChildPort);
// Let tasks know where they are running on.
diff --git
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/SingleTaskBackgroundRunner.java
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/SingleTaskBackgroundRunner.java
index 361c431c762..8e0ed1c33cb 100644
---
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/SingleTaskBackgroundRunner.java
+++
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/SingleTaskBackgroundRunner.java
@@ -98,7 +98,7 @@ public class SingleTaskBackgroundRunner implements
TaskRunner, QuerySegmentWalke
this.toolboxFactory = Preconditions.checkNotNull(toolboxFactory,
"toolboxFactory");
this.taskConfig = taskConfig;
this.emitter = Preconditions.checkNotNull(emitter, "emitter");
- this.location = TaskLocation.create(node.getHost(),
node.getPlaintextPort(), node.getTlsPort());
+ this.location = TaskLocation.create(node.getHost(),
node.getAdvertisedPlaintextPort(), node.getTlsPort());
this.serverConfig = serverConfig;
}
diff --git
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ThreadingTaskRunner.java
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ThreadingTaskRunner.java
index 999e36a3dd3..b057e0409ef 100644
---
a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ThreadingTaskRunner.java
+++
b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/ThreadingTaskRunner.java
@@ -182,7 +182,7 @@ public class ThreadingTaskRunner
final TaskLocation taskLocation =
TaskLocation.create(
node.getHost(),
- node.getPlaintextPort(),
+ node.getAdvertisedPlaintextPort(),
node.getTlsPort()
);
diff --git
a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/ForkingTaskRunnerTest.java
b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/ForkingTaskRunnerTest.java
index 51ffe8ac337..26b5a50ff97 100644
---
a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/ForkingTaskRunnerTest.java
+++
b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/ForkingTaskRunnerTest.java
@@ -67,10 +67,12 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.List;
+import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -528,6 +530,70 @@ public class ForkingTaskRunnerTest
Assertions.assertTrue(xmxJavaOptsIndex.get() >= 0);
}
+ @Test
+ public void testChildNeverInheritsAdvertisedPlaintextPort() throws
ExecutionException, InterruptedException
+ {
+ final Properties props = new Properties();
+ props.setProperty("druid.advertisedPlaintextPort", "9443");
+
props.setProperty("druid.indexer.fork.property.druid.advertisedPlaintextPort",
"9444");
+ final Task task = new NoopTask(
+ null,
+ null,
+ null,
+ 1L,
+ 0L,
+ Map.of("druid.indexer.fork.property.druid.advertisedPlaintextPort",
9445)
+ );
+ final AtomicReference<List<String>> childCommand = new AtomicReference<>();
+ final TaskConfig taskConfig = makeDefaultTaskConfigBuilder().build();
+ final WorkerConfig workerConfig = new WorkerConfig();
+ final ForkingTaskRunner forkingTaskRunner = new ForkingTaskRunner(
+ new ForkingTaskRunnerConfig(),
+ taskConfig,
+ workerConfig,
+ props,
+ new NoopTaskLogs(),
+ OBJECT_MAPPER,
+ new DruidNode("middleManager", "host", false, 8091, null, null, true,
false, null, 9443),
+ new StartupLoggingConfig(),
+ TaskStorageDirTracker.fromConfigs(workerConfig, taskConfig)
+ )
+ {
+ @Override
+ ProcessHolder runTaskProcess(List<String> command, File logFile,
TaskLocation taskLocation)
+ {
+ childCommand.set(command);
+ return makeTestProcessHolder(logFile, taskLocation);
+ }
+
+ @Override
+ int waitForTaskProcessToComplete(Task task, ProcessHolder processHolder,
File logFile, File reportsFile)
+ {
+ return 1;
+ }
+ };
+
+ forkingTaskRunner.setNumProcessorsPerTask();
+ forkingTaskRunner.run(task).get();
+
+ final List<String> advertisedArgs = childCommand.get()
+ .stream()
+ .filter(arg ->
arg.startsWith("-Ddruid.advertisedPlaintextPort="))
+
.collect(Collectors.toList());
+ Assertions.assertTrue(
+ advertisedArgs.containsAll(
+ List.of(
+ "-Ddruid.advertisedPlaintextPort=9443",
+ "-Ddruid.advertisedPlaintextPort=9444",
+ "-Ddruid.advertisedPlaintextPort=9445"
+ )
+ ),
+ advertisedArgs.toString()
+ );
+ // The JVM keeps the last -D value, so the forced -1 must come after every
inherited or overridden value.
+ Assertions.assertEquals("-Ddruid.advertisedPlaintextPort=-1",
advertisedArgs.get(advertisedArgs.size() - 1));
+ }
+
@Test
public void testInvalidTaskContextJavaOptsArray() throws
JsonProcessingException
{
diff --git
a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/SingleTaskBackgroundRunnerTest.java
b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/SingleTaskBackgroundRunnerTest.java
index 0a73e82ad31..f44f5b194e0 100644
---
a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/SingleTaskBackgroundRunnerTest.java
+++
b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/SingleTaskBackgroundRunnerTest.java
@@ -159,6 +159,19 @@ public class SingleTaskBackgroundRunnerTest
runner.stop();
}
+ @Test
+ public void testTaskLocationUsesAdvertisedPlaintextPort()
+ {
+ final SingleTaskBackgroundRunner advertisedRunner = new
SingleTaskBackgroundRunner(
+ EasyMock.createMock(TaskToolboxFactory.class),
+ new TaskConfigBuilder().build(),
+ new NoopServiceEmitter(),
+ new DruidNode("testServer", "testHost", false, 1000, null, null, true,
false, null, 2000),
+ new ServerConfig()
+ );
+ Assertions.assertEquals(TaskLocation.create("testHost", 2000, -1),
advertisedRunner.getTaskLocation("any"));
+ }
+
@Test
public void testRun() throws ExecutionException, InterruptedException
{
diff --git
a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/ThreadingTaskRunnerTest.java
b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/ThreadingTaskRunnerTest.java
index 968051d9339..c9fc2c40d99 100644
---
a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/ThreadingTaskRunnerTest.java
+++
b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/ThreadingTaskRunnerTest.java
@@ -21,6 +21,7 @@ package org.apache.druid.indexing.overlord;
import com.google.common.base.Optional;
import org.apache.commons.io.IOUtils;
+import org.apache.druid.indexer.TaskLocation;
import org.apache.druid.indexer.TaskState;
import org.apache.druid.indexer.TaskStatus;
import org.apache.druid.indexing.common.MultipleFileTaskReportFileWriter;
@@ -34,6 +35,7 @@ import
org.apache.druid.indexing.common.task.TestAppenderatorsManager;
import org.apache.druid.indexing.worker.config.WorkerConfig;
import org.apache.druid.jackson.DefaultObjectMapper;
import org.apache.druid.java.util.common.StringUtils;
+import org.apache.druid.java.util.common.concurrent.Execs;
import org.apache.druid.java.util.common.logger.Logger;
import org.apache.druid.server.DruidNode;
import org.apache.druid.tasklogs.NoopTaskLogs;
@@ -43,7 +45,9 @@ import org.junit.jupiter.api.Test;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -141,6 +145,52 @@ public class ThreadingTaskRunnerTest
);
}
+ @Test
+ public void testTaskLocationUsesAdvertisedPlaintextPort() throws Exception
+ {
+ final TaskConfig taskConfig =
ForkingTaskRunnerTest.makeDefaultTaskConfigBuilder().build();
+ final WorkerConfig workerConfig = new WorkerConfig();
+ runner = new ThreadingTaskRunner(
+ mockTaskToolboxFactory(),
+ taskConfig,
+ workerConfig,
+ new NoopTaskLogs(),
+ new DefaultObjectMapper(),
+ new TestAppenderatorsManager(),
+ new MultipleFileTaskReportFileWriter(),
+ new DruidNode("druid/indexer", "host", false, 8091, null, null, true,
false, null, 9091),
+ TaskStorageDirTracker.fromConfigs(workerConfig, taskConfig)
+ );
+
+ final List<TaskLocation> locations = new CopyOnWriteArrayList<>();
+ runner.registerListener(
+ new TaskRunnerListener()
+ {
+ @Override
+ public String getListenerId()
+ {
+ return "testTaskLocationUsesAdvertisedPlaintextPort";
+ }
+
+ @Override
+ public void locationChanged(String taskId, TaskLocation newLocation)
+ {
+ locations.add(newLocation);
+ }
+
+ @Override
+ public void statusChanged(String taskId, TaskStatus status)
+ {
+ }
+ },
+ Execs.directExecutor()
+ );
+
+ final TaskStatus status = runner.run(new NoopTask(null, null, null, 1L,
0L, Map.of())).get();
+ Assertions.assertEquals(TaskState.SUCCESS, status.getStatusCode());
+ Assertions.assertEquals(List.of(TaskLocation.create("host", 9091, -1)),
locations);
+ }
+
private static TaskToolboxFactory mockTaskToolboxFactory()
{
return new TestTaskToolboxFactory(new TestTaskToolboxFactory.Builder());
diff --git a/server/src/main/java/org/apache/druid/rpc/ServiceLocation.java
b/server/src/main/java/org/apache/druid/rpc/ServiceLocation.java
index ab588a0736e..971e6554d02 100644
--- a/server/src/main/java/org/apache/druid/rpc/ServiceLocation.java
+++ b/server/src/main/java/org/apache/druid/rpc/ServiceLocation.java
@@ -73,7 +73,7 @@ public class ServiceLocation
*/
public static ServiceLocation fromDruidNode(final DruidNode druidNode)
{
- return new ServiceLocation(druidNode.getHost(),
druidNode.getPlaintextPort(), druidNode.getTlsPort(), "");
+ return new ServiceLocation(druidNode.getHost(),
druidNode.getAdvertisedPlaintextPort(), druidNode.getTlsPort(), "");
}
/**
diff --git a/server/src/main/java/org/apache/druid/server/DruidNode.java
b/server/src/main/java/org/apache/druid/server/DruidNode.java
index f76edcfffcc..e3f7f0c464d 100644
--- a/server/src/main/java/org/apache/druid/server/DruidNode.java
+++ b/server/src/main/java/org/apache/druid/server/DruidNode.java
@@ -100,6 +100,10 @@ public class DruidNode
@JsonProperty
private Map<String, String> labels;
+ @JsonProperty
+ @Max(0xffff)
+ private int advertisedPlaintextPort = -1;
+
public DruidNode(
String serviceName,
String host,
@@ -110,7 +114,36 @@ public class DruidNode
boolean enableTlsPort
)
{
- this(serviceName, host, bindOnHost, plaintextPort, null, tlsPort,
enablePlaintextPort, enableTlsPort, null);
+ this(serviceName, host, bindOnHost, plaintextPort, null, tlsPort,
enablePlaintextPort, enableTlsPort, null, null);
+ }
+
+ public DruidNode(
+ String serviceName,
+ String host,
+ boolean bindOnHost,
+ Integer plaintextPort,
+ Integer port,
+ Integer tlsPort,
+ Boolean enablePlaintextPort,
+ boolean enableTlsPort
+ )
+ {
+ this(serviceName, host, bindOnHost, plaintextPort, port, tlsPort,
enablePlaintextPort, enableTlsPort, null, null);
+ }
+
+ public DruidNode(
+ String serviceName,
+ String host,
+ boolean bindOnHost,
+ Integer plaintextPort,
+ Integer port,
+ Integer tlsPort,
+ Boolean enablePlaintextPort,
+ boolean enableTlsPort,
+ Map<String, String> labels
+ )
+ {
+ this(serviceName, host, bindOnHost, plaintextPort, port, tlsPort,
enablePlaintextPort, enableTlsPort, labels, null);
}
/**
@@ -139,7 +172,8 @@ public class DruidNode
@JacksonInject(useInput = OptBoolean.TRUE) @Named("tlsServicePort")
@JsonProperty("tlsPort") Integer tlsPort,
@JsonProperty("enablePlaintextPort") Boolean enablePlaintextPort,
@JsonProperty("enableTlsPort") boolean enableTlsPort,
- @JsonProperty("labels") @Nullable Map<String, String> labels
+ @JsonProperty("labels") @Nullable Map<String, String> labels,
+ @JsonProperty("advertisedPlaintextPort") Integer advertisedPlaintextPort
)
{
init(
@@ -150,7 +184,8 @@ public class DruidNode
tlsPort,
enablePlaintextPort == null || enablePlaintextPort.booleanValue(),
enableTlsPort,
- labels
+ labels,
+ advertisedPlaintextPort
);
}
@@ -162,7 +197,8 @@ public class DruidNode
Integer tlsPort,
boolean enablePlaintextPort,
boolean enableTlsPort,
- Map<String, String> labels
+ Map<String, String> labels,
+ Integer advertisedPlaintextPort
)
{
Preconditions.checkNotNull(serviceName);
@@ -210,8 +246,12 @@ public class DruidNode
}
}
this.plaintextPort = plainTextPort;
+ this.advertisedPlaintextPort = advertisedPlaintextPort != null &&
advertisedPlaintextPort > 0
+ ? advertisedPlaintextPort
+ : this.plaintextPort;
} else {
this.plaintextPort = -1;
+ this.advertisedPlaintextPort = -1;
}
if (enableTlsPort) {
this.tlsPort = tlsPort;
@@ -246,6 +286,9 @@ public class DruidNode
return bindOnHost;
}
+ /**
+ * Port Jetty binds to. Peers should use {@link
#getAdvertisedPlaintextPort()} or {@link #getPortToUse()}.
+ */
public int getPlaintextPort()
{
return plaintextPort;
@@ -276,9 +319,17 @@ public class DruidNode
return buildRevision;
}
+ /**
+ * Plaintext port peers use to reach this node; falls back to {@link
#getPlaintextPort()} when unset.
+ */
+ public int getAdvertisedPlaintextPort()
+ {
+ return advertisedPlaintextPort;
+ }
+
public DruidNode withService(String service)
{
- return new DruidNode(service, host, bindOnHost, plaintextPort, tlsPort,
enablePlaintextPort, enableTlsPort);
+ return new DruidNode(service, host, bindOnHost, plaintextPort, null,
tlsPort, enablePlaintextPort, enableTlsPort, labels, advertisedPlaintextPort);
}
public String getServiceScheme()
@@ -292,10 +343,10 @@ public class DruidNode
public String getHostAndPort()
{
if (enablePlaintextPort) {
- if (plaintextPort < 0) {
+ if (advertisedPlaintextPort < 0) {
return HostAndPort.fromString(host).toString();
} else {
- return HostAndPort.fromParts(host, plaintextPort).toString();
+ return HostAndPort.fromParts(host, advertisedPlaintextPort).toString();
}
}
return null;
@@ -314,7 +365,7 @@ public class DruidNode
if (enableTlsPort) {
return getTlsPort();
} else {
- return getPlaintextPort();
+ return getAdvertisedPlaintextPort();
}
}
@@ -359,6 +410,7 @@ public class DruidNode
enablePlaintextPort == druidNode.enablePlaintextPort &&
tlsPort == druidNode.tlsPort &&
enableTlsPort == druidNode.enableTlsPort &&
+ advertisedPlaintextPort == druidNode.advertisedPlaintextPort &&
Objects.equals(serviceName, druidNode.serviceName) &&
Objects.equals(host, druidNode.host) &&
Objects.equals(labels, druidNode.labels);
@@ -367,7 +419,7 @@ public class DruidNode
@Override
public int hashCode()
{
- return Objects.hash(serviceName, host, port, plaintextPort,
enablePlaintextPort, tlsPort, enableTlsPort, labels);
+ return Objects.hash(serviceName, host, port, plaintextPort,
enablePlaintextPort, tlsPort, enableTlsPort, labels, advertisedPlaintextPort);
}
@Override
@@ -379,6 +431,7 @@ public class DruidNode
", bindOnHost=" + bindOnHost +
", port=" + port +
", plaintextPort=" + plaintextPort +
+ ", advertisedPlaintextPort=" + advertisedPlaintextPort +
", enablePlaintextPort=" + enablePlaintextPort +
", tlsPort=" + tlsPort +
", enableTlsPort=" + enableTlsPort +
diff --git
a/server/src/main/java/org/apache/druid/server/http/BaseDynamicConfigSyncer.java
b/server/src/main/java/org/apache/druid/server/http/BaseDynamicConfigSyncer.java
index 9c74298e34e..e863db2d37d 100644
---
a/server/src/main/java/org/apache/druid/server/http/BaseDynamicConfigSyncer.java
+++
b/server/src/main/java/org/apache/druid/server/http/BaseDynamicConfigSyncer.java
@@ -262,12 +262,7 @@ public abstract class
BaseDynamicConfigSyncer<DynamicConfig>
return null;
}
- return new ServiceLocation(
- druidNode.getHost(),
- druidNode.getPlaintextPort(),
- druidNode.getTlsPort(),
- ""
- );
+ return ServiceLocation.fromDruidNode(druidNode);
}
private void emitStat(CoordinatorStat stat, RowKey rowKey, long value)
diff --git a/server/src/test/java/org/apache/druid/rpc/ServiceLocationTest.java
b/server/src/test/java/org/apache/druid/rpc/ServiceLocationTest.java
index b5b8af06830..242a1ed363d 100644
--- a/server/src/test/java/org/apache/druid/rpc/ServiceLocationTest.java
+++ b/server/src/test/java/org/apache/druid/rpc/ServiceLocationTest.java
@@ -20,6 +20,7 @@
package org.apache.druid.rpc;
import nl.jqno.equalsverifier.EqualsVerifier;
+import org.apache.druid.server.DruidNode;
import org.apache.druid.server.coordination.DruidServerMetadata;
import org.apache.druid.server.coordination.ServerType;
import org.junit.jupiter.api.Assertions;
@@ -107,6 +108,23 @@ public class ServiceLocationTest
);
}
+ @Test
+ public void test_fromDruidNode_usesAdvertisedPlaintextPort()
+ {
+ Assertions.assertEquals(
+ new ServiceLocation("host", 8080, -1, ""),
+ ServiceLocation.fromDruidNode(new DruidNode("svc", "host", false,
8080, null, true, false))
+ );
+ Assertions.assertEquals(
+ new ServiceLocation("host", 9080, -1, ""),
+ ServiceLocation.fromDruidNode(new DruidNode("svc", "host", false,
8080, null, null, true, false, null, 9080))
+ );
+ Assertions.assertEquals(
+ new ServiceLocation("host", 9080, 8443, ""),
+ ServiceLocation.fromDruidNode(new DruidNode("svc", "host", false,
8080, null, 8443, true, true, null, 9080))
+ );
+ }
+
@Test
public void test_equals()
{
diff --git a/server/src/test/java/org/apache/druid/server/DruidNodeTest.java
b/server/src/test/java/org/apache/druid/server/DruidNodeTest.java
index 8da3a7f077d..ef4e21ac72a 100644
--- a/server/src/test/java/org/apache/druid/server/DruidNodeTest.java
+++ b/server/src/test/java/org/apache/druid/server/DruidNodeTest.java
@@ -27,6 +27,7 @@ import org.apache.druid.jackson.DefaultObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import java.net.URI;
import java.util.Map;
public class DruidNodeTest
@@ -188,6 +189,130 @@ public class DruidNodeTest
Assertions.assertEquals(-1, node.getTlsPort());
}
+ @Test
+ public void testAdvertisedPlaintextPort()
+ {
+ // When not set, advertisedPlaintextPort defaults to plaintextPort
+ DruidNode node = new DruidNode("test", "host", false, 8082, null, true,
false);
+ Assertions.assertEquals(8082, node.getPlaintextPort());
+ Assertions.assertEquals(8082, node.getAdvertisedPlaintextPort());
+ Assertions.assertEquals("host:8082", node.getHostAndPort());
+ Assertions.assertEquals("host:8082", node.getHostAndPortToUse());
+ Assertions.assertEquals(8082, node.getPortToUse());
+
+ // When set, peer-facing accessors use advertisedPlaintextPort;
getPlaintextPort() stays the bind port
+ node = new DruidNode("test", "host", false, 8082, null, 9443, true, false,
null, 9443);
+ Assertions.assertEquals(8082, node.getPlaintextPort());
+ Assertions.assertEquals(9443, node.getAdvertisedPlaintextPort());
+ Assertions.assertEquals("host:9443", node.getHostAndPort());
+ Assertions.assertEquals("host:9443", node.getHostAndPortToUse());
+ Assertions.assertEquals(9443, node.getPortToUse());
+ Assertions.assertEquals(URI.create("http://host:9443"),
node.getUriToUse());
+ }
+
+ @Test
+ public void testAdvertisedPlaintextPortNonPositiveFallsBack()
+ {
+ for (Integer advertised : new Integer[]{null, 0, -1}) {
+ DruidNode node = new DruidNode("test", "host", false, 8082, null, null,
true, false, null, advertised);
+ Assertions.assertEquals(8082, node.getAdvertisedPlaintextPort());
+ Assertions.assertEquals(8082, node.getPortToUse());
+ Assertions.assertEquals("host:8082", node.getHostAndPortToUse());
+ }
+ }
+
+ @Test
+ public void testAdvertisedPlaintextPortWithTls()
+ {
+ // TLS port still wins for the *ToUse accessors when TLS is enabled
+ DruidNode node = new DruidNode("test", "host", false, 8082, null, 8443,
true, true, null, 9443);
+ Assertions.assertEquals(8082, node.getPlaintextPort());
+ Assertions.assertEquals(9443, node.getAdvertisedPlaintextPort());
+ Assertions.assertEquals(8443, node.getTlsPort());
+ Assertions.assertEquals("host:9443", node.getHostAndPort());
+ Assertions.assertEquals("host:8443", node.getHostAndTlsPort());
+ Assertions.assertEquals("host:8443", node.getHostAndPortToUse());
+ Assertions.assertEquals(8443, node.getPortToUse());
+ Assertions.assertEquals(URI.create("https://host:8443"),
node.getUriToUse());
+ }
+
+ @Test
+ public void testAdvertisedPlaintextPortDisabledPlaintext()
+ {
+ DruidNode node = new DruidNode("test", "host", false, null, null, 8443,
false, true, null, 9443);
+ Assertions.assertEquals(-1, node.getPlaintextPort());
+ Assertions.assertEquals(-1, node.getAdvertisedPlaintextPort());
+ Assertions.assertNull(node.getHostAndPort());
+ Assertions.assertEquals(8443, node.getPortToUse());
+ }
+
+ @Test
+ public void testAdvertisedPlaintextPortSerde() throws Exception
+ {
+ DruidNode original = new DruidNode("service", "host", true, 8082, null,
5678, true, true, null, 9443);
+ DruidNode actual = mapper.readValue(mapper.writeValueAsString(original),
DruidNode.class);
+ Assertions.assertEquals(8082, actual.getPlaintextPort());
+ Assertions.assertEquals(9443, actual.getAdvertisedPlaintextPort());
+ Assertions.assertEquals(5678, actual.getTlsPort());
+ Assertions.assertEquals("host:9443", actual.getHostAndPort());
+ }
+
+ @Test
+ public void testAdvertisedPlaintextPortBackwardCompatDeserialization()
throws Exception
+ {
+ String json = "{\n"
+ + " \"service\":\"service\",\n"
+ + " \"host\":\"host\",\n"
+ + " \"plaintextPort\":8082,\n"
+ + " \"enablePlaintextPort\":true,\n"
+ + " \"enableTlsPort\":false\n"
+ + "}\n";
+ DruidNode actual = mapper.readValue(json, DruidNode.class);
+ Assertions.assertEquals(8082, actual.getPlaintextPort());
+ Assertions.assertEquals(8082, actual.getAdvertisedPlaintextPort());
+ Assertions.assertEquals("host:8082", actual.getHostAndPort());
+ }
+
+ @Test
+ public void testAdvertisedPlaintextPortDeserialization() throws Exception
+ {
+ String json = "{\n"
+ + " \"service\":\"service\",\n"
+ + " \"host\":\"host\",\n"
+ + " \"plaintextPort\":8082,\n"
+ + " \"advertisedPlaintextPort\":9443,\n"
+ + " \"enablePlaintextPort\":true,\n"
+ + " \"enableTlsPort\":false\n"
+ + "}\n";
+ DruidNode actual = mapper.readValue(json, DruidNode.class);
+ Assertions.assertEquals(8082, actual.getPlaintextPort());
+ Assertions.assertEquals(9443, actual.getAdvertisedPlaintextPort());
+ Assertions.assertEquals("host:9443", actual.getHostAndPort());
+ Assertions.assertEquals("host:9443", actual.getHostAndPortToUse());
+ Assertions.assertEquals(9443, actual.getPortToUse());
+ }
+
+ @Test
+ public void testAdvertisedPlaintextPortWithService()
+ {
+ DruidNode node = new DruidNode("test", "host", false, 8082, null, 9443,
true, false, null, 9443);
+ DruidNode copy = node.withService("other");
+ Assertions.assertEquals("other", copy.getServiceName());
+ Assertions.assertEquals(8082, copy.getPlaintextPort());
+ Assertions.assertEquals(9443, copy.getAdvertisedPlaintextPort());
+ }
+
+ @Test
+ public void testAdvertisedPlaintextPortEquality()
+ {
+ DruidNode a = new DruidNode("test", "host", false, 8082, null, 9443, true,
false, null, 9443);
+ DruidNode b = new DruidNode("test", "host", false, 8082, null, 9443, true,
false, null, 9443);
+ DruidNode c = new DruidNode("test", "host", false, 8082, null, true,
false);
+ Assertions.assertEquals(a, b);
+ Assertions.assertNotEquals(a, c);
+ Assertions.assertEquals(a.hashCode(), b.hashCode());
+ }
+
@Test
public void testConflictingPorts()
{
diff --git
a/server/src/test/java/org/apache/druid/server/http/CoordinatorDynamicConfigSyncerTest.java
b/server/src/test/java/org/apache/druid/server/http/CoordinatorDynamicConfigSyncerTest.java
index 4bc33131415..db3ab68d7ac 100644
---
a/server/src/test/java/org/apache/druid/server/http/CoordinatorDynamicConfigSyncerTest.java
+++
b/server/src/test/java/org/apache/druid/server/http/CoordinatorDynamicConfigSyncerTest.java
@@ -22,6 +22,7 @@ package org.apache.druid.server.http;
import com.google.common.util.concurrent.Futures;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
+import org.apache.druid.common.guava.FutureUtils;
import org.apache.druid.discovery.DiscoveryDruidNode;
import org.apache.druid.discovery.DruidNodeDiscovery;
import org.apache.druid.discovery.DruidNodeDiscoveryProvider;
@@ -31,14 +32,18 @@ import
org.apache.druid.java.util.emitter.service.ServiceEmitter;
import org.apache.druid.java.util.http.client.response.BytesFullResponseHolder;
import org.apache.druid.rpc.RequestBuilder;
import org.apache.druid.rpc.ServiceClient;
+import org.apache.druid.rpc.ServiceLocation;
import org.apache.druid.server.DruidNode;
import org.apache.druid.server.coordinator.CoordinatorConfigManager;
import org.apache.druid.server.coordinator.CoordinatorDynamicConfig;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -51,6 +56,7 @@ public class CoordinatorDynamicConfigSyncerTest
private ServiceClient serviceClient;
private CoordinatorConfigManager coordinatorConfigManager;
+ private DruidNodeDiscoveryProvider druidNodeDiscoveryProvider;
private DruidNodeDiscovery druidNodeDiscovery;
@BeforeEach
@@ -64,15 +70,15 @@ public class CoordinatorDynamicConfigSyncerTest
.when(serviceClient).asyncRequest(ArgumentMatchers.any(),
ArgumentMatchers.any());
coordinatorConfigManager = mock(CoordinatorConfigManager.class);
- DruidNodeDiscoveryProvider provider =
mock(DruidNodeDiscoveryProvider.class);
+ druidNodeDiscoveryProvider = mock(DruidNodeDiscoveryProvider.class);
druidNodeDiscovery = mock(DruidNodeDiscovery.class);
-
doReturn(druidNodeDiscovery).when(provider).getForNodeRole(NodeRole.BROKER);
+
doReturn(druidNodeDiscovery).when(druidNodeDiscoveryProvider).getForNodeRole(NodeRole.BROKER);
target = new CoordinatorDynamicConfigSyncer(
(serviceName, serviceLocator, retryPolicy) -> serviceClient,
coordinatorConfigManager,
DefaultObjectMapper.INSTANCE,
- provider,
+ druidNodeDiscoveryProvider,
mock(ServiceEmitter.class)
);
}
@@ -104,6 +110,36 @@ public class CoordinatorDynamicConfigSyncerTest
verify(serviceClient).asyncRequest(eq(requestBuilder),
ArgumentMatchers.any());
}
+ @Test
+ public void testSync_usesAdvertisedPlaintextPort()
+ {
+ final List<Set<ServiceLocation>> dialedLocations = new ArrayList<>();
+ target = new CoordinatorDynamicConfigSyncer(
+ (serviceName, serviceLocator, retryPolicy) -> {
+
dialedLocations.add(FutureUtils.getUnchecked(serviceLocator.locate(),
true).getLocations());
+ return serviceClient;
+ },
+ coordinatorConfigManager,
+ DefaultObjectMapper.INSTANCE,
+ druidNodeDiscoveryProvider,
+ mock(ServiceEmitter.class)
+ );
+
+
doReturn(CoordinatorDynamicConfig.builder().build()).when(coordinatorConfigManager).getCurrentDynamicConfig();
+ List<DiscoveryDruidNode> nodes = List.of(
+ new DiscoveryDruidNode(
+ new DruidNode("service", "host", false, 8080, null, null, true,
false, null, 9080),
+ NodeRole.BROKER,
+ null,
+ null
+ )
+ );
+ doReturn(nodes).when(druidNodeDiscovery).getAllNodes();
+
+ target.broadcastConfigToBrokers();
+ Assertions.assertEquals(List.of(Set.of(new ServiceLocation("host", 9080,
-1, ""))), dialedLocations);
+ }
+
@Test
public void testSync_whenDruidNode_isNull()
{
diff --git
a/services/src/test/java/org/apache/druid/server/router/TieredBrokerHostSelectorTest.java
b/services/src/test/java/org/apache/druid/server/router/TieredBrokerHostSelectorTest.java
index abd903a4847..9ddcbdef2e5 100644
---
a/services/src/test/java/org/apache/druid/server/router/TieredBrokerHostSelectorTest.java
+++
b/services/src/test/java/org/apache/druid/server/router/TieredBrokerHostSelectorTest.java
@@ -75,7 +75,7 @@ public class TieredBrokerHostSelectorTest
druidNodeDiscoveryProvider =
EasyMock.createStrictMock(DruidNodeDiscoveryProvider.class);
node1 = new DiscoveryDruidNode(
- new DruidNode("hotBroker", "hotHost", false, 8080, null, true, false),
+ new DruidNode("hotBroker", "hotHost", false, 8080, null, null, true,
false, null, 9080),
NodeRole.BROKER,
ImmutableMap.of()
);
@@ -192,7 +192,8 @@ public class TieredBrokerHostSelectorTest
);
Assertions.assertEquals("hotBroker", p.lhs);
- Assertions.assertEquals("hotHost:8080", p.rhs.getHost());
+ Assertions.assertEquals("hotHost:9080", p.rhs.getHost());
+ Assertions.assertEquals(9080, p.rhs.getPort());
}
@Test
@@ -367,7 +368,7 @@ public class TieredBrokerHostSelectorTest
ImmutableMap.of(
"mediumBroker", ImmutableList.of(),
"coldBroker", ImmutableList.of("coldHost1:8080", "coldHost2:8080"),
- "hotBroker", ImmutableList.of("hotHost:8080")
+ "hotBroker", ImmutableList.of("hotHost:9080")
),
Maps.transformValues(
brokerSelector.getAllBrokers(),
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]