@@ -5,11 +5,16 @@ import (
55 "compress/gzip"
66 "encoding/json"
77 "fmt"
8+ "io"
89 "log"
910 "net/http"
1011 "strings"
1112 "time"
1213
14+ "github.com/TwiN/gatus/v4/client"
15+ "github.com/TwiN/gatus/v4/config"
16+ "github.com/TwiN/gatus/v4/config/remote"
17+ "github.com/TwiN/gatus/v4/core"
1318 "github.com/TwiN/gatus/v4/storage/store"
1419 "github.com/TwiN/gatus/v4/storage/store/common"
1520 "github.com/TwiN/gatus/v4/storage/store/common/paging"
@@ -28,48 +33,89 @@ var (
2833// EndpointStatuses handles requests to retrieve all EndpointStatus
2934// Due to the size of the response, this function leverages a cache.
3035// Must not be wrapped by GzipHandler
31- func EndpointStatuses (writer http.ResponseWriter , r * http.Request ) {
32- page , pageSize := extractPageAndPageSizeFromRequest (r )
33- gzipped := strings .Contains (r .Header .Get ("Accept-Encoding" ), "gzip" )
34- var exists bool
35- var value interface {}
36- if gzipped {
37- writer .Header ().Set ("Content-Encoding" , "gzip" )
38- value , exists = cache .Get (fmt .Sprintf ("endpoint-status-%d-%d-gzipped" , page , pageSize ))
39- } else {
40- value , exists = cache .Get (fmt .Sprintf ("endpoint-status-%d-%d" , page , pageSize ))
36+ func EndpointStatuses (cfg * config.Config ) http.HandlerFunc {
37+ return func (writer http.ResponseWriter , r * http.Request ) {
38+ page , pageSize := extractPageAndPageSizeFromRequest (r )
39+ gzipped := strings .Contains (r .Header .Get ("Accept-Encoding" ), "gzip" )
40+ var exists bool
41+ var value interface {}
42+ if gzipped {
43+ writer .Header ().Set ("Content-Encoding" , "gzip" )
44+ value , exists = cache .Get (fmt .Sprintf ("endpoint-status-%d-%d-gzipped" , page , pageSize ))
45+ } else {
46+ value , exists = cache .Get (fmt .Sprintf ("endpoint-status-%d-%d" , page , pageSize ))
47+ }
48+ var data []byte
49+ if ! exists {
50+ var err error
51+ buffer := & bytes.Buffer {}
52+ gzipWriter := gzip .NewWriter (buffer )
53+ endpointStatuses , err := store .Get ().GetAllEndpointStatuses (paging .NewEndpointStatusParams ().WithResults (page , pageSize ))
54+ if err != nil {
55+ log .Printf ("[handler][EndpointStatuses] Failed to retrieve endpoint statuses: %s" , err .Error ())
56+ http .Error (writer , err .Error (), http .StatusInternalServerError )
57+ return
58+ }
59+ // ALPHA: Retrieve endpoint statuses from remote instances
60+ if endpointStatusesFromRemote , err := getEndpointStatusesFromRemoteInstances (cfg .Remote ); err != nil {
61+ log .Printf ("[handler][EndpointStatuses] Silently failed to retrieve endpoint statuses from remote: %s" , err .Error ())
62+ } else if endpointStatusesFromRemote != nil {
63+ endpointStatuses = append (endpointStatuses , endpointStatusesFromRemote ... )
64+ }
65+ // Marshal endpoint statuses to JSON
66+ data , err = json .Marshal (endpointStatuses )
67+ if err != nil {
68+ log .Printf ("[handler][EndpointStatuses] Unable to marshal object to JSON: %s" , err .Error ())
69+ http .Error (writer , "unable to marshal object to JSON" , http .StatusInternalServerError )
70+ return
71+ }
72+ _ , _ = gzipWriter .Write (data )
73+ _ = gzipWriter .Close ()
74+ gzippedData := buffer .Bytes ()
75+ cache .SetWithTTL (fmt .Sprintf ("endpoint-status-%d-%d" , page , pageSize ), data , cacheTTL )
76+ cache .SetWithTTL (fmt .Sprintf ("endpoint-status-%d-%d-gzipped" , page , pageSize ), gzippedData , cacheTTL )
77+ if gzipped {
78+ data = gzippedData
79+ }
80+ } else {
81+ data = value .([]byte )
82+ }
83+ writer .Header ().Add ("Content-Type" , "application/json" )
84+ writer .WriteHeader (http .StatusOK )
85+ _ , _ = writer .Write (data )
86+ }
87+ }
88+
89+ func getEndpointStatusesFromRemoteInstances (remoteConfig * remote.Config ) ([]* core.EndpointStatus , error ) {
90+ if remoteConfig == nil || len (remoteConfig .Instances ) == 0 {
91+ return nil , nil
4192 }
42- var data []byte
43- if ! exists {
44- var err error
45- buffer := & bytes.Buffer {}
46- gzipWriter := gzip .NewWriter (buffer )
47- endpointStatuses , err := store .Get ().GetAllEndpointStatuses (paging .NewEndpointStatusParams ().WithResults (page , pageSize ))
93+ var endpointStatusesFromAllRemotes []* core.EndpointStatus
94+ httpClient := client .GetHTTPClient (remoteConfig .ClientConfig )
95+ for _ , instance := range remoteConfig .Instances {
96+ response , err := httpClient .Get (instance .URL )
4897 if err != nil {
49- log .Printf ("[handler][EndpointStatuses] Failed to retrieve endpoint statuses: %s" , err .Error ())
50- http .Error (writer , err .Error (), http .StatusInternalServerError )
51- return
98+ return nil , err
5299 }
53- data , err = json . Marshal ( endpointStatuses )
100+ body , err := io . ReadAll ( response . Body )
54101 if err != nil {
55- log . Printf ( "[handler][EndpointStatuses] Unable to marshal object to JSON: %s" , err . Error () )
56- http . Error ( writer , "unable to marshal object to JSON " , http . StatusInternalServerError )
57- return
102+ _ = response . Body . Close ( )
103+ log . Printf ( "[handler][getEndpointStatusesFromRemoteInstances] Silently failed to retrieve endpoint statuses from %s: %s " , instance . URL , err . Error () )
104+ continue
58105 }
59- _ , _ = gzipWriter .Write (data )
60- _ = gzipWriter .Close ()
61- gzippedData := buffer .Bytes ()
62- cache .SetWithTTL (fmt .Sprintf ("endpoint-status-%d-%d" , page , pageSize ), data , cacheTTL )
63- cache .SetWithTTL (fmt .Sprintf ("endpoint-status-%d-%d-gzipped" , page , pageSize ), gzippedData , cacheTTL )
64- if gzipped {
65- data = gzippedData
106+ var endpointStatuses []* core.EndpointStatus
107+ if err = json .Unmarshal (body , & endpointStatuses ); err != nil {
108+ _ = response .Body .Close ()
109+ log .Printf ("[handler][getEndpointStatusesFromRemoteInstances] Silently failed to retrieve endpoint statuses from %s: %s" , instance .URL , err .Error ())
110+ continue
66111 }
67- } else {
68- data = value .([]byte )
112+ _ = response .Body .Close ()
113+ for _ , endpointStatus := range endpointStatuses {
114+ endpointStatus .Name = instance .EndpointPrefix + endpointStatus .Name
115+ }
116+ endpointStatusesFromAllRemotes = append (endpointStatusesFromAllRemotes , endpointStatuses ... )
69117 }
70- writer .Header ().Add ("Content-Type" , "application/json" )
71- writer .WriteHeader (http .StatusOK )
72- _ , _ = writer .Write (data )
118+ return endpointStatusesFromAllRemotes , nil
73119}
74120
75121// EndpointStatus retrieves a single core.EndpointStatus by group and endpoint name
0 commit comments