feat: inject provisioned credentials into component deployments
Components now automatically receive DATABASE_URL, REDIS_URL, and other infrastructure credentials when deployed. Previously, credentials were provisioned and stored but never injected into K8s deployments. Changes: - Add fetchProjectCredentials() to component_deploy.go - Populate spec.Secrets before calling deployer.Deploy() - Fix slackpath-4 to provision postgres + redis before services - Add terminology docs to clarify platform vs skeleton code This completes the infrastructure provisioning flow: 1. add-db → provisions CockroachDB, stores DATABASE_URL 2. add-service → deploys with DATABASE_URL in environment Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
34e12ff3d5
commit
1e853980e4
10
CLAUDE.md
10
CLAUDE.md
@ -4,6 +4,16 @@ Run Claude Code instances in isolated Kubernetes pods with REST API control. Ena
|
||||
|
||||
**Platform:** threesix.ai - Agent-driven development at scale with shared worker pools.
|
||||
|
||||
## Terminology
|
||||
|
||||
| Term | Meaning | Location |
|
||||
|------|---------|----------|
|
||||
| **platform** | rdev itself (orchestrator API, handlers, workers) | `cmd/rdev-api/`, `internal/`, `pkg/api/` |
|
||||
| **skeleton** | Code that ships in generated projects | `internal/adapter/templates/templates/skeleton/` |
|
||||
| **component templates** | Service/worker/app/cli templates added to skeleton | `templates/components/{service,worker,cli,app-*}/` |
|
||||
|
||||
When discussing code: "add to **platform**" = edit rdev; "add to **skeleton**" = edit project templates.
|
||||
|
||||
## Find Your Guide
|
||||
|
||||
| If you need to... | Read this |
|
||||
|
||||
@ -5,7 +5,9 @@
|
||||
|
||||
## Summary
|
||||
|
||||
Composable Monorepo Templates evolve rdev's project scaffolding from single templates to full monorepo architecture. Every project starts as a monorepo skeleton, with components (services, workers, apps, cli) added via API calls. Deployment can target the whole monorepo or individual components.
|
||||
Composable Monorepo Templates evolve rdev's project scaffolding from single templates to full monorepo architecture. Every project starts with the **skeleton** (monorepo base), with **component templates** (services, workers, apps, cli) added via API calls. Deployment can target the whole monorepo or individual components.
|
||||
|
||||
**Terminology:** "skeleton" = monorepo base code; "platform" = rdev itself. See [terminology.md](../terminology.md).
|
||||
|
||||
**Key Facts:**
|
||||
- `POST /projects` creates monorepo skeleton (not single template)
|
||||
|
||||
@ -4,6 +4,8 @@ Quick reference for rdev concepts and facts.
|
||||
|
||||
| Topic | File | Confidence | Updated | Summary |
|
||||
|-------|------|------------|---------|---------|
|
||||
| **Terminology** |
|
||||
| Platform vs Skeleton | [terminology.md](./terminology.md) | High | 2026-02 | Distinguishing rdev code from generated project code |
|
||||
| **Architecture** |
|
||||
| Hexagonal Architecture | [patterns/hexagonal.md](./patterns/hexagonal.md) | High | 2025-01 | Ports/adapters pattern, layer separation |
|
||||
| **Core Services** |
|
||||
|
||||
35
ai-lookup/terminology.md
Normal file
35
ai-lookup/terminology.md
Normal file
@ -0,0 +1,35 @@
|
||||
# rdev Terminology
|
||||
|
||||
**Last Updated:** 2026-02-05
|
||||
**Confidence:** High
|
||||
|
||||
## Summary
|
||||
|
||||
Distinguishing between rdev code (the platform) and generated project code (skeleton) prevents confusion when discussing implementations.
|
||||
|
||||
## Core Terms
|
||||
|
||||
| Term | Definition | Code Location |
|
||||
|------|------------|---------------|
|
||||
| **platform** | rdev API itself—handlers, services, adapters, workers | `cmd/rdev-api/`, `internal/`, `pkg/api/` |
|
||||
| **skeleton** | Monorepo base that ships in every generated project | `templates/skeleton/` |
|
||||
| **component templates** | Templates for services, workers, apps, CLIs added to skeleton | `templates/components/` |
|
||||
| **generated project** | A project created by rdev (contains skeleton + components) | Lives in Gitea repos |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
- "Add auth middleware to **platform**" → edit `internal/auth/`
|
||||
- "Add auth middleware to **skeleton**" → edit `templates/skeleton/pkg/auth/`
|
||||
- "Platform's work queue" → `internal/adapter/postgres/work_queue.go`
|
||||
- "Skeleton's job queue" → `templates/skeleton/pkg/queue/` (planned)
|
||||
|
||||
## File Pointers
|
||||
|
||||
- Platform entry: `cmd/rdev-api/main.go`
|
||||
- Skeleton root: `internal/adapter/templates/templates/skeleton/`
|
||||
- Component registry: `internal/adapter/templates/registry.go`
|
||||
|
||||
## Related
|
||||
|
||||
- [Composable Monorepo](./features/composable-monorepo.md) - Skeleton + component architecture
|
||||
- [Template Provider](./services/template-provider.md) - How templates are seeded
|
||||
@ -19,19 +19,42 @@ steps:
|
||||
- project_id: .data.name
|
||||
- domain: .data.domain
|
||||
|
||||
add-db:
|
||||
description: Add CockroachDB for user/auth storage
|
||||
depends_on: [create-project]
|
||||
action: api
|
||||
method: POST
|
||||
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components"
|
||||
body:
|
||||
type: postgres
|
||||
name: "main-db"
|
||||
|
||||
add-redis:
|
||||
description: Add Redis for job queue and pub/sub
|
||||
depends_on: [create-project]
|
||||
action: api
|
||||
method: POST
|
||||
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components"
|
||||
body:
|
||||
type: redis
|
||||
name: "job-queue"
|
||||
|
||||
add-auth:
|
||||
depends_on: [add-db]
|
||||
action: api
|
||||
method: POST
|
||||
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components"
|
||||
body: { type: service, name: "auth-svc" }
|
||||
|
||||
add-chat:
|
||||
depends_on: [add-redis]
|
||||
action: api
|
||||
method: POST
|
||||
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components"
|
||||
body: { type: service, name: "chat-svc" }
|
||||
|
||||
add-worker:
|
||||
depends_on: [add-redis]
|
||||
action: api
|
||||
method: POST
|
||||
endpoint: "/projects/{{ .outputs.create-project.project_id }}/components"
|
||||
|
||||
@ -32,6 +32,9 @@ func (s *ComponentService) createInitialComponentDeployment(
|
||||
// Build sibling service URLs for service discovery
|
||||
siblingServices := s.buildSiblingServiceURLs(ctx, projectID, component.Name)
|
||||
|
||||
// Fetch project credentials (DATABASE_URL, REDIS_URL, etc.) from credential store
|
||||
secrets := s.fetchProjectCredentials(ctx, projectID)
|
||||
|
||||
spec := domain.DeploySpec{
|
||||
ProjectName: projectID,
|
||||
ComponentPath: component.Path,
|
||||
@ -41,6 +44,7 @@ func (s *ComponentService) createInitialComponentDeployment(
|
||||
Replicas: 1,
|
||||
BasePath: basePath,
|
||||
SiblingServices: siblingServices,
|
||||
Secrets: secrets,
|
||||
}
|
||||
|
||||
log := logging.FromContext(ctx).WithService("component")
|
||||
@ -152,3 +156,58 @@ func (s *ComponentService) buildSiblingServiceURLs(ctx context.Context, projectI
|
||||
func toUpperSnake(s string) string {
|
||||
return strings.ToUpper(strings.ReplaceAll(s, "-", "_"))
|
||||
}
|
||||
|
||||
// fetchProjectCredentials retrieves stored infrastructure credentials for a project.
|
||||
// Returns a map of env var names to values (e.g., {"DATABASE_URL": "postgres://...", "REDIS_URL": "redis://..."}).
|
||||
// Missing credentials are silently skipped - not all projects have all infrastructure.
|
||||
func (s *ComponentService) fetchProjectCredentials(ctx context.Context, projectID string) map[string]string {
|
||||
if s.credentialStore == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List of infrastructure credentials to fetch
|
||||
credentialKeys := []string{
|
||||
"DATABASE_URL",
|
||||
"DATABASE_URL_STAGING",
|
||||
"REDIS_URL",
|
||||
"REDIS_URL_STAGING",
|
||||
"REDIS_PREFIX",
|
||||
}
|
||||
|
||||
// Build scoped keys: "{projectID}:{key}"
|
||||
scopedKeys := make([]string, len(credentialKeys))
|
||||
for i, key := range credentialKeys {
|
||||
scopedKeys[i] = projectID + ":" + key
|
||||
}
|
||||
|
||||
// Fetch all credentials in one call
|
||||
creds, err := s.credentialStore.GetMultiple(ctx, scopedKeys)
|
||||
if err != nil {
|
||||
log := logging.FromContext(ctx).WithService("component")
|
||||
log.Warn("failed to fetch project credentials",
|
||||
logging.FieldProjectID, projectID,
|
||||
logging.FieldError, err,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert scoped keys back to env var names
|
||||
secrets := make(map[string]string)
|
||||
for scopedKey, value := range creds {
|
||||
// Extract env var name from scoped key: "myproject:DATABASE_URL" -> "DATABASE_URL"
|
||||
parts := strings.SplitN(scopedKey, ":", 2)
|
||||
if len(parts) == 2 && value != "" {
|
||||
secrets[parts[1]] = value
|
||||
}
|
||||
}
|
||||
|
||||
if len(secrets) > 0 {
|
||||
log := logging.FromContext(ctx).WithService("component")
|
||||
log.Debug("fetched project credentials for deployment",
|
||||
logging.FieldProjectID, projectID,
|
||||
"credential_count", len(secrets),
|
||||
)
|
||||
}
|
||||
|
||||
return secrets
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user