Skip to content

feat: generate RAG service YAML config resource#313

Merged
tsivaprasad merged 5 commits intomainfrom
PLAT-491-rag-service-yaml-config-generation-swarm-config
Apr 7, 2026
Merged

feat: generate RAG service YAML config resource#313
tsivaprasad merged 5 commits intomainfrom
PLAT-491-rag-service-yaml-config-generation-swarm-config

Conversation

@tsivaprasad
Copy link
Copy Markdown
Contributor

@tsivaprasad tsivaprasad commented Mar 24, 2026

Summary

This PR adds RAGConfigResource, which generates pgedge-rag-server.yaml from pipeline configuration and writes it to the host data directory, completing the file-based config layer for the RAG service.

Changes

  • rag_config.go — YAML struct definitions mirroring the RAG server's
    Config struct and GenerateRAGConfig() generator; api_keys paths
    reference bind-mounted key files at /app/keys/{pipeline}_{embedding|rag}.key
  • rag_config_resource.goRAGConfigResource lifecycle (Create/Update/
    Refresh/Delete);
  • orchestrator.go — adds DirResource (host-side data directory) and
    RAGConfigResource to generateRAGInstanceResources; resource chain is now:
    DirResource → RAGServiceUserRole + RAGServiceKeysResource → RAGConfigResource
  • resources.go — registers ResourceTypeRAGConfig

Testing

Verification:

  1. Created a cluster

  2. Configured a database with RAG service (single-host and multi-host)
    rag_create_db.json
    rag_create_multi_host_db.json

  3. Confirmed successful database creation and generation of pgedge-rag-server.yaml with the expected data
    pgedge-rag-server.yaml

server:
  listen_address: 0.0.0.0
  port: 8080
pipelines:
- name: default
  description: Main RAG pipeline
  database:
    host: postgres-storefront-n1-689qacsi
    port: 5432
    database: storefront
    username: svc_rag_ro
    password: VzEYXCCFbcWh0exfguty56oeKI1bL9lVQhM7I990IP0=
    ssl_mode: prefer
  tables:
  - table: documents_content_chunks
    text_column: content
    vector_column: embedding
  embedding_llm:
    provider: openai
    model: text-embedding-3-small
  rag_llm:
    provider: anthropic
    model: claude-sonnet-4-5
  api_keys:
    anthropic: /app/keys/default_rag.key
    openai: /app/keys/default_embedding.key
  token_budget: 4000
  top_n: 15
  search:
    hybrid_enabled: true
    vector_weight: 0.7
defaults:
  token_budget: 2000
  top_n: 10


Checklist

  • Tests added

Notes for Reviewers

Container deployment is not part of this. The config file is written correctly but no container starts yet.

PLAT-491

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ec699031-0ebc-447d-8486-b911d039cc8e

📥 Commits

Reviewing files that changed from the base of the PR and between 2719035 and a86d831.

📒 Files selected for processing (2)
  • server/internal/orchestrator/swarm/rag_config_resource.go
  • server/internal/orchestrator/swarm/rag_config_resource_test.go
✅ Files skipped from review due to trivial changes (1)
  • server/internal/orchestrator/swarm/rag_config_resource_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/internal/orchestrator/swarm/rag_config_resource.go

📝 Walkthrough

Walkthrough

This pull request adds RAG configuration generation and resource management to the orchestrator swarm module. It introduces functions to generate pgedge-rag-server.yaml files, manages host-side configuration resources, registers resource types, integrates with the orchestrator, and includes comprehensive test coverage.

Changes

Cohort / File(s) Summary
RAG Configuration
rag_config.go, rag_config_test.go
Added GenerateRAGConfig function with helper utilities to generate pgedge-rag-server.yaml content. Includes pipeline YAML building, API key path generation with provider validation, database connection mapping, and comprehensive test coverage for configuration generation scenarios.
RAG Config Resource
rag_config_resource.go, rag_config_resource_test.go, resources.go
Introduced RAGConfigResource to manage host-side YAML configuration files with Create, Update, Refresh, and Delete operations. Includes file ownership management, dependency tracking, and resource registry integration. Tests validate identifier generation, dependencies, and resource lifecycle behavior.
Orchestrator Integration
orchestrator.go
Updated generateRAGInstanceResources to construct and append RAGConfigResource to the resource list alongside existing service resources. Populates resource with instance identifiers, service configuration, and database connection parameters from service specification.
Dependent Updates
rag_service_user_role_test.go, rag_service_keys_resource.go
Updated test expectations in rag_service_user_role_test.go to account for the new RAGConfigResource in generated resource counts and assertions. Minor formatting adjustment (blank line) in rag_service_keys_resource.go.

