TP-47112 | Adding client for monitoring service (#300)

* TP-47112 | Adding client for monitoring service

* TP-47112 | Adding unit tests for file utils

---------

Co-authored-by: Md Anees <md.anees@navi.com>
This commit is contained in:
Sriram Bhargav
2023-11-30 14:59:33 +05:30
committed by GitHub
parent 527ba2c04f
commit bf5e72cb05
9 changed files with 549 additions and 0 deletions

View File

@@ -37,6 +37,8 @@ generatemocks:
cd $(CURDIR)/service/slack && minimock -i ISlackService -s _mock.go -o $(CURDIR)/mocks
cd $(CURDIR)/service/incident && minimock -i IIncidentService -s _mock.go -o $(CURDIR)/mocks
cd $(CURDIR)/repository/rca && minimock -i IRcaRepository -s _mock.go -o $(CURDIR)/mocks
cd $(CURDIR)/model/incident && minimock -i IIncidentRepository -s _mock.go -o $(CURDIR)/mocks
cd $(CURDIR)/pkg/monitoringService && minimock -i MonitoringServiceActions -s _mock.go -o $(CURDIR)/mocks
cd $(CURDIR)/service/incident_channel && minimock -i IIncidentChannelService -s _mock.go -o $(CURDIR)/mocks
cd $(CURDIR)/model/team && minimock -i ITeamRepository -s _mock.go -o $(CURDIR)/mocks
cd $(CURDIR)/model/severity && minimock -i ISeverityRepository -s _mock.go -o $(CURDIR)/mocks

View File

@@ -0,0 +1,31 @@
package file
import (
"fmt"
"go.uber.org/zap"
"houston/logger"
"os"
)
func FetchFilesInDirectory(directory string) ([]os.DirEntry, error) {
files, err := os.ReadDir(directory)
if err != nil {
logger.Error(
fmt.Sprintf("Failed to fetch files in directory: %s", directory),
zap.Error(err),
)
return nil, err
}
return files, nil
}
func RemoveDirectory(directory string) error {
err := os.RemoveAll(directory)
if err != nil {
logger.Error(
fmt.Sprintf("Failed to remove directory: %s", directory),
zap.Error(err),
)
}
return err
}

View File

@@ -0,0 +1,79 @@
package file
import (
"houston/logger"
"os"
"testing"
"github.com/stretchr/testify/suite"
)
type FileUtilSuite struct {
suite.Suite
}
func (suite *FileUtilSuite) SetupSuite() {
logger.InitLogger()
}
func (suite *FileUtilSuite) TestFetchFilesInDirectory_Success() {
// Create a temporary directory for testing
testDir := "test_fetch_files"
err := os.Mkdir(testDir, os.ModePerm)
suite.NoError(err, "Error creating test directory")
// Create some test files in the temporary directory
_, err = os.Create(testDir + "/file1.txt")
suite.NoError(err, "Error creating test file 1")
_, err = os.Create(testDir + "/file2.txt")
suite.NoError(err, "Error creating test file 2")
// Call the function being tested
files, err := FetchFilesInDirectory(testDir)
// Assertions
suite.NoError(err, "Unexpected error fetching files")
suite.NotNil(files, "Files should not be nil")
suite.Equal(2, len(files), "Unexpected number of files")
// Cleanup: remove the temporary directory
err = os.RemoveAll(testDir)
suite.NoError(err, "Error removing test directory")
}
func (suite *FileUtilSuite) TestFetchFilesInDirectory_Failure() {
// Call the function being tested with a non-existent directory
files, err := FetchFilesInDirectory("nonexistent_directory")
// Assertions
suite.Error(err, "Expected an error fetching files")
suite.Nil(files, "Files should be nil")
}
func (suite *FileUtilSuite) TestRemoveDirectory_Success() {
// Create a temporary directory for testing
testDir := "test_remove_directory"
err := os.Mkdir(testDir, os.ModePerm)
suite.NoError(err, "Error creating test directory")
// Call the function being tested
err = RemoveDirectory(testDir)
// Assertions
suite.NoError(err, "Unexpected error removing directory")
// Verify that the directory has been removed
_, err = os.Stat(testDir)
suite.True(os.IsNotExist(err), "Directory should not exist after removal")
}
func (suite *FileUtilSuite) TestRemoveDirectory_Failure() {
// Call the function being tested with a non-existent directory
err := RemoveDirectory("nonexistent_directory/dsa")
// Assertions
suite.NoError(err, "Expected an error removing directory")
}
func TestFileUtilSuite(t *testing.T) {
suite.Run(t, new(FileUtilSuite))
}

119
common/util/file_util.go Normal file
View File

@@ -0,0 +1,119 @@
package util
import (
"archive/zip"
"errors"
"fmt"
"github.com/spf13/viper"
"houston/logger"
"io"
"os"
"path/filepath"
)
func UnzipFile(zipFilePath string, destinationDirectoryPath string) error {
maxFileSize := viper.GetUint64("MAX_FILE_SIZE") * 1024 * 1024
zipFile, err := zip.OpenReader(zipFilePath)
if err != nil {
return err
}
defer func(zipFile *zip.ReadCloser) {
err := zipFile.Close()
if err != nil {
logger.Error(fmt.Sprintf("Error while closing zip file: %v", err))
}
}(zipFile)
if _, err := os.Stat(destinationDirectoryPath); os.IsNotExist(err) {
return err
}
for _, file := range zipFile.File {
err := CheckFileSizeAndUncompress(file, destinationDirectoryPath, maxFileSize)
if err != nil {
logger.Error(fmt.Sprintf("Error while unzipping file: %v", err))
}
}
logger.Info("Unzip completed successfully.")
return nil
}
func CheckFileSizeAndUncompress(file *zip.File, destinationDirectoryPath string, maxFileSize uint64) error {
if file.UncompressedSize64 > maxFileSize {
return errors.New(fmt.Sprintf("File with name %v is too big", file.Name))
}
zipFile, err := file.Open()
if err != nil {
return err
}
defer func(zipFile io.ReadCloser) {
err := zipFile.Close()
if err != nil {
logger.Error(fmt.Sprintf("Error while closing zip file: %v", err))
}
}(zipFile)
destFilePath := filepath.Join(destinationDirectoryPath, file.Name)
destFile, err := os.Create(destFilePath)
if err != nil {
logger.Error(fmt.Sprintf("Error while creating destination file: %v", err))
}
defer func(destFile *os.File) {
err := destFile.Close()
if err != nil {
logger.Error(fmt.Sprintf("Error while closing destination file: %v", err))
}
}(destFile)
// Check file size
if file.UncompressedSize64 > maxFileSize {
logger.Error(fmt.Sprintf("File with name %v is too big", file.Name))
}
_, err = io.Copy(destFile, io.Reader(zipFile))
if err != nil {
logger.Error(fmt.Sprintf("Error while copying file: %v", err))
return err
}
return nil
}
func CheckAndCreateDirectory(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
logger.Error(fmt.Sprintf("Error while creating directory :%v", err))
return err
}
}
return nil
}
func CreateFileWithName(path string, fileName string) (*os.File, error) {
err := CheckAndCreateDirectory(path)
if err != nil {
return nil, err
}
file, err := os.Create(filepath.Join(path, fileName))
if err != nil {
return nil, err
}
return file, nil
}
func DeleteFile(path string) error {
err := os.Remove(path)
if err != nil {
logger.Error(fmt.Sprintf("Error while deleting file :%v", err))
return err
}
return nil
}
func CloseFile(file *os.File) {
err := file.Close()
if err != nil {
logger.Error(fmt.Sprintf("Error while closing file: %v", err))
}
}

