@@ -4,19 +4,26 @@ import (
44 "context"
55 "fmt"
66 "net"
7+ "net/url"
78 "os"
8- "os/exec "
9+ "strconv "
910 "time"
11+
12+ "github.com/flanksource/commons-db/db"
1013)
1114
15+ // ServiceManager starts the native services the e2e suite depends on.
16+ //
17+ // Only Postgres is started for real (via db.StartEmbedded). The other
18+ // services (Redis, OpenSearch, Loki, LocalStack) are not yet implemented;
19+ // the current e2e specs only assert on their connection-URL strings and on
20+ // locally-generated fixture data, so no live instance is required. Their
21+ // URL/port accessors remain so those specs keep compiling.
1222type ServiceManager struct {
13- postgresCmd * exec.Cmd
14- redisCmd * exec.Cmd
15- opensearchCmd * exec.Cmd
16- lokiCmd * exec.Cmd
17- localstackCmd * exec.Cmd
23+ postgresDSN string
24+ postgresPort int
25+ postgresStop func () error
1826
19- postgresPort int
2027 redisPort int
2128 opensearchPort int
2229 lokiPort int
@@ -27,7 +34,6 @@ type ServiceManager struct {
2734
2835func NewServiceManager () * ServiceManager {
2936 return & ServiceManager {
30- postgresPort : 5432 ,
3137 redisPort : 6379 ,
3238 opensearchPort : 9200 ,
3339 lokiPort : 3100 ,
@@ -42,51 +48,20 @@ func (sm *ServiceManager) StartAll(ctx context.Context) error {
4248 }
4349 sm .tmpDir = tmpDir
4450
45- // Start services in order
46- if err := sm .startPostgres (ctx ); err != nil {
51+ if err := sm .startPostgres (); err != nil {
4752 return fmt .Errorf ("failed to start postgres: %w" , err )
4853 }
4954
50- if err := sm .startRedis (ctx ); err != nil {
51- return fmt .Errorf ("failed to start redis: %w" , err )
52- }
53-
54- if err := sm .startOpenSearch (ctx ); err != nil {
55- return fmt .Errorf ("failed to start opensearch: %w" , err )
56- }
57-
58- if err := sm .startLoki (ctx ); err != nil {
59- return fmt .Errorf ("failed to start loki: %w" , err )
60- }
61-
62- if err := sm .startLocalStack (ctx ); err != nil {
63- return fmt .Errorf ("failed to start localstack: %w" , err )
64- }
65-
6655 return nil
6756}
6857
6958func (sm * ServiceManager ) StopAll (ctx context.Context ) error {
7059 var errs []error
7160
72- if sm .postgresCmd != nil && sm .postgresCmd .Process != nil {
73- _ = sm .postgresCmd .Process .Kill ()
74- }
75-
76- if sm .redisCmd != nil && sm .redisCmd .Process != nil {
77- _ = sm .redisCmd .Process .Kill ()
78- }
79-
80- if sm .opensearchCmd != nil && sm .opensearchCmd .Process != nil {
81- _ = sm .opensearchCmd .Process .Kill ()
82- }
83-
84- if sm .lokiCmd != nil && sm .lokiCmd .Process != nil {
85- _ = sm .lokiCmd .Process .Kill ()
86- }
87-
88- if sm .localstackCmd != nil && sm .localstackCmd .Process != nil {
89- _ = sm .localstackCmd .Process .Kill ()
61+ if sm .postgresStop != nil {
62+ if err := sm .postgresStop (); err != nil {
63+ errs = append (errs , fmt .Errorf ("stop postgres: %w" , err ))
64+ }
9065 }
9166
9267 if sm .tmpDir != "" {
@@ -102,77 +77,39 @@ func (sm *ServiceManager) StopAll(ctx context.Context) error {
10277 return nil
10378}
10479
80+ // AllHealthy reports whether every service StartAll actually started is
81+ // reachable. Only Postgres is started today, so only Postgres is checked.
10582func (sm * ServiceManager ) AllHealthy () bool {
106- return sm .isPostgresHealthy () && sm . isRedisHealthy () && sm . isOpenSearchHealthy () && sm . isLokiHealthy () && sm . isLocalStackHealthy ()
83+ return sm .isPostgresHealthy ()
10784}
10885
109- func (sm * ServiceManager ) startPostgres (ctx context.Context ) error {
110- if ! sm .isPortAvailable (sm .postgresPort ) {
111- return fmt .Errorf ("postgres port %d not available" , sm .postgresPort )
112- }
113-
114- // TODO: Implement postgres startup using embedded binaries from deps
115- // This is a placeholder that would use zonky postgres binaries
116- return nil
117- }
118-
119- func (sm * ServiceManager ) startRedis (ctx context.Context ) error {
120- if ! sm .isPortAvailable (sm .redisPort ) {
121- return fmt .Errorf ("redis port %d not available" , sm .redisPort )
122- }
123-
124- // TODO: Implement redis startup
125- return nil
126- }
127-
128- func (sm * ServiceManager ) startOpenSearch (ctx context.Context ) error {
129- if ! sm .isPortAvailable (sm .opensearchPort ) {
130- return fmt .Errorf ("opensearch port %d not available" , sm .opensearchPort )
131- }
132-
133- // TODO: Implement opensearch startup
134- return nil
135- }
136-
137- func (sm * ServiceManager ) startLoki (ctx context.Context ) error {
138- if ! sm .isPortAvailable (sm .lokiPort ) {
139- return fmt .Errorf ("loki port %d not available" , sm .lokiPort )
86+ func (sm * ServiceManager ) startPostgres () error {
87+ dsn , stop , err := db .StartEmbedded (db.EmbeddedConfig {
88+ DataDir : sm .tmpDir ,
89+ Database : "test" ,
90+ Port : 0 ,
91+ })
92+ if err != nil {
93+ return err
14094 }
14195
142- // TODO: Implement loki startup
143- return nil
144- }
145-
146- func (sm * ServiceManager ) startLocalStack (ctx context.Context ) error {
147- if ! sm .isPortAvailable (sm .localstackPort ) {
148- return fmt .Errorf ("localstack port %d not available" , sm .localstackPort )
96+ port , err := portFromDSN (dsn )
97+ if err != nil {
98+ _ = stop ()
99+ return err
149100 }
150101
151- // TODO: Implement localstack startup
102+ sm .postgresDSN = dsn
103+ sm .postgresPort = port
104+ sm .postgresStop = stop
152105 return nil
153106}
154107
155108func (sm * ServiceManager ) isPostgresHealthy () bool {
156- return sm .isPortHealthy (sm .postgresPort )
109+ return sm .postgresPort != 0 && isPortHealthy (sm .postgresPort )
157110}
158111
159- func (sm * ServiceManager ) isRedisHealthy () bool {
160- return sm .isPortHealthy (sm .redisPort )
161- }
162-
163- func (sm * ServiceManager ) isOpenSearchHealthy () bool {
164- return sm .isPortHealthy (sm .opensearchPort )
165- }
166-
167- func (sm * ServiceManager ) isLokiHealthy () bool {
168- return sm .isPortHealthy (sm .lokiPort )
169- }
170-
171- func (sm * ServiceManager ) isLocalStackHealthy () bool {
172- return sm .isPortHealthy (sm .localstackPort )
173- }
174-
175- func (sm * ServiceManager ) isPortHealthy (port int ) bool {
112+ func isPortHealthy (port int ) bool {
176113 conn , err := net .DialTimeout ("tcp" , fmt .Sprintf ("localhost:%d" , port ), 1 * time .Second )
177114 if err != nil {
178115 return false
@@ -181,17 +118,21 @@ func (sm *ServiceManager) isPortHealthy(port int) bool {
181118 return true
182119}
183120
184- func (sm * ServiceManager ) isPortAvailable (port int ) bool {
185- conn , err := net .Listen ("tcp" , fmt .Sprintf ("localhost:%d" , port ))
121+ // portFromDSN extracts the TCP port from a postgres:// DSN.
122+ func portFromDSN (dsn string ) (int , error ) {
123+ u , err := url .Parse (dsn )
186124 if err != nil {
187- return false
125+ return 0 , fmt . Errorf ( "parse dsn: %w" , err )
188126 }
189- defer conn .Close ()
190- return true
127+ p , err := strconv .Atoi (u .Port ())
128+ if err != nil {
129+ return 0 , fmt .Errorf ("dsn port %q: %w" , u .Port (), err )
130+ }
131+ return p , nil
191132}
192133
193134func (sm * ServiceManager ) PostgresURL () string {
194- return fmt . Sprintf ( "postgres://localhost:%d/test" , sm .postgresPort )
135+ return sm .postgresDSN
195136}
196137
197138func (sm * ServiceManager ) RedisURL () string {
0 commit comments