imbajin commented on code in PR #3119:
URL: https://github.com/apache/hugegraph/pull/3119#discussion_r3661390626


##########
hugegraph-server/hugegraph-dist/docker/docker-entrypoint.sh:
##########
@@ -20,23 +20,74 @@ set -euo pipefail
 DOCKER_FOLDER="./docker"
 INIT_FLAG_FILE="init_complete"
 GRAPH_CONF="./conf/graphs/hugegraph.properties"
+REST_SERVER_CONF="./conf/rest-server.properties"
 
 mkdir -p "${DOCKER_FOLDER}"
 
 log() { echo "[hugegraph-server-entrypoint] $*"; }
 
+# Sets a property to exactly one canonical `key=value` line. Existing
+# definitions are matched on any separator a properties file allows (`=`, `:`
+# or whitespace) and collapsed into that single line, because leaving a second
+# definition behind would make the parser expose the key as a list and a scalar
+# read of it would then fail. Comment lines are left alone. Matching is 
literal,
+# so no regex escaping of the key or value is needed.
 set_prop() {
     local key="$1" val="$2" file="$3"
-    local esc_key esc_val
 
+    SET_PROP_KEY="$key" SET_PROP_VAL="$val" awk '
+        BEGIN { key = ENVIRON["SET_PROP_KEY"]; val = ENVIRON["SET_PROP_VAL"] }
+        {
+            line = $0
+            probe = line
+            sub(/^[[:space:]]+/, "", probe)
+            if (index(probe, key) == 1) {
+                rest = substr(probe, length(key) + 1)
+                if (rest ~ /^[[:space:]]*[=:]/ || rest ~ /^[[:space:]]+/) {
+                    if (!done) { print key "=" val; done = 1 }
+                    next
+                }
+            }
+            print line
+        }
+        END { if (!done) print key "=" val }
+    ' "${file}" > "${file}.tmp" && mv "${file}.tmp" "${file}"

Review Comment:
   โš ๏ธ Replacing the config with `mv` breaks the mounted-config path this change 
supports and also discards file metadata. A single-file Docker/Kubernetes bind 
mount cannot normally be replaced by rename (`Device or resource busy`), so 
`set -e` aborts startup; for an ordinary `0600` file, the temporary file is 
created under the process umask and can become `0644`, exposing the 
`auth.admin_pa` written below. Please preserve the existing inode and 
protection metadata while replacing the contents, and add a writable bind-mount 
test plus a mode-preservation test.



##########
hugegraph-server/hugegraph-dist/docker/test/test-docker-entrypoint.sh:
##########
@@ -0,0 +1,386 @@
+#!/bin/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.
+#
+# Smoke tests for docker-entrypoint.sh init-store lifecycle.
+#
+# The entrypoint is run against a throwaway install tree whose ./bin scripts 
are
+# stubs recording their own invocation, so the tests assert on which scripts 
ran
+# and on the resulting config, without needing a JVM, a backend or Docker.
+#
+# Usage: hugegraph-server/hugegraph-dist/docker/test/test-docker-entrypoint.sh
+
+set -uo pipefail
+
+SELF_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+ENTRYPOINT="${SELF_DIR}/../docker-entrypoint.sh"
+
+PASS=0
+FAIL=0
+SKIP=0
+
+# docker-entrypoint.sh rewrites an existing property with GNU `sed -ri`, which

Review Comment:
   ๐Ÿงน This guard is now stale: `set_prop()` was rewritten with `awk`, but the 
test still claims it requires GNU `sed -ri` and skips the override and 
false-to-true cases on macOS. The local exact-head run consequently skipped 
both newly relevant cases. Please remove the obsolete GNU-sed guard so these 
portable tests run on every supported development platform.



##########
hugegraph-server/hugegraph-dist/src/main/java/org/apache/hugegraph/cmd/InitStore.java:
##########
@@ -69,11 +69,57 @@ public static void main(String[] args) throws Exception {
 
         String restConf = args[0];
 
-        RegisterUtil.registerBackends();
-        RegisterUtil.registerPlugins();
+        /*
+         * Only the server options are needed to read the gate below. Backend
+         * and plugin registration is deferred to the enabled path:
+         * registerPlugins() invokes every discovered plugin's register() and
+         * propagates their failures, which must not happen on a path that is
+         * documented to be a no-op.
+         */
         RegisterUtil.registerServer();
 
         HugeConfig restServerConfig = new HugeConfig(restConf);
+
+        /*
+         * Distributed deployments (PD/HStore) let the storage side own the
+         * metadata, so there is nothing for init-store to do. The option
+         * defaults to true, keeping standalone/tarball installs on the full
+         * init path.
+         *
+         * The loop below already skips hstore backends, so what this gate
+         * additionally avoids is scanning the graphs directory (which must
+         * otherwise exist), and, when auth is configured, opening the auth
+         * graph store in initAdminUserIfNeeded(). On Kubernetes that ran on
+         * every Server pod restart, since the entrypoint's init flag file does
+         * not survive one.
+         *
+         * NOTE: skipping also means the built-in admin account is not created
+         * here. The only other code path that creates it is
+         * GraphManager.initAdminUserIfNeeded(), reached from loadMetaFromPD(),
+         * which runs only when 'usePD' is true. Enabling auth with this option
+         * false and 'usePD' false therefore yields a server that enforces
+         * authentication with no account to authenticate against.
+         */
+        if (!restServerConfig.get(ServerOptions.INIT_STORE_ENABLED)) {
+            LOG.warn("Skipping init-store: '{}' is false in '{}'. Local " +
+                     "backend and admin initialization are not performed.",
+                     ServerOptions.INIT_STORE_ENABLED.name(), restConf);
+            if (!restServerConfig.get(ServerOptions.AUTHENTICATOR).isEmpty() &&
+                !restServerConfig.get(ServerOptions.USE_PD)) {
+                LOG.warn("'{}' is set but '{}' is false: no component will " +
+                         "create the built-in admin account. Set '{}' to true, 
" +
+                         "or leave '{}' enabled so it can create the account.",
+                         ServerOptions.AUTHENTICATOR.name(),
+                         ServerOptions.USE_PD.name(),
+                         ServerOptions.USE_PD.name(),
+                         ServerOptions.INIT_STORE_ENABLED.name());
+            }
+            return;

Review Comment:
   โ€ผ๏ธ This invalid non-Docker configuration still exits successfully. With 
`init_store.enabled=false`, a non-empty authenticator, and `usePD=false`, the 
code itself establishes that no component will create the built-in admin, but 
`init-store.sh` only receives this warning and returns zero. Tarball, init-job, 
or other callers can therefore continue with an auth-enabled server that nobody 
can access. Please fail this combination with a non-zero result, matching the 
Docker entrypoint's rejection, and add a direct CLI regression that verifies 
the failure status.



##########
hugegraph-server/hugegraph-dist/docker/docker-entrypoint.sh:
##########
@@ -54,19 +105,89 @@ migrate_env "PD_PEERS" "HG_SERVER_PD_PEERS"
 # โ”€โ”€ Map env โ†’ properties file โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 [[ -n "${HG_SERVER_BACKEND:-}"  ]] && set_prop "backend"  
"${HG_SERVER_BACKEND}"  "${GRAPH_CONF}"
 [[ -n "${HG_SERVER_PD_PEERS:-}" ]] && set_prop "pd.peers" 
"${HG_SERVER_PD_PEERS}" "${GRAPH_CONF}"
+if [[ -n "${HG_SERVER_INIT_STORE_ENABLED:-}" ]]; then
+    # Canonicalize before writing, so the property file only ever holds `true`
+    # or `false` and cannot be read differently by the shell and the server
+    if ! HG_SERVER_INIT_STORE_ENABLED=$(to_bool 
"${HG_SERVER_INIT_STORE_ENABLED}"); then
+        log "ERROR: HG_SERVER_INIT_STORE_ENABLED must be a boolean, got 
'${HG_SERVER_INIT_STORE_ENABLED}'"
+        exit 1
+    fi
+    set_prop "init_store.enabled" "${HG_SERVER_INIT_STORE_ENABLED}" 
"${REST_SERVER_CONF}"
+fi
 
 # โ”€โ”€ Build wait-storage env โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 WAIT_ENV=()
 [[ -n "${HG_SERVER_BACKEND:-}"  ]] && 
WAIT_ENV+=("hugegraph.backend=${HG_SERVER_BACKEND}")
 [[ -n "${HG_SERVER_PD_PEERS:-}" ]] && 
WAIT_ENV+=("hugegraph.pd.peers=${HG_SERVER_PD_PEERS}")
 
-# โ”€โ”€ Init store (once) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
-if [[ ! -f "${DOCKER_FOLDER}/${INIT_FLAG_FILE}" ]]; then
+wait_storage() {
     if (( ${#WAIT_ENV[@]} > 0 )); then
         env "${WAIT_ENV[@]}" ./bin/wait-storage.sh
     else
         ./bin/wait-storage.sh
     fi
+}
+
+# โ”€โ”€ Init store (once) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
+# With `init_store.enabled=false` (distributed PD/HStore) init-store is a 
no-op:
+# storage owns the metadata and the admin account is created on server startup
+# from `auth.admin_pa`. A requested PASSWORD is therefore written to that
+# property rather than piped into init-store.sh, where it would be read and
+# discarded without creating the account.
+#
+# The value is read back from the config file rather than from the env var, so
+# that a rest-server.properties mounted with the property already set behaves
+# the same as `HG_SERVER_INIT_STORE_ENABLED` (the env mapping above has already
+# been applied, so env still wins).
+INIT_STORE_ENABLED=$(get_prop "init_store.enabled" "${REST_SERVER_CONF}")
+if [[ -n "${INIT_STORE_ENABLED}" ]]; then
+    if ! INIT_STORE_ENABLED=$(to_bool "${INIT_STORE_ENABLED}"); then
+        log "ERROR: init_store.enabled in ${REST_SERVER_CONF} must be a 
boolean," \
+            "got '${INIT_STORE_ENABLED}'"
+        exit 1
+    fi
+fi
+if [[ "${INIT_STORE_ENABLED:-true}" == "false" ]]; then
+    log "init-store disabled, skipping local backend/admin init"
+
+    # With init-store skipped, nothing creates the built-in admin account
+    # unless the server takes the PD metadata path, which it only does when
+    # `usePD=true`. Enabling auth without that combination starts a server
+    # that enforces authentication while no account exists, so refuse it here
+    # rather than fail every request later.
+    AUTH_REQUESTED=""
+    [[ -n "${PASSWORD:-}" ]] && AUTH_REQUESTED=1
+    [[ -n "$(get_prop "auth.authenticator" "${REST_SERVER_CONF}")" ]] && 
AUTH_REQUESTED=1
+    if [[ -n "${AUTH_REQUESTED}" ]]; then
+        USE_PD=$(to_bool "$(get_prop "usePD" "${REST_SERVER_CONF}")" 
2>/dev/null || echo "false")
+        if [[ "${USE_PD}" != "true" ]]; then
+            log "ERROR: auth is enabled and init_store.enabled=false, but 
usePD is not true."
+            log "ERROR: With init-store skipped the admin account is only 
created on the PD"
+            log "ERROR: metadata path, so this combination would start a 
server that nobody"
+            log "ERROR: can authenticate against."
+            log "ERROR: Set usePD=true in ${REST_SERVER_CONF}, or leave 
init-store enabled"
+            log "ERROR: so that it can create the admin account locally."
+            exit 1
+        fi
+    fi
+
+    # Still wait: the server needs the storage side reachable at startup even
+    # though nothing is initialized here
+    wait_storage
+
+    if [[ -n "${PASSWORD:-}" ]]; then
+        log "enabling auth mode, admin password applied via auth.admin_pa"
+        ./bin/enable-auth.sh

Review Comment:
   โš ๏ธ `PASSWORD` is not safe with an already-auth-enabled mounted config on 
this skip path. `enable-auth.sh` blindly appends `auth.authenticator` and 
`auth.graph_store` on its first run, so an accepted `init_store.enabled=false` 
+ `usePD=true` config that already contains those scalar keys becomes 
duplicated; `HugeConfig` then exposes a list where callers expect a `String` 
and server startup fails. Please make auth enablement idempotent or 
canonicalize these keys, and add a mounted-auth + `PASSWORD` smoke test that 
loads the resulting file through `HugeConfig`.



-- 
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]

Reply via email to