View File

@@ -0,0 +1,5 @@
package model
type GetGrafanaImagesRequest struct {
Incidents []string `json:"houstonIncidents"`
}

View File

@@ -0,0 +1,5 @@
package monitoringService
type ServiceActions interface {
GetGrafanaImages(incidents string) (string, error)
}

View File

@@ -0,0 +1,132 @@
package monitoringService
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/spf13/viper"
"go.uber.org/zap"
"houston/common/util"
"houston/logger"
"houston/model"
"houston/pkg/rest"
service "houston/service/response"
"io"
"net/http"
"os"
"time"
)
type ActionsImpl struct {
BaseURL string
Client rest.ClientActions
DefaultTimeout time.Duration
GetGrafanaImagesPath string
}
func NewMonitoringServiceActionsImpl(client rest.ClientActions) *ActionsImpl {
return &ActionsImpl{
Client: client,
BaseURL: viper.GetString("MONITORING_SERVICE_BASEURL"),
DefaultTimeout: viper.GetDuration("DEFAULT_MONITORING_SERVICE_TIMEOUT"),
GetGrafanaImagesPath: viper.GetString("MONITORING_SERVICE_GET_GRAFANA_IMAGES_PATH"),
}
}
func (monitoringService *ActionsImpl) GetGrafanaImages(incidents []string, requestId string,
responseChannel chan service.GrafanaImageFetchResponse) {
fullURL := monitoringService.BaseURL + monitoringService.GetGrafanaImagesPath
requestHeaders := map[string]string{
"Content-Type": "application/json",
"CORRELATION_ID": requestId,
}
getGrafanaImagesRequestBody := model.GetGrafanaImagesRequest{
Incidents: incidents,
}
payload, err := json.Marshal(getGrafanaImagesRequestBody)
if err != nil {
logger.Error("Error while marshalling request body", zap.Error(err))
responseChannel <- service.GrafanaImageFetchResponse{Error: err}
return
}
response, err := monitoringService.Client.PostWithTimeout(fullURL, *bytes.NewBuffer(payload), requestHeaders,
monitoringService.DefaultTimeout, nil)
if err != nil {
logger.Error("Error while getting grafana images", zap.Error(err))
responseChannel <- service.GrafanaImageFetchResponse{Error: err}
return
}
if response.StatusCode != http.StatusOK {
logger.Error("Error while generating grafana images", zap.Error(err))
responseChannel <- service.GrafanaImageFetchResponse{Error: errors.New("error while generating grafana images")}
return
}
if response.Body == nil {
logger.Error("empty response body received from monitoring service")
responseChannel <- service.GrafanaImageFetchResponse{"",
errors.New("empty response body received from monitoring service"),
}
return
}
directoryPath, err := processGrafanaImagesResponse(requestId, response)
if err != nil {
logger.Error("Error while processing grafana images response", zap.Error(err))
responseChannel <- service.GrafanaImageFetchResponse{Error: err}
return
}
responseChannel <- service.GrafanaImageFetchResponse{DirectoryPath: directoryPath}
return
}
func processGrafanaImagesResponse(requestId string, response *http.Response) (string, error) {
currentDirectory, _ := os.Getwd()
grafanaFilesDirectory := currentDirectory + "/grafana_files"
// Create files directory if not exists to store all grafana images
err := util.CheckAndCreateDirectory(grafanaFilesDirectory)
if err != nil {
logger.Error("Error while creating directory", zap.Error(err))
return "", err
}
destinationDirectoryPath := fmt.Sprintf("%v/%v", grafanaFilesDirectory, requestId)
err = util.CheckAndCreateDirectory(destinationDirectoryPath)
zipFileName := requestId + ".zip"
zipFilePath := fmt.Sprintf("%v/%v.zip", destinationDirectoryPath, requestId)
zipFile, err := util.CreateFileWithName(destinationDirectoryPath, zipFileName)
if err != nil {
logger.Error("Error while creating zip file", zap.Error(err))
return "", err
}
defer util.CloseFile(zipFile)
_, err = io.Copy(zipFile, response.Body)
if err != nil {
logger.Error("Error while copying zip file", zap.Error(err))
return "", err
}
err = unzipAndDeleteZipFile(zipFilePath, destinationDirectoryPath)
if err != nil {
return "", err
}
return destinationDirectoryPath, nil
}
func unzipAndDeleteZipFile(zipFilePath string, destinationDirectoryPath string) error {
err := util.UnzipFile(zipFilePath, destinationDirectoryPath)
if err != nil {
logger.Error("Error while unzipping file", zap.Error(err))
return err
}
err = util.DeleteFile(zipFilePath)
if err != nil {
logger.Error("Error while deleting file", zap.Error(err))
return err
}
return nil
}

