forked from kuskoman/logstash-exporter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
162 additions
and
220 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,57 @@ | ||
package logstashclient | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
|
||
"github.com/kuskoman/logstash-exporter/fetcher/responses" | ||
"github.com/kuskoman/logstash-exporter/httphandler" | ||
) | ||
|
||
type DefaultClient struct { | ||
httpClient httphandler.HTTPHandler | ||
} | ||
|
||
type Client interface { | ||
GetNodeInfo() (*responses.NodeInfoResponse, error) | ||
GetNodeStats() (*responses.NodestatsResponse, error) | ||
} | ||
|
||
type httpClient interface { | ||
Get(url string) (*http.Response, error) | ||
} | ||
|
||
type DefaultClient struct { | ||
httpClient httpClient | ||
endpoint string | ||
} | ||
|
||
const defaultLogstashEndpoint = "http://localhost:9600" | ||
|
||
func NewClient(httpClient httphandler.HTTPHandler) *DefaultClient { | ||
var clientHandler httphandler.HTTPHandler | ||
func NewClient(endpoint string) Client { | ||
client := &DefaultClient{endpoint: endpoint} | ||
client.httpClient = &http.Client{} | ||
|
||
if endpoint == "" { | ||
client.endpoint = defaultLogstashEndpoint | ||
} | ||
|
||
return client | ||
} | ||
|
||
func getMetrics[T any](client httpClient, endpoint string) (*T, error) { | ||
resp, err := client.Get(endpoint) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
defer resp.Body.Close() | ||
|
||
return deserializeHttpResponse[T](resp) | ||
} | ||
|
||
func deserializeHttpResponse[T any](response *http.Response) (*T, error) { | ||
var result T | ||
|
||
if httpClient == nil { | ||
defaultHandler := httphandler.GetDefaultHTTPHandler(defaultLogstashEndpoint) | ||
clientHandler = defaultHandler | ||
} else { | ||
clientHandler = httpClient | ||
err := json.NewDecoder(response.Body).Decode(&result) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &DefaultClient{httpClient: clientHandler} | ||
return &result, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,24 @@ | ||
package logstashclient | ||
|
||
import ( | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/kuskoman/logstash-exporter/httphandler" | ||
) | ||
|
||
func TestNewClient(t *testing.T) { | ||
t.Run("with DefaultHTTPHandler", func(t *testing.T) { | ||
client := NewClient(nil) | ||
httpClient := client.httpClient | ||
if httpClient == nil { | ||
t.Error("Expected httpClient to be set") | ||
} | ||
t.Run("should return a new client for the default endpoint", func(t *testing.T) { | ||
client := NewClient("") | ||
|
||
_, isDefaultHandler := httpClient.(*httphandler.DefaultHTTPHandler) | ||
if !isDefaultHandler { | ||
t.Error("Expected httpClient to be of type HTTPHandler") | ||
if client.(*DefaultClient).endpoint != defaultLogstashEndpoint { | ||
t.Errorf("expected endpoint to be %s, got %s", defaultLogstashEndpoint, client.(*DefaultClient).endpoint) | ||
} | ||
}) | ||
|
||
t.Run("with custom HTTPHandler", func(t *testing.T) { | ||
handler := &mockHTTPHandler{} | ||
client := NewClient(handler) | ||
httpClient := client.httpClient | ||
if httpClient == nil { | ||
t.Error("Expected httpClient to be set") | ||
} | ||
|
||
_, isCustomHandler := httpClient.(*mockHTTPHandler) | ||
if !isCustomHandler { | ||
t.Error("Expected httpClient to be of type *mockHTTPHandler") | ||
} | ||
t.Run("should return a new client for the given endpoint", func(t *testing.T) { | ||
endpoint := "http://localhost:9601" | ||
client := NewClient(endpoint) | ||
|
||
_, isDefaultHandler := httpClient.(*httphandler.DefaultHTTPHandler) | ||
if isDefaultHandler { | ||
t.Error("Expected httpClient to not be of type *httpclient.DefaultHTTPHandler") | ||
if client.(*DefaultClient).endpoint != endpoint { | ||
t.Errorf("expected endpoint to be %s, got %s", endpoint, client.(*DefaultClient).endpoint) | ||
} | ||
}) | ||
} | ||
|
||
type mockHTTPHandler struct{} | ||
|
||
func (m *mockHTTPHandler) Get(path string) (*http.Response, error) { | ||
return nil, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,17 @@ | ||
package logstashclient | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/kuskoman/logstash-exporter/fetcher/responses" | ||
"github.com/kuskoman/logstash-exporter/httphandler" | ||
) | ||
|
||
func (c *DefaultClient) GetNodeInfo() (*responses.NodeInfoResponse, error) { | ||
var nodeInfoResponse responses.NodeInfoResponse | ||
err := httphandler.GetMetrics(c.httpClient, "", &nodeInfoResponse) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &nodeInfoResponse, nil | ||
func (client *DefaultClient) GetNodeInfo() (*responses.NodeInfoResponse, error) { | ||
fullPath := client.endpoint | ||
return getMetrics[responses.NodeInfoResponse](client.httpClient, fullPath) | ||
} | ||
|
||
func (c *DefaultClient) GetNodeStats() (*responses.NodestatsResponse, error) { | ||
var nodeStatsResponse responses.NodestatsResponse | ||
err := httphandler.GetMetrics(c.httpClient, "/_node/stats", &nodeStatsResponse) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &nodeStatsResponse, nil | ||
func (client *DefaultClient) GetNodeStats() (*responses.NodestatsResponse, error) { | ||
fullPath := fmt.Sprintf("%s/_node/stats", client.endpoint) | ||
return getMetrics[responses.NodestatsResponse](client.httpClient, fullPath) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,130 @@ | ||
package logstashclient | ||
|
||
import ( | ||
"errors" | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
type MockHTTPClient struct { | ||
Response *http.Response | ||
Err error | ||
} | ||
|
||
func (m *MockHTTPClient) Get(url string) (*http.Response, error) { | ||
return m.Response, m.Err | ||
} | ||
|
||
func TestGetNodeInfo(t *testing.T) { | ||
t.Run("with valid response", func(t *testing.T) { | ||
fixtureContent, err := os.ReadFile("../../fixtures/node_stats.json") | ||
t.Run("should return a valid NodeInfoResponse when the request is successful", func(t *testing.T) { | ||
mockClient, err := setupSuccessfulHttpMock("node_info.json") | ||
if err != nil { | ||
t.Fatalf("Error reading fixture file: %v", err) | ||
t.Fatalf("error setting up mock http client: %s", err) | ||
} | ||
handlerMock := &workingHandlerMock{fixture: string(fixtureContent)} | ||
client := NewClient(handlerMock) | ||
|
||
client := &DefaultClient{ | ||
httpClient: mockClient, | ||
endpoint: "http://localhost:9600", | ||
} | ||
|
||
response, err := client.GetNodeInfo() | ||
if err != nil { | ||
t.Fatalf("Error getting node info: %v", err) | ||
t.Fatalf("error getting node info: %s", err) | ||
} | ||
|
||
// we don't check every property, unmarschalling is tested in another test | ||
if (response.ID == "") || (response.Name == "") { | ||
t.Error("Expected response to be populated") | ||
if response.Status != "green" { | ||
t.Fatalf("expected status to be properly read as green, got %s", response.Status) | ||
} | ||
// detailed checks are done in the responses package | ||
}) | ||
|
||
t.Run("with invalid response", func(t *testing.T) { | ||
handlerMock := &failingHandlerMock{} | ||
client := NewClient(handlerMock) | ||
t.Run("should return an error when the request fails", func(t *testing.T) { | ||
mockClient := &MockHTTPClient{ | ||
Response: nil, | ||
Err: fmt.Errorf("error"), | ||
} | ||
|
||
client := &DefaultClient{ | ||
httpClient: mockClient, | ||
endpoint: "http://localhost:9600", | ||
} | ||
|
||
_, err := client.GetNodeInfo() | ||
if err == nil { | ||
t.Error("Expected error") | ||
t.Fatalf("expected error, got nil") | ||
} | ||
}) | ||
} | ||
|
||
type workingHandlerMock struct { | ||
fixture string | ||
func TestGetNodeStats(t *testing.T) { | ||
t.Run("should return a valid NodestatsResponse when the request is successful", func(t *testing.T) { | ||
mockClient, err := setupSuccessfulHttpMock("node_stats.json") | ||
if err != nil { | ||
t.Fatalf("error setting up mock http client: %s", err) | ||
} | ||
|
||
client := &DefaultClient{ | ||
httpClient: mockClient, | ||
endpoint: "http://localhost:9600", | ||
} | ||
|
||
response, err := client.GetNodeStats() | ||
if err != nil { | ||
t.Fatalf("error getting node stats: %s", err) | ||
} | ||
|
||
if response.Status != "green" { | ||
t.Fatalf("expected status to be properly read as green, got %s", response.Status) | ||
} | ||
// detailed checks are done in the responses package | ||
}) | ||
|
||
t.Run("should return an error when the request fails", func(t *testing.T) { | ||
mockClient := &MockHTTPClient{ | ||
Response: nil, | ||
Err: fmt.Errorf("error"), | ||
} | ||
|
||
client := &DefaultClient{ | ||
httpClient: mockClient, | ||
endpoint: "http://localhost:9600", | ||
} | ||
|
||
_, err := client.GetNodeStats() | ||
if err == nil { | ||
t.Fatalf("expected error, got nil") | ||
} | ||
}) | ||
} | ||
|
||
func (h *workingHandlerMock) Get(path string) (*http.Response, error) { | ||
reader := strings.NewReader(h.fixture) | ||
closer := io.NopCloser(reader) | ||
response := &http.Response{Body: closer} | ||
return response, nil | ||
func loadFixture(filename string) ([]byte, error) { | ||
fullPath := fmt.Sprintf("../../fixtures/%s", filename) | ||
fixtureBytes, err := os.ReadFile(fullPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return fixtureBytes, nil | ||
} | ||
|
||
type failingHandlerMock struct{} | ||
func setupSuccessfulHttpMock(filename string) (*MockHTTPClient, error) { | ||
fixtureBytes, err := loadFixture(filename) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
fixtureReader := bytes.NewReader(fixtureBytes) | ||
|
||
mockResponse := &http.Response{ | ||
Body: io.NopCloser(fixtureReader), | ||
StatusCode: 200, | ||
} | ||
|
||
func (h *failingHandlerMock) Get(path string) (*http.Response, error) { | ||
return nil, errors.New("failed to get response") | ||
return &MockHTTPClient{ | ||
Response: mockResponse, | ||
Err: nil, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.