Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions go/internal/database/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package database

import (
"fmt"
"os"
"strings"
"sync"

"github.com/glebarez/sqlite"
Expand Down Expand Up @@ -30,7 +32,8 @@ type SqliteConfig struct {
}

type PostgresConfig struct {
URL string
URL string
URLFile string
}

type Config struct {
Expand Down Expand Up @@ -63,7 +66,15 @@ func NewManager(config *Config) (*Manager, error) {
TranslateError: true,
})
case DatabaseTypePostgres:
db, err = gorm.Open(postgres.Open(config.PostgresConfig.URL), &gorm.Config{
url := config.PostgresConfig.URL
if config.PostgresConfig.URLFile != "" {
resolved, resolveErr := resolveURLFile(config.PostgresConfig.URLFile)
if resolveErr != nil {
return nil, fmt.Errorf("failed to resolve postgres URL from file: %w", resolveErr)
}
url = resolved
}
db, err = gorm.Open(postgres.Open(url), &gorm.Config{
Logger: logger.Default.LogMode(logLevel),
TranslateError: true,
})
Expand Down Expand Up @@ -142,6 +153,20 @@ func (m *Manager) Reset(recreateTables bool) error {
return nil
}

// resolveURLFile reads a database connection URL from a file and returns the
// trimmed contents. Returns an error if the file cannot be read or is empty.
func resolveURLFile(path string) (string, error) {
content, err := os.ReadFile(path)
if err != nil {
return "", fmt.Errorf("reading URL file: %w", err)
}
url := strings.TrimSpace(string(content))
if url == "" {
return "", fmt.Errorf("URL file %s is empty or contains only whitespace", path)
}
return url, nil
}

// Close closes the database connection
func (m *Manager) Close() error {
if m.db == nil {
Expand Down
60 changes: 60 additions & 0 deletions go/internal/database/manager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package database

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

func TestResolveURLFile(t *testing.T) {
tests := []struct {
name string
fileContent string
wantUrl string
wantErr bool
}{
{
name: "reads URL from file",
fileContent: "postgres://testuser:testpass@host:5432/testdb",
wantUrl: "postgres://testuser:testpass@host:5432/testdb",
},
{
name: "trims whitespace and newlines",
fileContent: " postgres://user:pass@host:5432/db\n",
wantUrl: "postgres://user:pass@host:5432/db",
},
{
name: "empty file returns error",
fileContent: "",
wantErr: true,
},
{
name: "whitespace-only file returns error",
fileContent: " \n\t\n ",
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "db-url")
err := os.WriteFile(tmpFile, []byte(tt.fileContent), 0600)
assert.NoError(t, err)

url, err := resolveURLFile(tmpFile)
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.wantUrl, url)
})
}

t.Run("missing file returns error", func(t *testing.T) {
_, err := resolveURLFile("/nonexistent/path/db-url")
assert.Error(t, err)
})
}
11 changes: 7 additions & 4 deletions go/pkg/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,10 @@ type Config struct {
WatchNamespaces string
A2ABaseUrl string
Database struct {
Type string
Path string
Url string
Type string
Path string
Url string
UrlFile string
}
}

Expand Down Expand Up @@ -156,6 +157,7 @@ func (cfg *Config) SetFlags(commandLine *flag.FlagSet) {
commandLine.StringVar(&cfg.Database.Type, "database-type", "sqlite", "The type of the database to use. Supported values: sqlite, postgres.")
commandLine.StringVar(&cfg.Database.Path, "sqlite-database-path", "./kagent.db", "The path to the SQLite database file.")
commandLine.StringVar(&cfg.Database.Url, "postgres-database-url", "postgres://postgres:kagent@db.kagent.svc.cluster.local:5432/crud", "The URL of the PostgreSQL database.")
commandLine.StringVar(&cfg.Database.UrlFile, "postgres-database-url-file", "", "Path to a file containing the PostgreSQL database URL. Takes precedence over --postgres-database-url.")

commandLine.StringVar(&cfg.WatchNamespaces, "watch-namespaces", "", "The namespaces to watch for .")

Expand Down Expand Up @@ -350,7 +352,8 @@ func Start(getExtensionConfig GetExtensionConfig) {
DatabasePath: cfg.Database.Path,
},
PostgresConfig: &database.PostgresConfig{
URL: cfg.Database.Url,
URL: cfg.Database.Url,
URLFile: cfg.Database.UrlFile,
},
})
if err != nil {
Expand Down
17 changes: 17 additions & 0 deletions go/pkg/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,23 @@ func TestLoadFromEnvDurationFlags(t *testing.T) {
}
}

func TestDatabaseUrlFileFlag(t *testing.T) {
fs := flag.NewFlagSet("test", flag.ContinueOnError)
cfg := Config{}
cfg.SetFlags(fs)

// Verify the flag exists and has the right default
f := fs.Lookup("postgres-database-url-file")
assert.NotNil(t, f, "postgres-database-url-file flag should be registered")
assert.Equal(t, "", f.DefValue, "default should be empty string")

// Verify env var loading works for the new flag
t.Setenv("POSTGRES_DATABASE_URL_FILE", "/etc/credentials/db-url")
err := LoadFromEnv(fs)
assert.NoError(t, err)
assert.Equal(t, "/etc/credentials/db-url", cfg.Database.UrlFile)
}

func TestLoadFromEnvIntegration(t *testing.T) {
envVars := map[string]string{
"METRICS_BIND_ADDRESS": ":9090",
Expand Down
6 changes: 5 additions & 1 deletion helm/kagent/templates/controller-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ data:
{{- end }}
{{- if eq .Values.database.type "sqlite" }}
SQLITE_DATABASE_PATH: /sqlite-volume/{{ .Values.database.sqlite.databaseName }}
{{- else if and (eq .Values.database.type "postgres") (not (eq .Values.database.postgres.url "")) }}
{{- else if eq .Values.database.type "postgres" }}
{{- if not (eq .Values.database.postgres.urlFile "") }}
POSTGRES_DATABASE_URL_FILE: {{ .Values.database.postgres.urlFile | quote }}
{{- else if not (eq .Values.database.postgres.url "") }}
POSTGRES_DATABASE_URL: {{ .Values.database.postgres.url | quote }}
{{- end }}
{{- end }}
STREAMING_INITIAL_BUF_SIZE: {{ .Values.controller.streaming.initialBufSize | quote }}
STREAMING_MAX_BUF_SIZE: {{ .Values.controller.streaming.maxBufSize | quote }}
STREAMING_TIMEOUT: {{ .Values.controller.streaming.timeout | quote }}
Expand Down
3 changes: 3 additions & 0 deletions helm/kagent/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ database:
databaseName: kagent.db
postgres:
url: postgres://postgres:kagent@pgsql-postgresql.kagent.svc.cluster.local:5432/postgres
# Path to a file containing the database URL.
# Takes precedence over url when set.
urlFile: ""

# ==============================================================================
# CONTROLLER CONFIGURATION
Expand Down