Skip to content

Commit

Permalink
Remove unnecessary fetcher package
Browse files Browse the repository at this point in the history
  • Loading branch information
kuskoman committed Feb 17, 2023
1 parent 2ae932e commit 3b7684c
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 220 deletions.
4 changes: 1 addition & 3 deletions collectors/collector_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/kuskoman/logstash-exporter/collectors/nodestats"
"github.com/kuskoman/logstash-exporter/config"
logstashclient "github.com/kuskoman/logstash-exporter/fetcher/logstash_client"
"github.com/kuskoman/logstash-exporter/httphandler"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/version"
)
Expand All @@ -24,8 +23,7 @@ type CollectorManager struct {
}

func NewCollectorManager(endpoint string) *CollectorManager {
httpHandler := httphandler.GetDefaultHTTPHandler(endpoint)
client := logstashclient.NewClient(httpHandler)
client := logstashclient.NewClient(endpoint)

collectors := getCollectors(client)

Expand Down
53 changes: 40 additions & 13 deletions fetcher/logstash_client/client.go
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
}
42 changes: 9 additions & 33 deletions fetcher/logstash_client/client_test.go
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
}
25 changes: 8 additions & 17 deletions fetcher/logstash_client/queries.go
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)
}
124 changes: 99 additions & 25 deletions fetcher/logstash_client/queries_test.go
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
}
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ module github.com/kuskoman/logstash-exporter

go 1.20

require github.com/prometheus/client_golang v1.14.0
require (
github.com/joho/godotenv v1.5.1
github.com/prometheus/client_golang v1.14.0
)

require (
github.com/gkampitakis/ciinfo v0.1.1 // indirect
github.com/gkampitakis/go-diff v1.3.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
Expand Down
Loading

0 comments on commit 3b7684c

Please sign in to comment.