1+ // Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+ // or more contributor license agreements. Licensed under the Elastic License;
3+ // you may not use this file except in compliance with the Elastic License.
4+
15package serverless
26
37import (
4- "bytes"
58 "context"
69 "encoding/json"
710 "fmt"
8- "io "
11+ "log "
912 "net/http"
13+ "time"
14+
15+ "github.com/elastic/elastic-package/internal/logger"
1016)
1117
1218// Project represents a serverless project
@@ -16,6 +22,7 @@ type Project struct {
1622
1723 Name string `json:"name"`
1824 ID string `json:"id"`
25+ Alias string `json:"alias"`
1926 Type string `json:"type"`
2027 Region string `json:"region_id"`
2128
@@ -32,44 +39,126 @@ type Project struct {
3239 } `json:"endpoints"`
3340}
3441
35- // NewObservabilityProject creates a new observability type project
36- func NewObservabilityProject (ctx context.Context , url , name , apiKey , region string ) (* Project , error ) {
37- return newProject (ctx , url , name , apiKey , region , "observability" )
42+ type serviceHealthy func (context.Context , * Project ) error
43+
44+ func (p * Project ) EnsureHealthy (ctx context.Context ) error {
45+ if err := p .ensureServiceHealthy (ctx , getESHealthy ); err != nil {
46+ return fmt .Errorf ("elasticsearch not healthy: %w" , err )
47+ }
48+ if err := p .ensureServiceHealthy (ctx , getKibanaHealthy ); err != nil {
49+ return fmt .Errorf ("kibana not healthy: %w" , err )
50+ }
51+ if err := p .ensureServiceHealthy (ctx , getFleetHealthy ); err != nil {
52+ return fmt .Errorf ("fleet not healthy: %w" , err )
53+ }
54+ return nil
3855}
3956
40- // newProject creates a new serverless project
41- // Note that the Project.Endpoints may not be populated and another call may be required.
42- func newProject (ctx context.Context , url , name , apiKey , region , projectType string ) (* Project , error ) {
43- ReqBody := struct {
44- Name string `json:"name"`
45- RegionID string `json:"region_id"`
46- }{
47- Name : name ,
48- RegionID : region ,
49- }
50- p , err := json .Marshal (ReqBody )
57+ func (p * Project ) ensureServiceHealthy (ctx context.Context , serviceFunc serviceHealthy ) error {
58+ timer := time .NewTimer (time .Millisecond )
59+ for {
60+ select {
61+ case <- ctx .Done ():
62+ return ctx .Err ()
63+ case <- timer .C :
64+ }
65+
66+ err := serviceFunc (ctx , p )
67+ if err != nil {
68+ logger .Debugf ("service not ready: %s" , err .Error ())
69+ timer .Reset (time .Second * 5 )
70+ continue
71+ }
72+
73+ return nil
74+ }
75+ return nil
76+ }
77+
78+ func getESHealthy (ctx context.Context , project * Project ) error {
79+ client , err := NewClient (
80+ WithAddress (project .Endpoints .Elasticsearch ),
81+ WithUsername (project .Credentials .Username ),
82+ WithPassword (project .Credentials .Password ),
83+ )
5184 if err != nil {
52- return nil , err
85+ return err
5386 }
54- req , err := http .NewRequestWithContext (ctx , "POST" , url + "/api/v1/serverless/projects/" + projectType , bytes .NewReader (p ))
87+
88+ statusCode , respBody , err := client .get (ctx , "/_cluster/health" )
5589 if err != nil {
56- return nil , err
90+ return fmt .Errorf ("failed to query elasticsearch health: %w" , err )
91+ }
92+
93+ if statusCode != http .StatusOK {
94+ return fmt .Errorf ("unexpected status code %d, body: %s" , statusCode , string (respBody ))
5795 }
58- req .Header .Set ("Content-Type" , "application/json" )
59- req .Header .Set ("Authorization" , "ApiKey " + apiKey )
6096
61- resp , err := http .DefaultClient .Do (req )
97+ var health struct {
98+ Status string `json:"status"`
99+ }
100+ if err := json .Unmarshal (respBody , & health ); err != nil {
101+ log .Printf ("Unable to decode response: %v body: %s" , err , string (respBody ))
102+ return err
103+ }
104+ if health .Status == "green" {
105+ return nil
106+ }
107+ return fmt .Errorf ("elasticsearch unhealthy: %s" , health .Status )
108+ }
109+
110+ func getKibanaHealthy (ctx context.Context , project * Project ) error {
111+ client , err := NewClient (
112+ WithAddress (project .Endpoints .Kibana ),
113+ WithUsername (project .Credentials .Username ),
114+ WithPassword (project .Credentials .Password ),
115+ )
116+ if err != nil {
117+ return err
118+ }
119+
120+ statusCode , respBody , err := client .get (ctx , "/api/status" )
121+ if err != nil {
122+ return fmt .Errorf ("failed to query kibana status: %w" , err )
123+ }
124+ if statusCode != http .StatusOK {
125+ return fmt .Errorf ("unexpected status code %d, body: %s" , statusCode , string (respBody ))
126+ }
127+
128+ var status struct {
129+ Status struct {
130+ Overall struct {
131+ Level string `json:"level"`
132+ } `json:"overall"`
133+ } `json:"status"`
134+ }
135+ if err := json .Unmarshal (respBody , & status ); err != nil {
136+ log .Printf ("Unable to decode response: %v body: %s" , err , string (respBody ))
137+ return err
138+ }
139+ if status .Status .Overall .Level == "available" {
140+ return nil
141+ }
142+ return fmt .Errorf ("kibana unhealthy: %s" , status .Status .Overall .Level )
143+ }
144+
145+ func getFleetHealthy (ctx context.Context , project * Project ) error {
146+ client , err := NewClient (
147+ WithAddress (project .Endpoints .Fleet ),
148+ WithUsername (project .Credentials .Username ),
149+ WithPassword (project .Credentials .Password ),
150+ )
62151 if err != nil {
63- return nil , err
152+ return err
64153 }
65- defer resp .Body .Close ()
66154
67- if resp .StatusCode != http .StatusCreated {
68- p , _ := io .ReadAll (resp .Body )
69- return nil , fmt .Errorf ("unexpected status code %d, body: %s" , resp .StatusCode , string (p ))
155+ statusCode , respBody , err := client .get (ctx , "/api/status" )
156+ if err != nil {
157+ return fmt .Errorf ("failed to query fleet status: %w" , err )
158+ }
159+ if statusCode != http .StatusOK {
160+ return fmt .Errorf ("fleet unhealthy: status code %d, body: %s" , statusCode , string (respBody ))
70161 }
71- project := & Project {url : url , apiKey : apiKey }
72162
73- err = json .NewDecoder (resp .Body ).Decode (project )
74- return project , err
163+ return nil
75164}
0 commit comments