Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Aggregation Sum line and added Date Filter #9

Merged
merged 3 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions .github/workflows/al-test.yml

This file was deleted.

10 changes: 0 additions & 10 deletions .github/workflows/test.yml

This file was deleted.

27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Grafana.Plugin.CostManagement
This is a Grafana datasource plugin to display Azure cost data

## Requirements

npm, mage, go, and any supported IDE for DEV running (suggested would be VS Code).

## Local Running

This requires a linux environment with npm, mage and go installed

###Build Steps

Clone the repos then cd dfe-azurecostbackend-datasource
npm install
npm run dev
mage -v build:linux

###Sign the Plugin

export GRAFANA_ACCESS_POLICY_TOKEN=your token created in the grafana cloud site
npx @grafana/sign-plugin@latest --rootUrls http://localhost:3000/

###Run Steps

Call: docker-compose up
Select the Azure Cost Datasource, confugure it then add it to a pannel
2 changes: 1 addition & 1 deletion dfe-azurecostbackend-datasource/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "1",
"name": "azurecost-backend",
"version": "1.0.0",
"version": "1.0.1",
"description": "Azure cost backend",
"scripts": {
"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",
Expand Down
46 changes: 41 additions & 5 deletions dfe-azurecostbackend-datasource/pkg/plugin/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,15 @@ func (d *Datasource) query(_ context.Context, pCtx backend.PluginContext, query
// Use the token as needed
log.Println("Fetched token:", token)

costs, err := getCosts(token, d.config)
start, end := getCurrentYearDates()

timeRange := query.TimeRange
if !timeRange.From.IsZero() && !timeRange.To.IsZero(){
start = timeRange.From.Format("2006-01-02")
end = timeRange.To.Format("2006-01-02")
}

costs, err := getCosts(token, d.config, start, end)
if err != nil {
log.Println("Error getting costs:", err)
return response
Expand All @@ -243,6 +251,9 @@ func (d *Datasource) query(_ context.Context, pCtx backend.PluginContext, query
// // Add fields for "time" and "values"
timeField := data.NewField("time", nil, make([]time.Time, len(datepoints)))
valuesField := data.NewField("values", nil, make([]float64, len(datepoints)))
sumField := data.NewField("sum-values", nil, make([]float64, len(datepoints)))

rollingTotal := 0.0

// Populate the fields with DatePoint values
for i, dp := range datepoints {
Expand All @@ -252,12 +263,16 @@ func (d *Datasource) query(_ context.Context, pCtx backend.PluginContext, query
fmt.Println("Error parsing date:", err)
continue
}

rollingTotal = rollingTotal + dp.Value

timeField.Set(i, date)
valuesField.Set(i, dp.Value)
sumField.Set(i, rollingTotal)
}

// Add fields to the frame
frame.Fields = append(frame.Fields, timeField, valuesField)
frame.Fields = append(frame.Fields, timeField, valuesField, sumField)

// add the frames to the response.
response.Frames = append(response.Frames, frame)
Expand Down Expand Up @@ -358,11 +373,16 @@ func parseAccessToken(body []byte) (string, error) {
}

// Fetch Costs
func getCosts(token string, config Config) (CostResponse, error) {
func getCosts(token string, config Config, start string, end string) (CostResponse, error) {
url := config.SubscriptionID + "/providers/Microsoft.CostManagement/query?api-version=2023-03-01"

bodyParameters := map[string]interface{}{
"type": "Usage",
"timeframe": "MonthToDate",
"timeframe": "Custom",
"timeperiod": map[string]string{
"from": start,
"to": end,
},
"dataset": map[string]interface{}{
"granularity": "Daily",
"aggregation": map[string]interface{}{
Expand All @@ -380,7 +400,7 @@ func getCosts(token string, config Config) (CostResponse, error) {
}

requestURL := config.AzureCostSubscriptionUrl + url
if(len(config.TokenURL) > 1){
if len(config.TokenURL) > 1 {
requestURL = config.TokenURL
}
req, err := http.NewRequest("POST", requestURL, bytes.NewBuffer(body))
Expand Down Expand Up @@ -461,3 +481,19 @@ func convertToStandardDateFormat(inputDate string) (string, error) {
fmt.Println("Invalid date format. Expected YYYYMMDD.")
return inputDate, fmt.Errorf("Invalid date format. Expected YYYYMMDD.")
}

func getCurrentYearDates() (string, string) {
// Get the current year
currentYear := time.Now().Year()

// Get the 1st of January of the current year
firstOfJanuary := time.Date(currentYear, time.January, 1, 0, 0, 0, 0, time.UTC)
firstOfJanuaryFormatted := firstOfJanuary.Format("2006-01-02")

// Get the 31st of December of the current year
thirtyFirstOfDecember := time.Date(currentYear, time.December, 31, 0, 0, 0, 0, time.UTC)
thirtyFirstOfDecemberFormatted := thirtyFirstOfDecember.Format("2006-01-02")

return firstOfJanuaryFormatted, thirtyFirstOfDecemberFormatted
}

32 changes: 31 additions & 1 deletion dfe-azurecostbackend-datasource/pkg/plugin/datasource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
"encoding/json"
"reflect"

Expand Down Expand Up @@ -143,7 +144,8 @@ func TestGetCosts(t *testing.T) {
// Call the getCosts function with the mock token and config
token := "your-mock-token"
config.TokenURL = server.URL
costs, err := getCosts(token, config)
start, end := getCurrentYearDates()
costs, err := getCosts(token, config, start, end)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
Expand Down Expand Up @@ -187,3 +189,31 @@ func createMockCostResponse() CostResponse {

return costResponse
}

var timeNow = time.Now

func TestGetCurrentYearDates(t *testing.T) {
// Mock current date for testing
mockDate := time.Date(2023, time.January, 15, 0, 0, 0, 0, time.UTC)
// Save the original time function and replace it with a mock
originalTimeNow := timeNow
timeNow = func() time.Time { return mockDate }
defer func() { timeNow = originalTimeNow }()

// Call the function to get the formatted dates
firstOfJanuary, thirtyFirstOfDecember := getCurrentYearDates()

// Expected results for the mock date
expectedFirstOfJanuary := "2023-01-01"
expectedThirtyFirstOfDecember := "2023-12-31"

// Check if the actual results match the expected results
if firstOfJanuary != expectedFirstOfJanuary {
t.Errorf("First of January: expected %s, got %s", expectedFirstOfJanuary, firstOfJanuary)
}

if thirtyFirstOfDecember != expectedThirtyFirstOfDecember {
t.Errorf("Thirty-First of December: expected %s, got %s", expectedThirtyFirstOfDecember, thirtyFirstOfDecember)
}
}

Loading