Poem

🐰 Hop-hop, the configs now generate with flair,
RAG pipelines and keys arranged with care,
Resources manage YAML files in place,
With proper ownership and database grace,
The orchestrator hops along the way!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding RAGConfigResource to generate the RAG service YAML config file.
Description check ✅ Passed The description covers Summary, Changes, Testing sections, and includes a Checklist with tests marked as added. Issue link (PLAT-491) is provided.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch PLAT-491-rag-service-yaml-config-generation-swarm-config

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 and usage tips.

@tsivaprasad
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

@tsivaprasad: I'll review the changes in this PR right away!

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/internal/orchestrator/swarm/orchestrator.go (1)

404-409: ⚠️ Potential issue | 🟠 Major

rag instances are now provisionable but never deployed.

GenerateServiceInstanceResources routes "rag" into generateRAGInstanceResources, but that path only returns filesystem/user/key/config resources. There is no ServiceInstanceSpecResource or ServiceInstanceResource, so nothing ever creates a Swarm service for the persisted ServiceInstance. In practice this leaves RAG instances stuck in creating with no container that can ever become ready. Either keep the "rag" path disabled until deployment lands, or add the deployment resources in this PR.

Also applies to: 531-594

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/orchestrator/swarm/orchestrator.go` around lines 404 - 409,
The "rag" branch in GenerateServiceInstanceResources currently calls
generateRAGInstanceResources which only returns filesystem/user/key/config
resources and omits ServiceInstanceSpecResource and ServiceInstanceResource, so
RAG instances are never deployed; either remove/guard the "rag" case until
deployment support lands, or modify generateRAGInstanceResources to create and
return the same deployment resources as other service types (add
ServiceInstanceSpecResource and ServiceInstanceResource entries, including
container/service spec needed for Swarm creation) so the persisted
ServiceInstance can be turned into a running Swarm service; update the switch in
GenerateServiceInstanceResources and the body of generateRAGInstanceResources
(and mirrored logic around lines 531–594) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/internal/orchestrator/swarm/rag_config.go`:
- Around line 94-98: GenerateRAGConfig currently builds pipelines by calling
buildRAGPipelineYAML without validating that per-stage credentials match; this
lets a pipeline with EmbeddingLLM.APIKey and RAGLLM.APIKey set to different
values silently prefer one key and misauthenticate. Add a guard where pipelines
are iterated (in GenerateRAGConfig, and similarly in ParseRAGServiceConfig if
present) to detect when both EmbeddingLLM.APIKey and RAGLLM.APIKey are non-empty
for the same provider (e.g., "openai", "anthropic") and differ, then return an
error (fail fast) describing the mismatched keys and include the pipeline
identifier; update buildRAGPipelineYAML callers to rely on this validation so no
silent overwrite occurs.
- Around line 24-35: The YAML model is collapsing explicit zeros because
TokenBudget and TopN are plain ints with `omitempty`; update ragPipelineYAML to
use `*int` for TokenBudget and TopN (and change the corresponding numeric fields
in any related YAML structs such as ragDatabaseYAML and the RAGDefaults YAML
struct to `*int` as well), and preserve/propagate the pointers when mapping
between internal structs and these YAML structs so nil vs. explicit 0 is
preserved (look for all mappings that build/return ragPipelineYAML,
ragDatabaseYAML, and the RAGDefaults YAML structs and assign pointer values
instead of converting to plain ints).

---

Outside diff comments:
In `@server/internal/orchestrator/swarm/orchestrator.go`:
- Around line 404-409: The "rag" branch in GenerateServiceInstanceResources
currently calls generateRAGInstanceResources which only returns
filesystem/user/key/config resources and omits ServiceInstanceSpecResource and
ServiceInstanceResource, so RAG instances are never deployed; either
remove/guard the "rag" case until deployment support lands, or modify
generateRAGInstanceResources to create and return the same deployment resources
as other service types (add ServiceInstanceSpecResource and
ServiceInstanceResource entries, including container/service spec needed for
Swarm creation) so the persisted ServiceInstance can be turned into a running
Swarm service; update the switch in GenerateServiceInstanceResources and the
body of generateRAGInstanceResources (and mirrored logic around lines 531–594)
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2c6dcd4a-44fe-4d12-9fe1-c4b54b1b6cc8

📥 Commits

