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

Sandersaarond/forward tenant #79

Merged
merged 15 commits into from
Jul 31, 2024
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
3 changes: 1 addition & 2 deletions .config/grafana/plugins/app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ apps:
metadataUrl: http://ai-training-api:8000
lokiDatasourceName: Loki
mimirDatasourceName: Mimir
isApiKeySet: true
secureJsonData:
apiKey: secret-key
metadataToken: secret-key
2 changes: 1 addition & 1 deletion grafana-aitraining-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "grafana-aitraining-app",
"version": "1.0.2",
"version": "1.0.3",
"description": "Track compare and visualize your ml models with 5 lines of code",
"scripts": {
"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",
Expand Down
56 changes: 34 additions & 22 deletions grafana-aitraining-app/pkg/plugin/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package plugin
import (
"context"
"encoding/json"
"fmt"
"net/http"

"github.com/grafana/grafana-plugin-sdk-go/backend"
Expand All @@ -24,42 +25,53 @@ var (
// App is an example app backend plugin which can respond to data queries.
type App struct {
backend.CallResourceHandler

lokiDatasourceName string
mimirDatasourceName string
metadataUrl string
metadataToken string
stackId string
}

// NewApp creates a new example *App instance.
func NewApp(_ context.Context, appSettings backend.AppInstanceSettings) (instancemgmt.Instance, error) {
log.DefaultLogger.Info("Creating new App instance")
var app App

var settings map[string]interface{}
err := json.Unmarshal(appSettings.JSONData, &settings)
if err != nil {
log.DefaultLogger.Error("Failed to unmarshal app settings", "error", err)
return nil, err
}
err := json.Unmarshal(appSettings.JSONData, &settings)
if err != nil {
log.DefaultLogger.Error("Failed to unmarshal app settings", "error", err)
return nil, err
}

// Check if metadataUrl exists in settings
if value, ok := settings["metadataUrl"]; ok {
switch url := value.(type) {
case string:
app.metadataUrl = url
log.DefaultLogger.Info("Metadata URL set", "url", app.metadataUrl)
default:
app.metadataUrl = ""
log.DefaultLogger.Warn("Metadata URL in settings but is not a string, using empty string")
// Helper function to get string value from JSONData
getStringValue := func(key string) string {
if value, ok := settings[key]; ok {
if strValue, ok := value.(string); ok {
log.DefaultLogger.Info(fmt.Sprintf("%s set from JSONData", key), key, strValue)
return strValue
}
log.DefaultLogger.Warn(fmt.Sprintf("%s in JSONData is not a string", key))
} else {
log.DefaultLogger.Warn(fmt.Sprintf("%s not found in settings", key))
}
return ""
}

// Set values from JSONData
app.metadataUrl = getStringValue("metadataUrl")
app.stackId = getStringValue("stackId")
app.lokiDatasourceName = getStringValue("lokiDatasourceName")
app.mimirDatasourceName = getStringValue("mimirDatasourceName")

// Set metadataToken from DecryptedSecureJSONData
if token, ok := appSettings.DecryptedSecureJSONData["metadataToken"]; ok {
app.metadataToken = token
log.DefaultLogger.Info("Metadata token set from DecryptedSecureJSONData")
} else {
// If metadataUrl is not found, set it to an empty string or a default value
app.metadataUrl = ""
log.DefaultLogger.Warn("Metadata URL not found in settings, using empty string")
log.DefaultLogger.Warn("Metadata token not found in DecryptedSecureJSONData")
}

// Use a httpadapter (provided by the SDK) for resource calls. This allows us
// to use a *http.ServeMux for resource calls, so we can map multiple routes
// to CallResource without having to implement extra logic.
// Use a httpadapter (provided by the SDK) for resource calls.
mux := http.NewServeMux()
app.registerRoutes(mux)
app.CallResourceHandler = httpadapter.New(mux)
Expand Down
127 changes: 127 additions & 0 deletions grafana-aitraining-app/pkg/plugin/app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package plugin

import (
"context"
"encoding/json"
"testing"

"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestNewApp(t *testing.T) {
tests := []struct {
name string
jsonData map[string]interface{}
decryptedSecureJSONData map[string]string
expected map[string]string
}{
{
name: "All fields set correctly",
jsonData: map[string]interface{}{
"metadataUrl": "https://example.com",
"stackId": "stack123",
"lokiDatasourceName": "loki",
"mimirDatasourceName": "mimir",
},
decryptedSecureJSONData: map[string]string{
"metadataToken": "token123",
},
expected: map[string]string{
"metadataUrl": "https://example.com",
"stackId": "stack123",
"lokiDatasourceName": "loki",
"mimirDatasourceName": "mimir",
"metadataToken": "token123",
},
},
{
name: "Missing some fields",
jsonData: map[string]interface{}{
"metadataUrl": "https://example.com",
"lokiDatasourceName": "loki",
},
decryptedSecureJSONData: map[string]string{
"metadataToken": "token456",
},
expected: map[string]string{
"metadataUrl": "https://example.com",
"stackId": "",
"lokiDatasourceName": "loki",
"mimirDatasourceName": "",
"metadataToken": "token456",
},
},
{
name: "No data provided",
expected: map[string]string{
"metadataUrl": "",
"stackId": "",
"lokiDatasourceName": "",
"mimirDatasourceName": "",
"metadataToken": "",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
jsonData, err := json.Marshal(tt.jsonData)
require.NoError(t, err)

appSettings := backend.AppInstanceSettings{
JSONData: jsonData,
DecryptedSecureJSONData: tt.decryptedSecureJSONData,
}

instance, err := NewApp(context.Background(), appSettings)
require.NoError(t, err)
require.NotNil(t, instance)

app, ok := instance.(*App)
require.True(t, ok)

assert.Equal(t, tt.expected["metadataUrl"], app.metadataUrl)
assert.Equal(t, tt.expected["stackId"], app.stackId)
assert.Equal(t, tt.expected["lokiDatasourceName"], app.lokiDatasourceName)
assert.Equal(t, tt.expected["mimirDatasourceName"], app.mimirDatasourceName)
assert.Equal(t, tt.expected["metadataToken"], app.metadataToken)
})
}
}

func TestNewAppWithInvalidJSONData(t *testing.T) {
appSettings := backend.AppInstanceSettings{
JSONData: []byte(`invalid json`),
}

_, err := NewApp(context.Background(), appSettings)
assert.Error(t, err)
}

func TestNewAppWithNonStringValues(t *testing.T) {
jsonData, err := json.Marshal(map[string]interface{}{
"metadataUrl": 123,
"stackId": true,
"lokiDatasourceName": []string{"not", "a", "string"},
"mimirDatasourceName": map[string]string{"not": "a string"},
})
require.NoError(t, err)

appSettings := backend.AppInstanceSettings{
JSONData: jsonData,
}

instance, err := NewApp(context.Background(), appSettings)
require.NoError(t, err)
require.NotNil(t, instance)

app, ok := instance.(*App)
require.True(t, ok)

assert.Empty(t, app.metadataUrl)
assert.Empty(t, app.stackId)
assert.Empty(t, app.lokiDatasourceName)
assert.Empty(t, app.mimirDatasourceName)
}
4 changes: 2 additions & 2 deletions grafana-aitraining-app/pkg/plugin/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ func (a *App) metadataHandler(target string) func(http.ResponseWriter, *http.Req
}

// Set Authorization header if token is available
if a.metadataToken != "" {
req.Header.Set("Authorization", "Bearer "+a.metadataToken)
if a.metadataToken != "" && a.stackId != "" { // Change belongs here
req.Header.Set("Authorization", "Bearer "+ a.stackId + ":" + a.metadataToken)
log.DefaultLogger.Debug("Added bearer token to request")
} else {
log.DefaultLogger.Debug("No metadata token set, not adding to request")
Expand Down
3 changes: 2 additions & 1 deletion grafana-aitraining-app/pkg/plugin/resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,12 @@ func TestMetadataHandlerTokenInjection(t *testing.T) {
app := &App{
metadataUrl: "http://example.com",
metadataToken: "test-token",
stackId: "test-stack-id",
}

// Create a test server to mock the metadata service
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "Bearer test-token", r.Header.Get("Authorization"), "Bearer token should be set")
assert.Equal(t, "Bearer test-stack-id:test-token", r.Header.Get("Authorization"), "Bearer token should be set correctly")
assert.Equal(t, "/test", r.URL.Path, "Path should be correctly modified")
assert.Equal(t, "test-host", r.Header.Get("X-Forwarded-Host"), "X-Forwarded-Host should be set")
w.WriteHeader(http.StatusOK)
Expand Down
Loading
Loading