Visitar URL original
fix(#10536): the server seeds an empty configuration directory with the shipped defaults at startup by robfrank · Pull Request #10555 · ArcadeData/arcadedb · GitHub
Skip to content

fix(#10536): the server seeds an empty configuration directory with the shipped defaults at startup - #10555

Merged
robfrank merged 2 commits into
mainfrom
fix/10536-seed-config-directory-from-shipped-defaults
Oct 21, 2026
Merged

robfrank merged 2 commits into
mainfrom
fix/10536-seed-config-directory-from-shipped-defaults

Conversation

@robfrank

@robfrank robfrank commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #10536

Summary

A configuration directory moved off <root>/config (arcadedb.server.configDirectory pointing at a Kubernetes PersistentVolumeClaim, which is never populated from the image) started empty. The server then ran on its built-in groups, which have no readonly group, and Gremlin started from TinkerPop's bare Settings. The plain StatefulSet worked around it with an init container running /bin/sh, which the native images (scratch, distroless) do not have. That manifest was removed in #10542, and this PR implements the issue's suggested fix: the server seeds the directory itself at startup, from the <root>/config of the image it runs. It needs no shell and no second image reference, so it cannot copy another version's defaults.

  • ConfigDirectorySeeder copies server-groups.json, gremlin-server.yaml, gremlin-server.groovy and gremlin-server.properties. It runs in ArcadeDBServer.startInternal(), before security, the AI and support configurations and the plugins read the directory.
  • The seed is all or nothing: files are staged in a sub-directory and moved into place only once every copy succeeded, so a failure cannot leave a half-seeded directory that is never seeded again.
  • Only a directory that holds no regular file yet is seeded (missing, empty, or holding only a lost+found directory). From then on, what is in it or missing from it is the operator's: an edit is never overwritten and a file deleted on purpose does not come back.
  • Only the shipped defaults are copied. Users, API tokens or a server-configuration.json left under <root>/config by a server that ran there are not carried over.
  • The logging files are not seeded (the issue's second note): the JVM loads them through -Djava.util.logging.config.file, never from the configuration directory.
  • A failure is logged as a WARNING, and the server starts on its built-in defaults as before.
  • PackagingManifestsTest now requires every file under package/src/main/config to be either seeded or excluded with a reason, so a new shipped default cannot be forgotten.

Finding ledger

Test plan

  • Issue10536ConfigDirectorySeedTest (8 tests, including a seed that fails halfway and leaves no file behind): a started server seeds an empty moved directory byte for byte, and the security in force has the readonly group (red before the fix, when the server wrote its built-in groups instead); a directory in use is left alone; a deleted default stays deleted; a volume holding only lost+found is seeded; stale users, tokens and settings under <root>/config are not copied; the default (unmoved) directory and an embedded server with no shipped config are untouched.
  • PackagingManifestsTest.everyShippedConfigFileIsSeededIntoAMovedConfigDirectoryOrExcludedForAReason, checked to fail when an unplaced file is added to package/src/main/config.
  • Regression: mvn -o -pl server test -Dtest='*Security*Test,*Config*Test,*Group*Test,ServerConfigurationIT': 402 tests, 0 failures. Issue7415ConfigDirectoryTest, UnstartedServerTest: green.

Coverage

Invariant: a server whose configuration directory starts empty runs with the defaults its own image ships, on every image.

Sweep:

$ git grep -n "startInternal()" -- "*/src/main/java/*"
server/src/main/java/com/arcadedb/server/ArcadeDBServer.java:543:      startInternal();      <- the only caller, from start()
$ git grep -n "serverConfigPath" server/src/main/java/com/arcadedb/server/ArcadeDBServer.java
359, 375: resolved in both constructors
595: ConfigDirectorySeeder.seed(...)      <- new
658: new ServerSecurity(...)  672: InstanceIdResolver  682: AiConfiguration  686: SupportService
2792: loadConfiguration() (constructor, reads server-configuration.json, which is never seeded)
$ git grep -n "getConfigPath()" -- "*/src/main/java/*"
GremlinServerPlugin:93, MCPPlugin:74, ServerControlPlane:1746/1777/2067, AutoBackupSchedulerPlugin:65/363   <- plugins/handlers, all after startInternal() line 595
Entry point Covered by the fix? Test
ServerSecurity loading server-groups.json at start yes aServerStartedOnAnEmptyConfigDirectorySeedsItWithTheShippedDefaults
GremlinServerPlugin.startService() reading gremlin-server.yaml from getConfigPath() yes, the file is in place before startPlugins argued: the seed runs at line 595, plugins start later in the same method; the file is asserted in place by the test above
JVM launcher (server.sh) and native image (mainClass = ArcadeDBServer) yes, both start through ArcadeDBServer.start() argued: native/pom.xml:36 and ArcadeDBServer.main
gremlin-server.properties named in the yaml's graphs: not needed argued: ArcadeGraphManager only logs pre-configured graphs and never opens that path (ArcadeGraphManager.java:78-83)

Known gaps

Residual risk

Adversarial pass

Review cycles

  • cycle 1, dff0f39: initial fix. The claude review flagged that a seed failing halfway is never repaired (1), plus import order and overload naming (4), and a missing failure test (5). Also symlinks (3).
  • cycle 2, 7266953: seed made all or nothing through a staging directory, seedFromRoot renamed, imports sorted, failure test added. Claude review: LGTM, four non-blocking suggestions.

Deferred items

  • Cycle 1 (3), symlinks followed under <root>/config: skipped. The source is the image's own config directory, so following a link there is intended.
  • Cycle 2 (1), several servers starting on one shared directory can mix seeded files: skipped, since each node has its own directory in every supported layout. Each file is still either the shipped one or the other writer's, never a partial file.
  • Cycle 2 (2), a FINE log when configDirectory is a regular file: skipped, because the server fails on that path elsewhere with its own error.
  • Cycle 2 (3), document symlink following: skipped, same reason as cycle 1 (3).
  • Cycle 2 (4), split the half-failure test: skipped. The steps are one scenario (fail, no leftovers, recover) and each assertion carries its own message.

Final state

clean-approval (LGTM on 7266953, remaining suggestions non-blocking and recorded above).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Empty or relocated configuration directories are automatically populated with the shipped server configuration files during startup.
    • Existing configuration files are preserved, and only supported default configuration files are copied.
    • Servers can start with the shipped security policy when using an empty configuration volume.
  • Bug Fixes

    • Improved startup handling for empty, partially populated, or lost+found-only configuration directories.
    • Configuration seeding failures no longer prevent server startup; built-in defaults remain available.

…he shipped defaults at startup

A configuration directory moved off <root>/config (a Kubernetes volume claim, never populated from the
image) started empty, so the server ran without the shipped server-groups.json (no readonly group) and
the Gremlin server files. Seeding it took an init container running /bin/sh, which the native images
do not have. The server now copies the shipped defaults itself, from the image it runs, on the first
start against a directory that holds no file yet. The logging files are not seeded: the JVM never
reads them from the configuration directory.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 882fa465-e921-487d-814c-c59526af5221
📥 Commits

Reviewing files that changed from the base of the PR and between c270314 and 7266953.

📒 Files selected for processing: 4
  • server/src/main/java/com/arcadedb/server/ArcadeDBServer.java
  • server/src/main/java/com/arcadedb/server/ConfigDirectorySeeder.java
  • server/src/test/java/com/arcadedb/server/Issue10536ConfigDirectorySeedTest.java
  • server/src/test/java/com/arcadedb/server/PackagingManifestsTest.java

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

📝 Walkthrough

Server startup now seeds eligible configuration directories with selected shipped defaults. The seeder skips ineligible cases, handles staging and concurrent file creation, and logs failures without stopping startup. Tests cover seeding rules, startup behavior, and packaged-file coverage.

Changes: Configuration Directory Seeding

Layer / File(s) Summary
Seeding rules and safeguards
server/src/main/java/.../ConfigDirectorySeeder.java
Defines the allowlisted files and copies them into eligible targets. The seeder stages files, preserves concurrent files, and cleans up after failures.
Startup integration and validation
server/src/main/java/.../ArcadeDBServer.java, server/src/main/java/.../ConfigDirectorySeeder.java, server/src/test/java/.../Issue10536ConfigDirectorySeedTest.java, server/src/test/java/.../PackagingManifestsTest.java
Startup invokes the seeder. Tests check seeding behavior, startup, and consistency between shipped configuration files and the allowlist.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant ArcadeDBServer
  participant ConfigDirectorySeeder
  participant FileSystem
  ArcadeDBServer->>ConfigDirectorySeeder: seedFromRoot(serverRootPath, serverConfigPath)
  ConfigDirectorySeeder->>FileSystem: inspect target and copy eligible defaults
  ConfigDirectorySeeder-->>ArcadeDBServer: return after logging success or warning
Loading

Suggested reviewers: lvca

Merge Risk: ⚪ Minimal · up to 72669

The seeding change is ready to merge after normal checks; no unresolved issue was established.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 20 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: startup seeding of empty configuration directories with shipped defaults.
Description check Passed The description is comprehensive and covers the change, motivation, related issues, implementation details, tests, known gaps, and residual risks. It uses different headings from the template but prov…
Linked Issues check Passed Directly linked issue #10536 is closed and is historical context only. Per the issue state, it supplies no active coding requirements. The PR implements the described startup seeding approach, but tha…
Out of Scope Changes check Passed The changes remain within the PR scope described for #10536: startup seeding, all-or-nothing staging, allowlisted configuration files, startup integration, and focused packaging and behavior tests. Th…
Full details: Description check

Explanation

The description is comprehensive and covers the change, motivation, related issues, implementation details, tests, known gaps, and residual risks. It uses different headings from the template but provides the required information, including test coverage and validation results.

Full details: Linked Issues check

Explanation

Directly linked issue #10536 is closed and is historical context only. Per the issue state, it supplies no active coding requirements. The PR implements the described startup seeding approach, but that implementation is not required for this assessment.

Full details: Out of Scope Changes check

Explanation

The changes remain within the PR scope described for #10536: startup seeding, all-or-nothing staging, allowlisted configuration files, startup integration, and focused packaging and behavior tests. The test changes support the implementation. No unrelated change is established by the provided summary.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude

claude Bot commented Oct 21, 2026

Copy link
Copy Markdown

Review

Overall this is a clean, well-scoped fix. It needs no shell, copies only an allow-list of files, never overwrites (no REPLACE_EXISTING), and a failure only logs a WARNING. I found no security hazard: the copied file names are constants, so there is no path traversal, and nothing from <root>/config like users or tokens is carried over. The tests are thorough and cover the regression, the idempotency and the exclusion cases.

Points to consider

  1. Partial seeding is never repaired. The 'directory holds no regular file' check means that if the copy loop fails midway (disk full, I/O error), the first file(s) are written and every later start sees a non-empty directory. The server then runs permanently without the remaining defaults, for example gremlin-server.yaml. Consider copying each file to a temp name and moving it into place, and deleting the already-copied files if a later one fails. Alternatively, document this as accepted.
  2. Order of seeding. server-groups.json is first in SEEDED_FILES, so the most important file lands first. That is good, but the partial-state concern above still applies to the Gremlin files.
  3. Symlinks. Files.isRegularFile(from) follows symlinks, so a symlinked file under <root>/config is copied by content. That seems fine, but a NOFOLLOW_LINKS check would make the intent explicit if the image is not fully trusted.
  4. Style nits. The import order in ConfigDirectorySeeder has java.util.stream.Stream before java.util.List. The two seed overloads (public Path,Path and package-private String,String) differ only by argument type, so a distinct name for the second, such as seedFromRoot, would read more clearly.
  5. Test. aServerStartedOnAnEmptyConfigDirectory... is the only test that starts a server. That is appropriate. The only gap is that no test simulates a copy failure to confirm the server still starts, which would be easy with a read-only target directory.

None of these block the PR; point 1 is the one I would most like addressed or explicitly documented.

🤖 Generated with Claude Code

@mergify

mergify Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

This pull request does not currently match the merge queue conditions, so it cannot be queued from here. The box comes back if it matches again.

@codacy-production

codacy-production Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 24 complexity

Metric Results
Complexity 24

View in Codacy

🟢 Coverage 90.00% diff coverage

Metric Results
Coverage variation Report missing for a5661e91
Diff coverage ✅ 90.00% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (a5661e9) Report Missing Report Missing Report Missing
Head commit (7266953) 281024 230463 82.01%

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#10555) 60 54 90.00%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

1 Codacy didn't receive coverage data for the commit, or there was an error processing the received data. Check your integration for errors and validate that your coverage setup is correct.

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

…me the root overload, sort imports

A seed that failed halfway left a file behind, and a directory holding a file is never seeded again.
The files are now staged in a sub-directory and moved into place only once every copy succeeded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Oct 21, 2026

Copy link
Copy Markdown

Review

Overall this is a solid, well-scoped change. I found no security hazards, so I did not add the do-not-merge label.

What I checked

  • Security: only a fixed allow-list of four shipped files is copied, and the names are constants, so there is no path traversal. Stale users, tokens and settings are explicitly not carried over, and a test pins that. The staging directory from createTempDirectory is 0700 on POSIX, and the moved files get default permissions rather than the staging directory's. There is no new dependency.
  • Robustness: the seed is all or nothing. It stages the files, moves them with no REPLACE_EXISTING, rolls back on failure, and logs a WARNING and falls back to built-in defaults. A staging directory left by a crash is not a regular file, so a later start seeds again.
  • Performance: it is a one-off at startup that reads a single directory listing. It adds nothing to the hot paths.
  • Tests: the coverage is good. It includes a regression test that fails before the fix, a manifest guard in PackagingManifestsTest, and a half-failure rollback test with the root-user and non-POSIX cases skipped via assume. The tests follow the assertThat(...) style from CLAUDE.md and use a StaticBaseServerTest fixture with no hardcoded ports.

Minor suggestions (non-blocking)

  1. If several servers share one volume, the concurrent-start case can leave a mix of seeded files. The FileAlreadyExistsException branch keeps the other writer's file, and the rest still land. A short note in the Javadoc would be enough, since a shared config directory is unusual.
  2. holdsAFile treats any non-directory target as "holds a file". If configDirectory points at a regular file, the seeder silently skips it. That is probably right, because the server fails elsewhere, but a FINE log would help diagnosis.
  3. Files.isRegularFile(source.resolve(file)) follows symlinks. That is intended for a Kubernetes ConfigMap mount, but you could mention it in the Javadoc.
  4. aSeedThatFailsHalfwayLeavesNoFileBehind... mixes the seed, seedFromRoot and re-seed assertions in one test. Splitting them would make failures easier to read.

LGTM otherwise.

@robfrank
robfrank merged commit 416a64a into main Oct 21, 2026
31 of 33 checks passed
@robfrank
robfrank deleted the fix/10536-seed-config-directory-from-shipped-defaults branch October 21, 2026 21:43
@codecov

codecov Bot commented Oct 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.66667% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.80%. Comparing base (09ac837) to head (7266953).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...ava/com/arcadedb/server/ConfigDirectorySeeder.java 81.35% 6 Missing and 5 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main   #10555      +/-   ##
============================================
+ Coverage     78.77%   78.80%   +0.03%     
+ Complexity     4736     4734       -2     
============================================
  Files          2284     2287       +3     
  Lines        247913   248323     +410     
  Branches      53982    54065      +83     
============================================
+ Hits         195293   195701     +408     
+ Misses        32599    32587      -12     
- Partials      20021    20035      +14     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The StatefulSet seed runs /bin/sh and its new test requires the seed image to equal the server's, which excludes both of the project's own native images

1 participant