Reviewing files that changed from the base of the PR and between c384b7d and f67ddc3.

📒 Files selected for processing (8)
  • server/internal/orchestrator/swarm/orchestrator.go
  • server/internal/orchestrator/swarm/rag_config.go
  • server/internal/orchestrator/swarm/rag_config_resource.go
  • server/internal/orchestrator/swarm/rag_config_resource_test.go
  • server/internal/orchestrator/swarm/rag_config_test.go
  • server/internal/orchestrator/swarm/rag_service_user_role_test.go
  • server/internal/orchestrator/swarm/resources.go
  • server/internal/orchestrator/swarm/service_spec.go

@tsivaprasad tsivaprasad force-pushed the PLAT-490-rag-service-api-key-file-management branch 2 times, most recently from 7a20ebb to da808b3 Compare April 3, 2026 14:48
@tsivaprasad tsivaprasad force-pushed the PLAT-491-rag-service-yaml-config-generation-swarm-config branch from 64e5c45 to f0b6b0a Compare April 4, 2026 19:00
@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Apr 4, 2026

Up to standards ✅

🟢 Issues 3 medium

Results:
3 new issues

Category Results
Complexity 3 medium

View in Codacy

🟢 Metrics 58 complexity · 14 duplication

Metric Results
Complexity 58
Duplication 14

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

@tsivaprasad
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

@tsivaprasad: I'll review the changes in this PR right away!


🧠 Learnings used
Learnt from: tsivaprasad
Repo: pgEdge/control-plane PR: 302
File: server/internal/orchestrator/swarm/orchestrator.go:686-704
Timestamp: 2026-04-04T13:55:25.081Z
Learning: In `server/internal/orchestrator/swarm/orchestrator.go` (Go), the `generateRAGInstanceResources` function intentionally omits the `ServiceInstanceSpecResource`/`ServiceInstanceResource` pair (and the corresponding `KeysPath` mount wiring). The deployable service spec/instance resources for RAG are deferred to a subsequent ticket. Do not flag the absence of `ServiceInstanceSpecResource`/`ServiceInstanceResource` in `generateRAGInstanceResources` as a missing implementation during code review.
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tsivaprasad
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

@tsivaprasad: I'll review the changes in this PR right away!


🧠 Learnings used
Learnt from: tsivaprasad
Repo: pgEdge/control-plane PR: 302
File: server/internal/orchestrator/swarm/orchestrator.go:686-704
Timestamp: 2026-04-04T13:55:26.501Z
Learning: In `server/internal/orchestrator/swarm/orchestrator.go` (Go), the `generateRAGInstanceResources` function intentionally omits the `ServiceInstanceSpecResource`/`ServiceInstanceResource` pair (and the corresponding `KeysPath` mount wiring). The deployable service spec/instance resources for RAG are deferred to a subsequent ticket. Do not flag the absence of `ServiceInstanceSpecResource`/`ServiceInstanceResource` in `generateRAGInstanceResources` as a missing implementation during code review.

Learnt from: rshoemaker
Repo: pgEdge/control-plane PR: 287
File: server/internal/orchestrator/swarm/service_user_role.go:59-60
Timestamp: 2026-03-11T16:05:35.061Z
Learning: In pgEdge/control-plane server/internal/orchestrator/swarm/service_user_role.go (Go): A ResourceVersion bump (e.g., "2" → "3") in ServiceUserRole is a documentation/safety marker only. When the resource Identifier key changes (e.g., from ServiceInstanceID to ServiceID), recreation is driven automatically by the (Type, ID) key mismatch — old resources with the previous identifier are deleted and new ones are created. Do not flag a ResourceVersion bump as a missing recreate trigger when an Identifier change is already causing full recreate.

Learnt from: CR
Repo: pgEdge/control-plane PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T16:43:14.333Z
Learning: Applies to server/internal/orchestrator/swarm/**/*.go : Docker Swarm integration should use services for Postgres instances, overlay networks for database isolation, and bind mounts for configuration and data directories

Learnt from: jason-lynch
Repo: pgEdge/control-plane PR: 295
File: server/internal/orchestrator/systemd/orchestrator.go:361-363
Timestamp: 2026-03-24T16:43:04.216Z
Learning: In `server/internal/orchestrator/systemd/orchestrator.go` (Go), `GenerateServiceInstanceResources` and `GetServiceInstanceStatus` on the SystemD `Orchestrator` intentionally return `errors.New("unimplemented")`. Service-instance support for the SystemD orchestrator is tracked in a separate ticket and will be addressed there. Do not flag these as missing implementations during code review.
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
server/internal/orchestrator/swarm/rag_service_user_role_test.go (1)