View File

@@ -0,0 +1,170 @@
package monitoringService
import (
"bytes"
"encoding/json"
"errors"
"github.com/gojuno/minimock/v3"
"github.com/google/uuid"
"github.com/spf13/viper"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
"houston/logger"
"houston/mocks"
"houston/model"
"houston/pkg/rest"
service "houston/service/response"
"net/http"
"testing"
)
type MonitoringServiceSuite struct {
suite.Suite
}
func (suite *MonitoringServiceSuite) SetupSuite() {
logger.InitLogger()
viper.Set("MONITORING_SERVICE_BASEURL", "http://localhost:8082")
viper.Set("DEFAULT_MONITORING_SERVICE_TIMEOUT", "5s")
viper.Set("MONITORING_SERVICE_GET_GRAFANA_IMAGES_PATH", "/incident/v1/grafanaScreenshot")
viper.Set("MAX_FILE_SIZE", 10)
viper.Set("DEFAULT_HTTP_REQUEST_TIMEOUT", "5s")
}
func (suite *MonitoringServiceSuite) Test_GetGrafanaImages_500Case() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
requestId := uuid.NewString()
restClient := mocks.NewClientActionsMock(controller)
// Mocking DB calls
incidents := []string{"incident1", "incident2"}
// Mocking HTTP calls
defaultTimeOut := viper.GetDuration("DEFAULT_MONITORING_SERVICE_TIMEOUT")
fullURL := viper.GetString("MONITORING_SERVICE_BASEURL") + viper.GetString("MONITORING_SERVICE_GET_GRAFANA_IMAGES_PATH")
getGrafanaImagesRequestBody := model.GetGrafanaImagesRequest{
Incidents: incidents,
}
payload, _ := json.Marshal(getGrafanaImagesRequestBody)
requestHeaders := map[string]string{
"Content-Type": "application/json",
"CORRELATION_ID": requestId,
}
response := &http.Response{
StatusCode: 500,
Body: nil,
}
restClient.PostWithTimeoutMock.When(fullURL, *bytes.NewBuffer(payload), requestHeaders, defaultTimeOut,
nil).Then(response, nil)
responseChannel := make(chan service.GrafanaImageFetchResponse)
defer close(responseChannel)
go NewMonitoringServiceActionsImpl(restClient).GetGrafanaImages(incidents, requestId, responseChannel)
select {
case monitoringServiceResponse := <-responseChannel:
logger.Info("monitoringServiceResponse", zap.Any("monitoringServiceResponse", monitoringServiceResponse))
suite.Empty(monitoringServiceResponse.DirectoryPath)
suite.Equal(monitoringServiceResponse.Error, errors.New("error while generating grafana images"))
}
}
func (suite *MonitoringServiceSuite) Test_GetGrafanaImages_ErrorResponseCase() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
requestId := uuid.NewString()
restClient := mocks.NewClientActionsMock(controller)
// Mocking DB calls
incidents := []string{"incident1", "incident2"}
// Mocking HTTP calls
defaultTimeOut := viper.GetDuration("DEFAULT_MONITORING_SERVICE_TIMEOUT")
fullURL := viper.GetString("MONITORING_SERVICE_BASEURL") + viper.GetString("MONITORING_SERVICE_GET_GRAFANA_IMAGES_PATH")
getGrafanaImagesRequestBody := model.GetGrafanaImagesRequest{
Incidents: incidents,
}
payload, _ := json.Marshal(getGrafanaImagesRequestBody)
requestHeaders := map[string]string{
"Content-Type": "application/json",
"CORRELATION_ID": requestId,
}
restClient.PostWithTimeoutMock.When(fullURL, *bytes.NewBuffer(payload), requestHeaders, defaultTimeOut,
nil).Then(nil, errors.New("error while getting grafana images"))
responseChannel := make(chan service.GrafanaImageFetchResponse)
defer close(responseChannel)
go NewMonitoringServiceActionsImpl(restClient).GetGrafanaImages(incidents, requestId, responseChannel)
select {
case monitoringServiceResponse := <-responseChannel:
logger.Info("monitoringServiceResponse", zap.Any("monitoringServiceResponse", monitoringServiceResponse))
suite.Empty(monitoringServiceResponse.DirectoryPath)
suite.Equal(monitoringServiceResponse.Error, errors.New("error while getting grafana images"))
}
}
func (suite *MonitoringServiceSuite) Test_GetGrafanaImages_EmptyResponseBodyCase() {
controller := minimock.NewController(suite.T())
suite.T().Cleanup(controller.Finish)
requestId := uuid.NewString()
restClient := mocks.NewClientActionsMock(controller)
// Mocking DB calls
incidents := []string{"incident1", "incident2"}
// Mocking HTTP calls
defaultTimeOut := viper.GetDuration("DEFAULT_MONITORING_SERVICE_TIMEOUT")
fullURL := viper.GetString("MONITORING_SERVICE_BASEURL") + viper.GetString("MONITORING_SERVICE_GET_GRAFANA_IMAGES_PATH")
getGrafanaImagesRequestBody := model.GetGrafanaImagesRequest{
Incidents: incidents,
}
payload, _ := json.Marshal(getGrafanaImagesRequestBody)
requestHeaders := map[string]string{
"Content-Type": "application/json",
"CORRELATION_ID": requestId,
}
response := &http.Response{
StatusCode: 200,
Body: nil,
}
restClient.PostWithTimeoutMock.When(fullURL, *bytes.NewBuffer(payload), requestHeaders, defaultTimeOut,
nil).Then(response, nil)
responseChannel := make(chan service.GrafanaImageFetchResponse)
defer close(responseChannel)
go NewMonitoringServiceActionsImpl(restClient).GetGrafanaImages(incidents, requestId, responseChannel)
select {
case monitoringServiceResponse := <-responseChannel:
suite.Empty(monitoringServiceResponse.DirectoryPath)
suite.Equal(monitoringServiceResponse.Error, errors.New("empty response body received from monitoring service"))
}
}
func (suite *MonitoringServiceSuite) Test_getfiles() {
viper.Set("MONITORING_SERVICE_BASEURL", "http://localhost:8082")
viper.Set("DEFAULT_MONITORING_SERVICE_TIMEOUT", "500s")
viper.Set("MONITORING_SERVICE_GET_GRAFANA_IMAGES_PATH", "/incident/v1/grafanaScreenshot")
viper.Set("MAX_FILE_SIZE", 1)
viper.Set("DEFAULT_HTTP_REQUEST_TIMEOUT", "500s")
monitoringService := NewMonitoringServiceActionsImpl(rest.NewClientActionsImpl())
responseChannel := make(chan service.GrafanaImageFetchResponse)
go monitoringService.GetGrafanaImages([]string{"_incident-8705", "_incident-8707"}, uuid.NewString(), responseChannel)
select {
case monitoringServiceResponse := <-responseChannel:
logger.Info("monitoringServiceResponse", zap.Any("monitoringServiceResponse", monitoringServiceResponse))
suite.NotEmpty(monitoringServiceResponse.DirectoryPath)
suite.Nil(monitoringServiceResponse.Error)
}
}
func TestDriveService(t *testing.T) {
suite.Run(t, new(MonitoringServiceSuite))
}

View File

@@ -0,0 +1,6 @@
package service
type GrafanaImageFetchResponse struct {
DirectoryPath string
Error error
}