-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathconnection.go
More file actions
114 lines (97 loc) · 3.8 KB
/
connection.go
File metadata and controls
114 lines (97 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package common
import (
"fmt"
"github.com/timescale/tiger-cli/internal/tiger/api"
)
// ConnectionDetailsOptions configures how the connection string is built
type ConnectionDetailsOptions struct {
// Pooled determines whether to use the pooler endpoint (if available)
Pooled bool
// Role is the database role/username to use (e.g., "tsdbadmin")
Role string
// WithPassword determines whether to include the password in the output
WithPassword bool
// InitialPassword is an optional password to use directly (e.g., from service creation response)
// If provided and WithPassword is true, this password will be used
// instead of fetching from password storage. This is useful when password_storage=none.
InitialPassword string
}
type ConnectionDetails struct {
Role string `json:"role,omitempty"`
Password string `json:"password,omitempty"`
Host string `json:"host,omitempty"`
Port int `json:"port,omitempty"`
Database string `json:"database,omitempty"`
IsPooler bool `json:"is_pooler,omitempty"`
}
func GetConnectionDetails(service api.Service, opts ConnectionDetailsOptions) (*ConnectionDetails, error) {
if service.Endpoint == nil {
return nil, fmt.Errorf("service endpoint not available")
}
// Use pooler endpoint if requested and available, otherwise use direct endpoint
var endpoint *api.Endpoint
isPooler := false
if opts.Pooled && service.ConnectionPooler != nil && service.ConnectionPooler.Endpoint != nil {
endpoint = service.ConnectionPooler.Endpoint
isPooler = true
} else {
// If pooled was requested but no pooler is available, fall back to direct connection
endpoint = service.Endpoint
}
if endpoint == nil || endpoint.Host == nil || *endpoint.Host == "" {
return nil, fmt.Errorf("endpoint host not available")
}
if endpoint == nil || endpoint.Port == nil || *endpoint.Port == 0 {
return nil, fmt.Errorf("endpoint port not available")
}
details := &ConnectionDetails{
Role: opts.Role,
Host: *endpoint.Host,
Port: *endpoint.Port,
Database: "tsdb", // Database is always "tsdb" for TimescaleDB/PostgreSQL services
IsPooler: isPooler,
}
if opts.WithPassword {
if opts.InitialPassword != "" {
details.Password = opts.InitialPassword
} else if password, err := GetPassword(service, opts.Role); err == nil {
details.Password = password
}
}
return details, nil
}
// String creates a PostgreSQL connection string from service details
func (d *ConnectionDetails) String() string {
if d.Password == "" {
// Build connection string without password (default behavior)
return fmt.Sprintf("postgresql://%s@%s:%d/%s?sslmode=require", d.Role, d.Host, d.Port, d.Database)
}
// Include password in connection string
return fmt.Sprintf("postgresql://%s:%s@%s:%d/%s?sslmode=require", d.Role, d.Password, d.Host, d.Port, d.Database)
}
// GetPassword fetches the password for the specified service from the
// configured password storage mechanism. It returns an error if it fails to
// find the password.
func GetPassword(service api.Service, role string) (string, error) {
storage := GetPasswordStorage()
password, err := storage.Get(service, role)
if err != nil {
// Provide specific error messages based on storage type
switch storage.(type) {
case *NoStorage:
return "", fmt.Errorf("password storage is disabled (--password-storage=none)")
case *KeyringStorage:
return "", fmt.Errorf("no password found in keyring for this service")
case *PgpassStorage:
return "", fmt.Errorf("no password found in ~/.pgpass for this service")
case *AutoFallbackStorage:
return "", fmt.Errorf("no password found for this service (checked keyring and ~/.pgpass)")
default:
return "", fmt.Errorf("failed to retrieve password: %w", err)
}
}
if password == "" {
return "", fmt.Errorf("no password available for service")
}
return password, nil
}