207-210: Strengthen this case by asserting trailing resource types too.

Line 207 updates the expected count, but this test still doesn’t verify that the appended resources are Dir -> RAGServiceKeys -> RAGConfig. Adding those checks will catch order/type regressions in this path as well.

Proposed test hardening
@@
 	if !perNodeNames["n1"] || !perNodeNames["n3"] {
 		t.Errorf("per-node resources = %v, want n1 and n3", perNodeNames)
 	}
+
+	// Data dir, keys, and config resource are appended last.
+	if result.Resources[3].Identifier.Type != filesystem.ResourceTypeDir {
+		t.Errorf("Resources[3].Identifier.Type = %q, want %q",
+			result.Resources[3].Identifier.Type, filesystem.ResourceTypeDir)
+	}
+	if result.Resources[4].Identifier.Type != ResourceTypeRAGServiceKeys {
+		t.Errorf("Resources[4].Identifier.Type = %q, want %q",
+			result.Resources[4].Identifier.Type, ResourceTypeRAGServiceKeys)
+	}
+	if result.Resources[5].Identifier.Type != ResourceTypeRAGConfig {
+		t.Errorf("Resources[5].Identifier.Type = %q, want %q",
+			result.Resources[5].Identifier.Type, ResourceTypeRAGConfig)
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/orchestrator/swarm/rag_service_user_role_test.go` around
lines 207 - 210, The test currently only asserts len(result.Resources) == 6 but
should also verify the trailing resource types/order to catch regressions;
update the test (in rag_service_user_role_test.go) to assert that
result.Resources[len(result.Resources)-3].Type == "Dir",
result.Resources[len(result.Resources)-2].Type == "RAGServiceKeys", and
result.Resources[len(result.Resources)-1].Type == "RAGConfig" (or equivalent
fields used to identify resource type in the Resource struct) so the final three
appended resources are validated in sequence.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@server/internal/orchestrator/swarm/rag_service_user_role_test.go`:
- Around line 207-210: The test currently only asserts len(result.Resources) ==
6 but should also verify the trailing resource types/order to catch regressions;
update the test (in rag_service_user_role_test.go) to assert that
result.Resources[len(result.Resources)-3].Type == "Dir",
result.Resources[len(result.Resources)-2].Type == "RAGServiceKeys", and
result.Resources[len(result.Resources)-1].Type == "RAGConfig" (or equivalent
fields used to identify resource type in the Resource struct) so the final three
appended resources are validated in sequence.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: edeeca6b-a1ee-4d95-b72b-f1bfacc301b7

📥 Commits

Reviewing files that changed from the base of the PR and between f0b6b0a and 863297f.

📒 Files selected for processing (2)
  • server/internal/orchestrator/swarm/orchestrator.go
  • server/internal/orchestrator/swarm/rag_service_user_role_test.go
✅ Files skipped from review due to trivial changes (1)
  • server/internal/orchestrator/swarm/orchestrator.go

Copy link
Copy Markdown
Member

@jason-lynch jason-lynch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! The only suggestion I have is very minor.

Copy link
Copy Markdown
Contributor

@rshoemaker rshoemaker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just the one question on config updates below...

return fmt.Errorf("failed to get service data dir path: %w", err)
}

return r.writeConfigFile(fs, dirPath, rc)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to do anything else after a config update (like send a SIGHUP to the RAG server)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — no SIGHUP handling yet in RAG Server. We have PLAT-497 tracked for this in Control Plane. The plan is to work on it in parallel:

RAG server & Control Plane — same pattern as MCP

@tsivaprasad tsivaprasad changed the base branch from PLAT-490-rag-service-api-key-file-management to main April 7, 2026 18:47
RAGConfigResource was missing from the RAG resource chain due to a
rebase conflict resolution that took the HEAD version of
generateRAGInstanceResources (which predated RAGConfigResource).
The resource is now appended after DirResource and RAGServiceKeysResource,
completing the chain required to write pgedge-rag-server.yaml.

PLAT-491
@tsivaprasad tsivaprasad force-pushed the PLAT-491-rag-service-yaml-config-generation-swarm-config branch from fbf00c8 to 2719035 Compare April 7, 2026 18:57
@tsivaprasad tsivaprasad force-pushed the PLAT-491-rag-service-yaml-config-generation-swarm-config branch from 2719035 to a86d831 Compare April 7, 2026 19:00
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
server/internal/orchestrator/swarm/rag_config_test.go (1)

