This is an automated email from the ASF dual-hosted git repository.

yuqi1129 pushed a commit to branch feat/mcp-governance-task3-6
in repository https://gitbox.apache.org/repos/asf/gravitino.git

commit 61d3bdf085a12e7e31602dc852b67e601ec3f77b
Author: yuqi <[email protected]>
AuthorDate: Thu Jun 11 16:37:39 2026 +0800

    Add localtest example and docs.
---
 mcp-server/dev/INSPECTOR_DEMO.md       | 208 ++++++++++++++++++++++++++
 mcp-server/dev/start_inspector_demo.sh | 260 +++++++++++++++++++++++++++++++++
 mcp-server/dev/stop_inspector_demo.sh  |  77 ++++++++++
 3 files changed, 545 insertions(+)

diff --git a/mcp-server/dev/INSPECTOR_DEMO.md b/mcp-server/dev/INSPECTOR_DEMO.md
new file mode 100644
index 0000000000..d3a86f9b46
--- /dev/null
+++ b/mcp-server/dev/INSPECTOR_DEMO.md
@@ -0,0 +1,208 @@
+<!--
+  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.
+-->
+
+# Hands-on MCP Authorization Demo (with MCP Inspector)
+
+Drive the Gravitino MCP server interactively through the
+[MCP Inspector](https://github.com/modelcontextprotocol/inspector) and watch
+per-user authorization and audit work end to end, as two principals
+(`admin` and `bob`).
+
+This exercises the three governance moments:
+
+1. **Scoped discovery** — `admin` and `bob` run the same call and get 
different,
+   authorization-scoped results.
+2. **Write denied** — `bob` (read-only) attempts a write and is denied by
+   Gravitino authorization, surfaced as an explicit error through MCP.
+3. **Audit trail** — every call produces an audit record attributed to the
+   correct principal with an allow/deny outcome.
+
+---
+
+## 1. Prerequisites
+
+- A built Gravitino distribution. If you don't have one:
+  ```bash
+  ./gradlew compileDistribution -x test -PskipWeb=true
+  # produces distribution/package/
+  ```
+- `uv` available in the `mcp-server/` directory (the project already uses it).
+- Node.js (the start script launches the Inspector via `npx 
@modelcontextprotocol/inspector`).
+- If you run behind an HTTP proxy, the scripts already export
+  `NO_PROXY=localhost,127.0.0.1`; make sure your shell doesn't force the proxy
+  for loopback in some other way.
+
+---
+
+## 2. Start the demo environment
+
+From the `mcp-server/` directory:
+
+```bash
+./dev/start_inspector_demo.sh
+```
+
+This script (idempotent — safe to re-run):
+
+1. Enables `simple` auth + authorization in 
`distribution/package/conf/gravitino.conf`
+   (backing up the original).
+2. Starts Gravitino (or reuses a running one).
+3. Provisions demo data into metalake **`mcp_authz_it`**:
+   - `cat_allowed` and `cat_denied` (two model catalogs)
+   - user **`bob`**
+   - a reader role granting `bob` `USE_CATALOG` on `cat_allowed` **only**
+4. Starts the MCP server in HTTP mode at `http://127.0.0.1:8000/mcp`.
+5. Starts the **MCP Inspector** at `http://localhost:6274/` (with the 
session-token
+   requirement disabled, so the plain URL works directly).
+
+On success it prints the connection details and the two principals' tokens. You
+should see the provisioning summary confirming the different slices:
+
+```
+[demo]   admin sees catalogs: ['cat_allowed', 'cat_denied']
+[demo]   bob sees catalogs: ['cat_allowed']
+```
+
+> If a distribution isn't found, set `GRAVITINO_HOME=/path/to/distribution`.
+
+---
+
+## 3. Open the Inspector
+
+The start script already launched it. Just open:
+
+```
+http://localhost:6274/
+```
+
+(No session token needed — the script starts it with 
`DANGEROUSLY_OMIT_AUTH=true`
+for local convenience.)
+
+### Connect
+
+| Field          | Value                                    |
+|----------------|------------------------------------------|
+| Transport Type | `Streamable HTTP`                        |
+| URL            | `http://127.0.0.1:8000/mcp`              |
+| Header Name    | `Authorization`                          |
+| Header Value   | `Basic YWRtaW46ZHVtbXk=`  (this is `admin`) |
+
+The two principal tokens (these are Gravitino simple-auth headers, i.e.
+`Basic base64("<user>:dummy")`):
+
+| Principal | Authorization header value     |
+|-----------|--------------------------------|
+| `admin`   | `Basic YWRtaW46ZHVtbXk=`       |
+| `bob`     | `Basic Ym9iOmR1bW15`           |
+
+Click **Connect**, then **List Tools** — you should see the full read + write
+tool surface (catalogs, schemas, tables, filesets, topics, models, tags, …).
+
+> **Identity is the header.** The Inspector sends your `Authorization` header 
on
+> every request; the MCP server forwards it verbatim to Gravitino, which
+> authorizes against that principal. To switch principals, change the header
+> value and reconnect.
+
+---
+
+## 4. The three scenarios
+
+Keep a terminal tailing the audit log while you click:
+
+```bash
+tail -f gravitino-mcp-audit.log
+```
+
+### Scenario 1 — Scoped discovery
+
+1. Connected as **admin**, run tool **`get_list_of_catalogs`**.
+   → Returns **both** `cat_allowed` and `cat_denied`.
+2. Reconnect with the **bob** header (`Basic Ym9iOmR1bW15`), run
+   **`get_list_of_catalogs`** again.
+   → Returns **only** `cat_allowed`.
+
+Same call, different results — sourced entirely from Gravitino's list 
filtering,
+not from any logic in the MCP server.
+
+### Scenario 2 — Write denied by authorization
+
+Still connected as **bob**, run **`create_tag`** with arguments:
+
+```json
+{ "name": "test_tag", "comment": "x", "properties": {} }
+```
+
+→ You get an explicit error, e.g.
+`User 'bob' is not authorized to perform operation 'createTag' on metadata 
'mcp_authz_it'`.
+
+It's a real authorization denial, not a hidden tool or a silent no-op. 
Reconnect
+as **admin** and run the same `create_tag` — it succeeds (admin owns the 
metalake).
+
+### Scenario 3 — Audit trail
+
+Look at the audit log you've been tailing. You should see discrete, correctly
+attributed records, for example:
+
+```json
+{"timestamp": "...", "principal": "admin", "tool": "get_list_of_catalogs", 
"outcome": "allow"}
+{"timestamp": "...", "principal": "bob",   "tool": "get_list_of_catalogs", 
"outcome": "allow"}
+{"timestamp": "...", "principal": "bob",   "tool": "create_tag", "outcome": 
"deny", "error_type": "McpError"}
+```
+
+`admin`'s reads attributed to `admin`, `bob`'s reads to `bob`, and `bob`'s 
denied
+write to `bob` with a `deny` outcome.
+
+---
+
+## 5. Tear down
+
+```bash
+./dev/stop_inspector_demo.sh
+```
+
+Stops the Inspector, the MCP server, and Gravitino, and restores the original
+`gravitino.conf`.
+
+---
+
+## 6. Troubleshooting
+
+**The audit log looks empty / stuck at 0 bytes.**
+The MCP server opens the audit file once at startup and holds the handle. If 
you
+`rm` the file while the server is running, the process keeps writing to the now
+unlinked inode and a new empty file appears at the path — so you see nothing.
+Don't delete it mid-run; truncate instead (`: > gravitino-mcp-audit.log`), or
+restart the server. The start script truncates rather than deletes for exactly
+this reason.
+
+**`HTTP 000` / `502` when curling localhost.**
+An HTTP proxy is intercepting loopback traffic. Export
+`NO_PROXY=localhost,127.0.0.1` (the scripts already do) or pass `curl 
--noproxy '*'`.
+
+**MCP server fails to bind (`can't assign requested address`).**
+`localhost` resolved to a non-loopback address. The scripts bind to the
+`127.0.0.1` literal to avoid this.
+
+**`ModuleNotFoundError: No module named 'mcp_server'`.**
+Launch with `uv run python -m mcp_server ...` (the scripts do); `uv run 
mcp_server`
+needs an editable install.
+
+**Re-running the start script.**
+It's idempotent: it reuses a running Gravitino/MCP server and drops+recreates 
the
+demo metalake, so you can re-run it freely.
diff --git a/mcp-server/dev/start_inspector_demo.sh 
b/mcp-server/dev/start_inspector_demo.sh
new file mode 100755
index 0000000000..65ac519f72
--- /dev/null
+++ b/mcp-server/dev/start_inspector_demo.sh
@@ -0,0 +1,260 @@
+#!/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.
+#
+# Bring up a persistent local demo environment for hands-on MCP authorization
+# testing with the MCP Inspector:
+#
+#   1. Gravitino server with simple auth + authorization enabled.
+#   2. Demo data: metalake + two catalogs, user "bob", a role granting bob
+#      access to only one catalog (so admin and bob see different slices).
+#   3. MCP server in HTTP transport mode.
+#
+# Unlike run_authz_integration_test.sh, this script LEAVES everything running 
so
+# you can drive it from the Inspector. Run stop_inspector_demo.sh to tear down.
+#
+# Usage:
+#   ./dev/start_inspector_demo.sh
+#   GRAVITINO_HOME=/path/to/distribution ./dev/start_inspector_demo.sh
+
+set -euo pipefail
+
+# Everything is local; never route through an HTTP proxy.
+export NO_PROXY="localhost,127.0.0.1"
+export no_proxy="localhost,127.0.0.1"
+
+MCP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+REPO_ROOT="$(cd "${MCP_DIR}/.." && pwd)"
+
+GRAVITINO_PORT="${GRAVITINO_PORT:-8090}"
+GRAVITINO_URI="http://127.0.0.1:${GRAVITINO_PORT}";
+MCP_PORT="${MCP_PORT:-8000}"
+MCP_URL="http://127.0.0.1:${MCP_PORT}/mcp";
+MCP_METALAKE="${MCP_METALAKE:-mcp_authz_it}"
+MCP_PID_FILE="${MCP_DIR}/.inspector-demo-mcp.pid"
+ADMIN_AUTH="Basic $(printf '%s' 'admin:dummy' | base64)"
+
+# MCP Inspector ports (defaults baked into @modelcontextprotocol/inspector).
+INSPECTOR_UI_PORT="${INSPECTOR_UI_PORT:-6274}"
+INSPECTOR_PROXY_PORT="${INSPECTOR_PROXY_PORT:-6277}"
+INSPECTOR_PID_FILE="${MCP_DIR}/.inspector-demo-inspector.pid"
+
+log() { echo "[demo] $*"; }
+
+# ---------------------------------------------------------------------------
+# 1. Resolve the Gravitino distribution
+# ---------------------------------------------------------------------------
+if [[ -z "${GRAVITINO_HOME:-}" ]]; then
+  GRAVITINO_HOME="${REPO_ROOT}/distribution/package"
+fi
+if [[ ! -x "${GRAVITINO_HOME}/bin/gravitino.sh" ]]; then
+  log "Distribution not found at ${GRAVITINO_HOME}."
+  log "Build it first:  ./gradlew compileDistribution -x test -PskipWeb=true"
+  log "Or set GRAVITINO_HOME to an existing distribution."
+  exit 1
+fi
+log "Using GRAVITINO_HOME=${GRAVITINO_HOME}"
+
+# ---------------------------------------------------------------------------
+# 2. Enable simple auth + authorization (idempotent)
+# ---------------------------------------------------------------------------
+CONF="${GRAVITINO_HOME}/conf/gravitino.conf"
+if ! grep -q "gravitino.authorization.enable = true" "${CONF}"; then
+  cp "${CONF}" "${CONF}.inspector-demo.bak"
+  sed -i.tmp \
+    -e '/^gravitino.authenticators/d' \
+    -e '/^gravitino.authorization.enable/d' \
+    -e '/^gravitino.authorization.serviceAdmins/d' \
+    "${CONF}"
+  rm -f "${CONF}.tmp"
+  cat >> "${CONF}" <<EOF
+
+# --- injected by start_inspector_demo.sh (restored on stop) ---
+gravitino.authenticators = simple
+gravitino.authorization.enable = true
+gravitino.authorization.serviceAdmins = admin
+EOF
+  log "Configured simple auth + authorization (serviceAdmins=admin)"
+else
+  log "Authorization already enabled in config"
+fi
+
+# ---------------------------------------------------------------------------
+# 3. Start Gravitino (if not already up)
+# ---------------------------------------------------------------------------
+if curl -sf --noproxy '*' -H "Authorization: ${ADMIN_AUTH}" \
+     "${GRAVITINO_URI}/api/version" >/dev/null 2>&1; then
+  log "Gravitino already running on ${GRAVITINO_PORT}"
+else
+  log "Starting Gravitino server..."
+  "${GRAVITINO_HOME}/bin/gravitino.sh" start
+  log "Waiting for Gravitino to become healthy..."
+  for i in $(seq 1 60); do
+    if curl -sf --noproxy '*' -H "Authorization: ${ADMIN_AUTH}" \
+         "${GRAVITINO_URI}/api/version" >/dev/null 2>&1; then
+      log "Gravitino is up."
+      break
+    fi
+    if [[ "${i}" == "60" ]]; then
+      log "ERROR: Gravitino did not become healthy in time"
+      exit 1
+    fi
+    sleep 2
+  done
+fi
+
+# ---------------------------------------------------------------------------
+# 4. Provision demo data (idempotent: drop + recreate the metalake)
+# ---------------------------------------------------------------------------
+log "Provisioning demo data into metalake '${MCP_METALAKE}'..."
+(
+  cd "${MCP_DIR}"
+  GRAVITINO_URI="${GRAVITINO_URI}" MCP_METALAKE="${MCP_METALAKE}" \
+  uv run python <<'PY'
+import base64
+import os
+
+import httpx
+
+from tests.integration.gravitino_setup import GravitinoFixture
+
+uri = os.environ["GRAVITINO_URI"]
+ml = os.environ["MCP_METALAKE"]
+
+
+def hdr(user):
+    return "Basic " + base64.b64encode(f"{user}:dummy".encode()).decode()
+
+
+# Drop an existing metalake so the script can be re-run cleanly.
+client = httpx.Client(
+    base_url=uri, headers={"Authorization": hdr("admin")}, timeout=30
+)
+if client.get(f"/api/metalakes/{ml}").status_code == 200:
+    client.put(
+        f"/api/metalakes/{ml}",
+        json={"updates": [{"@type": "setProperty", "property": "in-use", 
"value": "false"}]},
+    )
+    client.delete(f"/api/metalakes/{ml}?force=true")
+    print(f"[demo] removed existing metalake '{ml}'")
+client.close()
+
+GravitinoFixture(uri, ml).provision()
+print(f"[demo] provisioned metalake '{ml}': cat_allowed, cat_denied, user bob, 
reader role")
+
+# Show the resulting authorization slice.
+for user in ("admin", "bob"):
+    r = httpx.get(
+        f"{uri}/api/metalakes/{ml}/catalogs?details=true",
+        headers={"Authorization": hdr(user)},
+    )
+    names = [c["name"] for c in r.json().get("catalogs", [])]
+    print(f"[demo]   {user} sees catalogs: {names}")
+PY
+)
+
+# ---------------------------------------------------------------------------
+# 5. Start the MCP server in HTTP mode (if not already up)
+# ---------------------------------------------------------------------------
+if nc -z 127.0.0.1 "${MCP_PORT}" 2>/dev/null; then
+  log "An MCP server is already listening on ${MCP_PORT}; leaving it as-is."
+else
+  log "Starting MCP server (HTTP) on ${MCP_URL}..."
+  (
+    cd "${MCP_DIR}"
+    # Truncate (not delete) the audit log so the running process keeps its 
handle.
+    : > gravitino-mcp-audit.log
+    nohup uv run python -m mcp_server \
+      --metalake "${MCP_METALAKE}" \
+      --gravitino-uri "${GRAVITINO_URI}" \
+      --transport http \
+      --mcp-url "${MCP_URL}" > "${MCP_DIR}/.inspector-demo-mcp.out" 2>&1 &
+    echo $! > "${MCP_PID_FILE}"
+  )
+  for i in $(seq 1 30); do
+    nc -z 127.0.0.1 "${MCP_PORT}" 2>/dev/null && break
+    sleep 1
+  done
+  if nc -z 127.0.0.1 "${MCP_PORT}" 2>/dev/null; then
+    log "MCP server is up (pid $(cat "${MCP_PID_FILE}"))."
+  else
+    log "ERROR: MCP server did not start; see 
${MCP_DIR}/.inspector-demo-mcp.out"
+    exit 1
+  fi
+fi
+
+# ---------------------------------------------------------------------------
+# 6. Start the MCP Inspector (UI on :6274, proxy on :6277)
+# ---------------------------------------------------------------------------
+# DANGEROUSLY_OMIT_AUTH disables the session-token requirement so the plain
+# http://localhost:6274/ URL works without a token (fine for a local demo).
+# MCP_AUTO_OPEN_ENABLED=false keeps it from popping a browser when 
backgrounded.
+# The Inspector binds to "localhost" (may be IPv6 ::1), so probe via localhost.
+if nc -z localhost "${INSPECTOR_UI_PORT}" 2>/dev/null; then
+  log "An Inspector is already listening on ${INSPECTOR_UI_PORT}; leaving it 
as-is."
+else
+  log "Starting MCP Inspector on http://localhost:${INSPECTOR_UI_PORT} ..."
+  (
+    cd "${MCP_DIR}"
+    DANGEROUSLY_OMIT_AUTH=true \
+    MCP_AUTO_OPEN_ENABLED=false \
+    nohup npx @modelcontextprotocol/inspector \
+      > "${MCP_DIR}/.inspector-demo-inspector.out" 2>&1 &
+    echo $! > "${INSPECTOR_PID_FILE}"
+  )
+  for i in $(seq 1 60); do
+    nc -z localhost "${INSPECTOR_UI_PORT}" 2>/dev/null && break
+    sleep 1
+  done
+  if nc -z localhost "${INSPECTOR_UI_PORT}" 2>/dev/null; then
+    log "Inspector is up (pid $(cat "${INSPECTOR_PID_FILE}"))."
+  else
+    log "WARN: Inspector did not come up; see 
${MCP_DIR}/.inspector-demo-inspector.out"
+    log "      (You can still start it manually: npx 
@modelcontextprotocol/inspector)"
+  fi
+fi
+
+# ---------------------------------------------------------------------------
+# 7. Print connection details
+# ---------------------------------------------------------------------------
+cat <<EOF
+
+============================================================
+ Demo environment is ready.
+============================================================
+ Gravitino : ${GRAVITINO_URI}   (simple auth + authorization)
+ MCP server: ${MCP_URL}   (Streamable HTTP)
+ Metalake  : ${MCP_METALAKE}
+ Audit log : ${MCP_DIR}/gravitino-mcp-audit.log
+
+ >>> Open the Inspector:  http://localhost:${INSPECTOR_UI_PORT}/
+
+ In the Inspector, connect with:
+   Transport Type : Streamable HTTP
+   URL            : ${MCP_URL}
+   Header Name    : Authorization
+   Header Value   : ${ADMIN_AUTH}     <- admin
+                    Basic $(printf '%s' 'bob:dummy' | base64)     <- bob
+
+ Watch audit records live:
+   tail -f ${MCP_DIR}/gravitino-mcp-audit.log
+
+ Tear everything down (includes the Inspector):
+   ./dev/stop_inspector_demo.sh
+============================================================
+EOF
diff --git a/mcp-server/dev/stop_inspector_demo.sh 
b/mcp-server/dev/stop_inspector_demo.sh
new file mode 100755
index 0000000000..0335dac0f2
--- /dev/null
+++ b/mcp-server/dev/stop_inspector_demo.sh
@@ -0,0 +1,77 @@
+#!/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.
+#
+# Tear down the demo environment started by start_inspector_demo.sh:
+# stops the MCP server, stops Gravitino, and restores the original config.
+
+set -uo pipefail
+
+MCP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+REPO_ROOT="$(cd "${MCP_DIR}/.." && pwd)"
+
+MCP_PORT="${MCP_PORT:-8000}"
+MCP_PID_FILE="${MCP_DIR}/.inspector-demo-mcp.pid"
+INSPECTOR_UI_PORT="${INSPECTOR_UI_PORT:-6274}"
+INSPECTOR_PROXY_PORT="${INSPECTOR_PROXY_PORT:-6277}"
+INSPECTOR_PID_FILE="${MCP_DIR}/.inspector-demo-inspector.pid"
+
+log() { echo "[demo] $*"; }
+
+if [[ -z "${GRAVITINO_HOME:-}" ]]; then
+  GRAVITINO_HOME="${REPO_ROOT}/distribution/package"
+fi
+
+# 1. Stop the MCP Inspector (npx spawns child processes; free its ports too).
+if [[ -f "${INSPECTOR_PID_FILE}" ]]; then
+  INSPECTOR_PID="$(cat "${INSPECTOR_PID_FILE}")"
+  kill "${INSPECTOR_PID}" 2>/dev/null && \
+    log "Stopped Inspector (pid ${INSPECTOR_PID})" || true
+  rm -f "${INSPECTOR_PID_FILE}"
+fi
+for port in "${INSPECTOR_UI_PORT}" "${INSPECTOR_PROXY_PORT}"; do
+  lsof -ti :"${port}" 2>/dev/null | xargs kill -9 2>/dev/null && \
+    log "Freed Inspector port ${port}" || true
+done
+
+# 2. Stop the MCP server.
+if [[ -f "${MCP_PID_FILE}" ]]; then
+  MCP_PID="$(cat "${MCP_PID_FILE}")"
+  if kill "${MCP_PID}" 2>/dev/null; then
+    log "Stopped MCP server (pid ${MCP_PID})"
+  fi
+  rm -f "${MCP_PID_FILE}"
+fi
+# Belt and suspenders: free the port if anything is still bound.
+lsof -ti :"${MCP_PORT}" 2>/dev/null | xargs kill -9 2>/dev/null && \
+  log "Freed port ${MCP_PORT}" || true
+
+# 3. Stop Gravitino.
+if [[ -x "${GRAVITINO_HOME}/bin/gravitino.sh" ]]; then
+  "${GRAVITINO_HOME}/bin/gravitino.sh" stop || true
+  log "Stopped Gravitino"
+fi
+
+# 4. Restore the original config.
+CONF="${GRAVITINO_HOME}/conf/gravitino.conf"
+if [[ -f "${CONF}.inspector-demo.bak" ]]; then
+  mv "${CONF}.inspector-demo.bak" "${CONF}"
+  log "Restored original gravitino.conf"
+fi
+
+log "Teardown complete."

Reply via email to