imbajin commented on code in PR #3149:
URL: https://github.com/apache/hugegraph/pull/3149#discussion_r3890888850
##########
docker/docker-compose.yml:
##########
@@ -14,117 +14,59 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-name: hugegraph-single
+
+name: hugegraph-standalone
networks:
hg-net:
- driver: bridge
volumes:
- hg-pd-data:
- hg-store-data:
+ server-data:
+ hubble-data:
services:
-
- pd:
- image: hugegraph/pd:${HUGEGRAPH_VERSION:-latest}
- pull_policy: always
- container_name: hg-pd
- hostname: pd
- restart: unless-stopped
- networks: [hg-net]
- environment:
- HG_PD_GRPC_HOST: pd
- HG_PD_GRPC_PORT: "8686"
- HG_PD_REST_PORT: "8620"
- HG_PD_RAFT_ADDRESS: pd:8610
- HG_PD_RAFT_PEERS_LIST: pd:8610
- HG_PD_INITIAL_STORE_LIST: store:8500
- HG_PD_DATA_PATH: /hugegraph-pd/pd_data
- ports:
- - "8620:8620"
- volumes:
- - hg-pd-data:/hugegraph-pd/pd_data
- healthcheck:
- test: ["CMD-SHELL", "curl -fsS http://localhost:8620/v1/health
>/dev/null || exit 1"]
- interval: 10s
- timeout: 5s
- retries: 12
- start_period: 30s
-
- store:
- image: hugegraph/store:${HUGEGRAPH_VERSION:-latest}
- pull_policy: always
- container_name: hg-store
- hostname: store
- restart: unless-stopped
- networks: [hg-net]
- depends_on:
- pd:
- condition: service_healthy
- environment:
- HG_STORE_PD_ADDRESS: pd:8686
- HG_STORE_GRPC_HOST: store
- HG_STORE_GRPC_PORT: "8500"
- HG_STORE_REST_PORT: "8520"
- HG_STORE_RAFT_ADDRESS: store:8510
- HG_STORE_DATA_PATH: /hugegraph-store/storage
- ports:
- - "8520:8520"
- volumes:
- - hg-store-data:/hugegraph-store/storage
- healthcheck:
- test: ["CMD-SHELL", "curl -fsS http://localhost:8520/v1/health
>/dev/null || exit 1"]
- interval: 10s
- timeout: 10s
- retries: 30
- start_period: 60s
-
server:
- image:
${HUGEGRAPH_SERVER_IMAGE:-hugegraph/server:${HUGEGRAPH_VERSION:-latest}}
- pull_policy: ${HUGEGRAPH_SERVER_PULL_POLICY:-always}
- container_name: hg-server
- hostname: server
+ image: hugegraph/hugegraph:${HUGEGRAPH_VERSION:-latest}
+ pull_policy: ${HUGEGRAPH_PULL_POLICY:-missing}
restart: unless-stopped
networks: [hg-net]
- depends_on:
- store:
- condition: service_healthy
environment:
- HG_SERVER_BACKEND: hstore
- HG_SERVER_PD_PEERS: pd:8686
- HG_SERVER_CLUSTER: hg
- HG_SERVER_USE_PD: "true"
- HG_SERVER_REST_URL: http://server:8080
- HG_SERVER_MIN_FREE_MEMORY: "0"
- HG_SERVER_INIT_STORE_ENABLED: "false"
+ PASSWORD: ${HUGEGRAPH_ADMIN_PASSWORD:-}
Review Comment:
‼️ Critical. With no `.env`, `HUGEGRAPH_ADMIN_PASSWORD` expands to an empty
value, and the server entrypoint treats that as authentication disabled, while
`8080:8080` publishes the API on all host interfaces. A default `docker compose
up` therefore exposes an unauthenticated Server to the LAN. Fail closed with
required credentials, or bind the auth-off default to loopback and make the
unauthenticated mode explicit; apply the same policy to the HStore and HA files.
##########
docker/conf/hubble/standalone.properties:
##########
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+server.host=0.0.0.0
Review Comment:
‼️ Critical. This mounted Hubble properties file, along with the HStore and
HStore-HA variants, omits `server.port`. The Hubble image startup script reads
`server.host` and `server.port` directly to build its `/about` probe URL;
because this file replaces the image config, the image default of 8088 is not
preserved and the probe can be built with an empty port. Add `server.port=8088`
to all three files and assert it in the render contract.
##########
docker/docker-compose.yml:
##########
@@ -14,117 +14,59 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-name: hugegraph-single
+
+name: hugegraph-standalone
Review Comment:
‼️ Critical. This changes the default Compose project from the previous
PD/Store/HStore topology to a standalone RocksDB Server with a different
project name and volume (`server-data`). An existing user who upgrades the file
and runs the documented default command will no longer mount the old HStore
data; the old stack and its ports remain separate, with no migration, backup,
or rollback warning. Preserve the legacy entrypoint or document an explicit
migration and safe rollback path before making this the default.
##########
docker/docker-compose-3pd-3store-3server.yml:
##########
@@ -58,6 +59,17 @@ x-store-common: &store-common
retries: 40
start_period: 120s
+x-server-environment: &server-environment
+ STORE_REST: store0:8520
+ HG_SERVER_BACKEND: hstore
+ HG_SERVER_PD_PEERS: pd0:8686,pd1:8686,pd2:8686
+ HG_SERVER_CLUSTER: hg
+ HG_SERVER_USE_PD: "true"
+ HG_SERVER_MIN_FREE_MEMORY: "0"
+ HG_SERVER_INIT_STORE_ENABLED: "false"
+ HG_SERVER_AUTH_TOKEN_SECRET: ${HUGEGRAPH_AUTH_TOKEN_SECRET:-}
Review Comment:
‼️ Critical. HA leaves `HG_SERVER_AUTH_TOKEN_SECRET` empty by default. When
a password is set, the Server entrypoint generates a random secret if this
variable is absent; with three Server replicas, each container can then sign
JWTs with a different key and requests routed across replicas intermittently
fail with 401. Require one non-empty shared secret for HA (or generate and
inject it once) and add a password-only multi-replica check.
##########
docker/test-compose.sh:
##########
@@ -0,0 +1,396 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -Eeuo pipefail
+
+DOCKER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PASSWORD="ci-compose-password"
+SECRET="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+VERSION="ci-version"
+RENDER_HUBBLE_IMAGE="example.invalid/hugegraph/hubble:ci"
+DATASOURCE="jdbc:h2:file:/hubble/data/hubble;DB_CLOSE_ON_EXIT=FALSE"
+ACTIVE_PROJECT=""
+ACTIVE_FILES=()
+RENDER_DIR=""
+
+compose_auth() {
+ env HUGEGRAPH_VERSION="${VERSION}" \
+ HUBBLE_IMAGE="${RENDER_HUBBLE_IMAGE}" \
+ HUGEGRAPH_ADMIN_PASSWORD="${PASSWORD}" \
+ HUGEGRAPH_AUTH_TOKEN_SECRET="${SECRET}" \
+ docker compose "$@"
+}
+
+render() {
+ local output="$1"
+ shift
+ compose_auth "$@" config --format json > "${output}"
+}
+
+assert_file_property() {
+ local file="$1"
+ local property="$2"
+ grep -Fqx "${property}" "${file}"
+}
+
+assert_common() {
+ local rendered="$1"
+ local services="$2"
+ local volumes="$3"
+
+ jq -e \
+ --arg password "${PASSWORD}" \
+ --arg secret "${SECRET}" \
+ --argjson services "${services}" \
+ --argjson volumes "${volumes}" '
+ (.services | keys) == $services and
+ (.volumes | keys) == $volumes and
+ (.networks | keys) == ["hg-net"] and
+ all(.networks[]; .external != true) and
+ all(.volumes[]; .external != true) and
+ all(.services[];
+ (.networks | keys) == ["hg-net"] and
+ .healthcheck.test[0] == "CMD-SHELL") and
+ all(
+ [.services | to_entries[] |
+ select(.key | startswith("server")) |
+ .value.environment][];
+ .PASSWORD == $password and
+ .HG_SERVER_AUTH_TOKEN_SECRET == $secret)
+ ' "${rendered}" >/dev/null
+}
+
+assert_hubble() {
+ local rendered="$1"
+ local config="$2"
+ shift 2
+ local dependencies
+ dependencies="$(printf '%s\n' "$@" | jq -Rsc 'split("\n")[:-1] | sort')"
+
+ jq -e \
+ --arg datasource "${DATASOURCE}" \
+ --arg config "/docker/conf/hubble/${config}" \
+ --argjson dependencies "${dependencies}" '
+ .services.hubble.image == "example.invalid/hugegraph/hubble:ci" and
+ .services.hubble.pull_policy == "missing" and
+ .services.hubble.environment.SPRING_DATASOURCE_URL == $datasource and
+ any(.services.hubble.ports[];
+ .target == 8088 and .published == "8088" and
+ .host_ip == "127.0.0.1") and
+ (.services.hubble.depends_on | keys | sort) == $dependencies and
+ all(.services.hubble.depends_on[];
+ .condition == "service_healthy") and
+ any(.services.hubble.volumes[];
+ .type == "volume" and .source == "hubble-data" and
+ .target == "/hubble/data") and
+ any(.services.hubble.volumes[];
+ .type == "bind" and (.source | endswith($config)) and
+ .target == "/hubble/conf/hugegraph-hubble.properties" and
+ .read_only == true) and
+ (.services.hubble.healthcheck.test[1] |
+ contains("http://localhost:8088/about") and
+ contains("\"status\":200") and
+ contains("\"name\":\"hugegraph-hubble\""))
+ ' "${rendered}" >/dev/null
+}
+
+assert_standalone() {
+ local rendered="$1"
+ assert_common "${rendered}" \
+ '["hubble","server"]' \
+ '["hubble-data","server-data"]'
+ assert_hubble "${rendered}" "standalone.properties" server
+ jq -e '
+ .services.server.image == "hugegraph/hugegraph:ci-version" and
+ .services.server.pull_policy == "missing" and
+ .services.server.healthcheck.test[1] ==
+ "curl -fsS http://localhost:8080/versions >/dev/null" and
+ any(.services.server.volumes[];
+ .source == "server-data" and
+ .target == "/hugegraph-server/rocksdb-data")
+ ' "${rendered}" >/dev/null
+ assert_file_property "${DOCKER_DIR}/conf/hubble/standalone.properties" \
+ "pd.enabled=false"
+ assert_file_property "${DOCKER_DIR}/conf/hubble/standalone.properties" \
+ "server.direct_url=http://server:8080"
+}
+
+assert_hstore() {
+ local rendered="$1"
+ assert_common "${rendered}" \
+ '["hubble","pd","server","store"]' \
+ '["hubble-data","pd-data","store-data"]'
+ assert_hubble "${rendered}" "hstore.properties" server
+ jq -e '
+ .services.pd.image == "hugegraph/pd:ci-version" and
+ .services.store.image == "hugegraph/store:ci-version" and
+ .services.server.image == "hugegraph/server:ci-version" and
+ all([.services.pd, .services.store, .services.server][];
+ .pull_policy == "missing") and
+ .services.server.environment.HG_SERVER_BACKEND == "hstore" and
+ .services.server.environment.HG_SERVER_PD_PEERS == "pd:8686" and
+ .services.server.environment.HG_SERVER_USE_PD == "true" and
+ .services.server.environment.HG_SERVER_REST_URL ==
+ "http://server:8080" and
+ .services.server.healthcheck.test[1] ==
+ "curl -fsS http://server:8080/versions >/dev/null" and
+ any(.services.pd.volumes[];
+ .source == "pd-data" and
+ .target == "/hugegraph-pd/pd_data") and
+ any(.services.store.volumes[];
+ .source == "store-data" and
+ .target == "/hugegraph-store/storage")
+ ' "${rendered}" >/dev/null
+ assert_file_property "${DOCKER_DIR}/conf/hubble/hstore.properties" \
+ "pd.peers=pd:8686"
+ assert_file_property "${DOCKER_DIR}/conf/hubble/hstore.properties" \
+ "operations.store.allowed_targets=[http://store:8520]"
+}
+
+assert_ha() {
+ local rendered="$1"
+ assert_common "${rendered}" \
+
'["hubble","pd0","pd1","pd2","server0","server1","server2","store0","store1","store2"]'
\
+
'["hg-pd0-data","hg-pd1-data","hg-pd2-data","hg-store0-data","hg-store1-data","hg-store2-data","hubble-data"]'
+ assert_hubble "${rendered}" "hstore-ha.properties" \
+ server0 server1 server2
+ jq -e '
+ all([.services.pd0, .services.pd1, .services.pd2][];
+ .image == "hugegraph/pd:ci-version" and
+ .pull_policy == "missing") and
+ all([.services.store0, .services.store1, .services.store2][];
+ .image == "hugegraph/store:ci-version" and
+ .pull_policy == "missing") and
+ all([.services.server0, .services.server1, .services.server2][];
+ .image == "hugegraph/server:ci-version" and
+ .pull_policy == "missing" and
+ .environment.STORE_REST == "store0:8520" and
+ .environment.HG_SERVER_BACKEND == "hstore" and
+ .environment.HG_SERVER_PD_PEERS ==
+ "pd0:8686,pd1:8686,pd2:8686" and
+ .environment.HG_SERVER_CLUSTER == "hg" and
+ .environment.HG_SERVER_USE_PD == "true" and
+ .environment.HG_SERVER_INIT_STORE_ENABLED == "false") and
+ [.services.server0.environment.HG_SERVER_REST_URL,
+ .services.server1.environment.HG_SERVER_REST_URL,
+ .services.server2.environment.HG_SERVER_REST_URL] ==
+ ["http://server0:8080",
+ "http://server1:8080",
+ "http://server2:8080"] and
+ [.services.server0.healthcheck.test[1],
+ .services.server1.healthcheck.test[1],
+ .services.server2.healthcheck.test[1]] ==
+ ["curl -fsS http://server0:8080/versions >/dev/null",
+ "curl -fsS http://server1:8080/versions >/dev/null",
+ "curl -fsS http://server2:8080/versions >/dev/null"]
+ ' "${rendered}" >/dev/null
+ assert_file_property "${DOCKER_DIR}/conf/hubble/hstore-ha.properties" \
+ "pd.peers=pd0:8686,pd1:8686,pd2:8686"
+ assert_file_property "${DOCKER_DIR}/conf/hubble/hstore-ha.properties" \
+
"operations.store.allowed_targets=[http://store0:8520,http://store1:8520,http://store2:8520]"
+}
+
+assert_dev_override() {
+ local rendered="$1"
+ local override="$2"
+ assert_common "${rendered}" \
+ '["hubble","pd","server","store"]' \
+ '["hubble-data","pd-data","store-data"]'
+ jq -e '
+ .services.pd.image == "hugegraph/pd:dev" and
+ .services.store.image == "hugegraph/store:dev" and
+ .services.server.image == "hugegraph/server:dev" and
+ all([.services.pd, .services.store, .services.server][];
+ .pull_policy == "build" and .build != null) and
+ .services.hubble.image == "example.invalid/hugegraph/hubble:ci" and
+ .services.hubble.build == null
+ ' "${rendered}" >/dev/null
+ jq -e '
+ (.services | keys) == ["pd","server","store"] and
+ (.networks | keys) == ["default"] and .volumes == null and
+ all(.services[];
+ .build != null and .image != null and
+ .pull_policy == "build" and
+ .environment == null and
+ (.networks | keys) == ["default"] and
+ .volumes == null)
+ ' "${override}" >/dev/null
+}
+
+cleanup() {
+ if [[ -n "${ACTIVE_PROJECT}" ]]; then
+ compose_active down -v --remove-orphans >/dev/null 2>&1 || true
+ fi
+ [[ -z "${RENDER_DIR}" ]] || rm -rf "${RENDER_DIR}"
+}
+
+run_render() {
+ RENDER_DIR="$(mktemp -d)"
+ trap cleanup EXIT INT TERM
+ render "${RENDER_DIR}/standalone.json" \
+ -f "${DOCKER_DIR}/docker-compose.yml"
+ render "${RENDER_DIR}/hstore.json" \
+ -f "${DOCKER_DIR}/docker-compose-hstore.yml"
+ render "${RENDER_DIR}/ha.json" \
+ -f "${DOCKER_DIR}/docker-compose-3pd-3store-3server.yml"
+ render "${RENDER_DIR}/dev.json" \
+ -f "${DOCKER_DIR}/docker-compose-hstore.yml" \
+ -f "${DOCKER_DIR}/docker-compose.dev.yml"
+ render "${RENDER_DIR}/override.json" \
+ -f "${DOCKER_DIR}/docker-compose.dev.yml"
+
+ assert_standalone "${RENDER_DIR}/standalone.json"
+ assert_hstore "${RENDER_DIR}/hstore.json"
+ assert_ha "${RENDER_DIR}/ha.json"
+ assert_dev_override "${RENDER_DIR}/dev.json" \
+ "${RENDER_DIR}/override.json"
+ echo "Compose render contracts passed"
+}
+
+compose_active() {
+ env HUGEGRAPH_VERSION="${HUGEGRAPH_VERSION:-latest}" \
+ HUBBLE_IMAGE="${HUBBLE_IMAGE:-hugegraph/hubble:latest}" \
+ HUGEGRAPH_ADMIN_PASSWORD="${PASSWORD}" \
+ HUGEGRAPH_AUTH_TOKEN_SECRET="${SECRET}" \
+ COMPOSE_PROGRESS=plain \
+ docker compose -p "${ACTIVE_PROJECT}" "${ACTIVE_FILES[@]}" "$@"
+}
+
+diagnose() {
+ compose_active ps || true
+ compose_active logs --no-color --tail 200 || true
+}
+
+http_status() {
+ curl -sS -o /dev/null -w '%{http_code}' "$@"
+}
+
+wait_hubble_mode() {
+ local expected_pd="$1"
+ local expected_auth="$2"
+ local response=""
+ local _
+ for _ in {1..30}; do
+ response="$(curl -fsS http://localhost:8088/api/v1.3/config || true)"
Review Comment:
⚠️ Important. The retry loop is bounded, but each `curl` has no connect or
total-request timeout. If Hubble accepts a connection and its handler hangs,
this iteration never returns, so the 30-iteration bound and trap cleanup are
ineffective. Add shared `--connect-timeout` and `--max-time` options to
`wait_hubble_mode`, `http_status`, login, and anonymous probes.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]