11-19: These assertions are round-tripping the production YAML schema.

parseRAGYAML unmarshals back into ragYAMLConfig, so tag/schema regressions can still pass because the same tags are used on both sides. It also collapses omitted string fields to zero values, so checks like description == "", system_prompt == "", and id_column == "" do not actually prove omitempty behavior. For schema-sensitive cases, decode into an independent test struct or a raw map[string]any.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/orchestrator/swarm/rag_config_test.go` around lines 11 - 19,
The test currently round-trips production struct ragYAMLConfig via parseRAGYAML
which masks tag/omitempty regressions; change parseRAGYAML (and tests that call
it) to unmarshal the generated YAML into an independent representation (either a
dedicated test struct that does NOT reuse ragYAMLConfig or into a raw
map[string]any / yaml.Node) and assert on presence/absence of keys and actual
YAML values (e.g., check that keys like "description", "system_prompt",
"id_column" are omitted rather than empty strings). Update references to
parseRAGYAML and any assertions in the tests that rely on ragYAMLConfig to use
the new independent type or map checks so schema and omitempty behavior are
validated independently of GenerateRAGConfig.
server/internal/orchestrator/swarm/rag_config_resource_test.go (1)

10-76: The behaviorful resource paths still need coverage.

This suite only exercises metadata helpers. Create/Update/Refresh—dir resolution, ServiceUserRole lookup, file write, and ownership change—remain untested, so the main regression surface for this resource can slip through. A small afero-backed lifecycle test would close that gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/orchestrator/swarm/rag_config_resource_test.go` around lines
10 - 76, Add an afero-backed lifecycle test that exercises
RAGConfigResource.Create, Update and Refresh to cover directory resolution,
ServiceUserRole lookup, key file write and ownership change: instantiate an
afero.NewMemMapFs (or mem fs) and a fake resource manager that returns
filesystem.DirResourceIdentifier("...") for DirResourceID and
ServiceUserRoleIdentifier("rag", ServiceUserRoleRO) for the service role, call
r.Create(ctx, mgr) then r.Update/Refresh and assert the expected file(s) exist
with correct contents, modes and ownership semantics (owner checks can be
simulated via metadata on the fake fs or mock owner-changer), and ensure
RAGServiceKeysResourceIdentifier("...") interactions are exercised; use the same
unique symbols (RAGConfigResource.Create, Update, Refresh,
DirResourceIdentifier, ServiceUserRoleIdentifier,
RAGServiceKeysResourceIdentifier) so the test hooks the real code paths rather
than only metadata helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/internal/orchestrator/swarm/rag_config_resource.go`:
- Around line 149-153: The write path using afero.WriteFile leaves existing file
modes unchanged; after calling afero.WriteFile(fs, configPath, content, 0o600)
ensure you explicitly enforce strict permissions by calling fs.Chmod(configPath,
0o600) before changing ownership — add fs.Chmod(configPath, 0o600) immediately
after the WriteFile call (and before fs.Chown(configPath, ragContainerUID,
ragContainerUID)) so pgedge-rag-server.yaml always ends up with 0o600 regardless
of prior mode.

---

Nitpick comments:
In `@server/internal/orchestrator/swarm/rag_config_resource_test.go`:
- Around line 10-76: Add an afero-backed lifecycle test that exercises
RAGConfigResource.Create, Update and Refresh to cover directory resolution,
ServiceUserRole lookup, key file write and ownership change: instantiate an
afero.NewMemMapFs (or mem fs) and a fake resource manager that returns
filesystem.DirResourceIdentifier("...") for DirResourceID and
ServiceUserRoleIdentifier("rag", ServiceUserRoleRO) for the service role, call
r.Create(ctx, mgr) then r.Update/Refresh and assert the expected file(s) exist
with correct contents, modes and ownership semantics (owner checks can be
simulated via metadata on the fake fs or mock owner-changer), and ensure
RAGServiceKeysResourceIdentifier("...") interactions are exercised; use the same
unique symbols (RAGConfigResource.Create, Update, Refresh,
DirResourceIdentifier, ServiceUserRoleIdentifier,
RAGServiceKeysResourceIdentifier) so the test hooks the real code paths rather
than only metadata helpers.

In `@server/internal/orchestrator/swarm/rag_config_test.go`:
- Around line 11-19: The test currently round-trips production struct
ragYAMLConfig via parseRAGYAML which masks tag/omitempty regressions; change
parseRAGYAML (and tests that call it) to unmarshal the generated YAML into an
independent representation (either a dedicated test struct that does NOT reuse
ragYAMLConfig or into a raw map[string]any / yaml.Node) and assert on
presence/absence of keys and actual YAML values (e.g., check that keys like
"description", "system_prompt", "id_column" are omitted rather than empty
strings). Update references to parseRAGYAML and any assertions in the tests that
rely on ragYAMLConfig to use the new independent type or map checks so schema
and omitempty behavior are validated independently of GenerateRAGConfig.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d35b942f-f641-45bf-9420-5d13ab97faa9

📥 Commits

Reviewing files that changed from the base of the PR and between 863297f and 2719035.

📒 Files selected for processing (9)
  • server/internal/orchestrator/swarm/orchestrator.go
  • server/internal/orchestrator/swarm/rag_config.go
  • server/internal/orchestrator/swarm/rag_config_resource.go
  • server/internal/orchestrator/swarm/rag_config_resource_test.go
  • server/internal/orchestrator/swarm/rag_config_test.go
  • server/internal/orchestrator/swarm/rag_service_keys_resource.go
  • server/internal/orchestrator/swarm/rag_service_user_role_test.go
  • server/internal/orchestrator/swarm/resources.go
  • server/internal/orchestrator/swarm/service_spec.go
✅ Files skipped from review due to trivial changes (3)
  • server/internal/orchestrator/swarm/service_spec.go
  • server/internal/orchestrator/swarm/rag_service_keys_resource.go
  • server/internal/orchestrator/swarm/resources.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/internal/orchestrator/swarm/orchestrator.go

Comment on lines +149 to +153
if err := afero.WriteFile(fs, configPath, content, 0o600); err != nil {
return fmt.Errorf("failed to write %s: %w", configPath, err)
}
if err := fs.Chown(configPath, ragContainerUID, ragContainerUID); err != nil {
return fmt.Errorf("failed to change ownership for %s: %w", configPath, err)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does Go's os.WriteFile, and github.com/spf13/afero.WriteFile which mirrors it, apply the provided file mode when rewriting an existing file, or is the mode only used when the file is created?

💡 Result:

For Go’s os.WriteFile: the perm argument is only used when the file is created. If the file already exists, os.WriteFile truncates and overwrites it without changing its permissions/mode. [1]

For github.com/spf13/afero.WriteFile / Afero.WriteFile: it follows the same rule—creates the file with perm if it doesn’t exist; otherwise it truncates before writing (i.e., it does not apply perm as a chmod step when overwriting an existing file). [2]

Sources:
[1] Go os.WriteFile docs (pkg.go.dev) (pkg.go.dev)
[2] Afero WriteFile docs (pkg.go.dev) (pkg.go.dev)

Citations:


Add explicit Chmod after WriteFile to enforce strict permissions on every write.

The perm argument in afero.WriteFile is only applied when creating a new file. When the file already exists, WriteFile truncates and overwrites it without resetting the mode. If pgedge-rag-server.yaml previously had broader permissions (e.g., 0o644), the DB credentials remain readable by unintended users. Call fs.Chmod(configPath, 0o600) immediately after the write to guarantee 0o600 on both create and update.

🔧 Suggested fix
 	if err := afero.WriteFile(fs, configPath, content, 0o600); err != nil {
 		return fmt.Errorf("failed to write %s: %w", configPath, err)
 	}
+	if err := fs.Chmod(configPath, 0o600); err != nil {
+		return fmt.Errorf("failed to change mode for %s: %w", configPath, err)
+	}
 	if err := fs.Chown(configPath, ragContainerUID, ragContainerUID); err != nil {
 		return fmt.Errorf("failed to change ownership for %s: %w", configPath, err)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/internal/orchestrator/swarm/rag_config_resource.go` around lines 149 -
153, The write path using afero.WriteFile leaves existing file modes unchanged;
after calling afero.WriteFile(fs, configPath, content, 0o600) ensure you
explicitly enforce strict permissions by calling fs.Chmod(configPath, 0o600)
before changing ownership — add fs.Chmod(configPath, 0o600) immediately after
the WriteFile call (and before fs.Chown(configPath, ragContainerUID,
ragContainerUID)) so pgedge-rag-server.yaml always ends up with 0o600 regardless
of prior mode.

@tsivaprasad tsivaprasad merged commit b6b6f3b into main Apr 7, 2026
3 checks passed
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.

3 participants