@@ -5,11 +5,16 @@ import (
5
5
"compress/gzip"
6
6
"encoding/json"
7
7
"fmt"
8
+ "io"
8
9
"log"
9
10
"net/http"
10
11
"strings"
11
12
"time"
12
13
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"
13
18
"github.com/TwiN/gatus/v4/storage/store"
14
19
"github.com/TwiN/gatus/v4/storage/store/common"
15
20
"github.com/TwiN/gatus/v4/storage/store/common/paging"
@@ -28,48 +33,89 @@ var (
28
33
// EndpointStatuses handles requests to retrieve all EndpointStatus
29
34
// Due to the size of the response, this function leverages a cache.
30
35
// 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
41
92
}
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 )
48
97
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
52
99
}
53
- data , err = json . Marshal ( endpointStatuses )
100
+ body , err := io . ReadAll ( response . Body )
54
101
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
58
105
}
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
66
111
}
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 ... )
69
117
}
70
- writer .Header ().Add ("Content-Type" , "application/json" )
71
- writer .WriteHeader (http .StatusOK )
72
- _ , _ = writer .Write (data )
118
+ return endpointStatusesFromAllRemotes , nil
73
119
}
74
120
75
121
// EndpointStatus retrieves a single core.EndpointStatus by group and endpoint name
0 commit comments