Files
grafana/pkg/registry/apis/datasource/sub_query_test.go
Sarah Zinger 0152f414f0 DS Apiservers: return 404 when receiving a datasource not found error (#100025)
* DS Apiservers should return a k8s 404 error

* Do not swallow status codes

* Updates from initial CR.

* Add test for ds apiserver to retunr 404 when a datasource is not found

* Didn't intend for a change here
2025-02-10 12:07:51 -05:00

146 lines
4.6 KiB
Go

package datasource
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/stretchr/testify/require"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
)
func TestSubQueryConnect(t *testing.T) {
sqr := subQueryREST{
builder: &DataSourceAPIBuilder{
client: mockClient{
lastCalledWithHeaders: &map[string]string{},
},
datasources: mockDatasources{},
contextProvider: mockContextProvider{},
log: log.NewNopLogger(),
},
}
mr := mockResponder{}
handler, err := sqr.Connect(context.Background(), "dsname", nil, mr)
require.NoError(t, err)
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/some-path", nil)
req.Header.Set(models.FromAlertHeaderName, "true")
req.Header.Set(models.CacheSkipHeaderName, "true")
req.Header.Set("X-Rule-Name", "name-1")
req.Header.Set("X-Rule-Uid", "abc")
req.Header.Set("X-Rule-Folder", "folder-1")
req.Header.Set("X-Rule-Source", "grafana-ruler")
req.Header.Set("X-Rule-Type", "type-1")
req.Header.Set("X-Rule-Version", "version-1")
req.Header.Set("X-Grafana-Org-Id", "1")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("some-unexpected-header", "some-value")
handler.ServeHTTP(rr, req)
// test that headers are forwarded and cased appropriately
require.Equal(t, map[string]string{
models.FromAlertHeaderName: "true",
models.CacheSkipHeaderName: "true",
"X-Rule-Name": "name-1",
"X-Rule-Uid": "abc",
"X-Rule-Folder": "folder-1",
"X-Rule-Source": "grafana-ruler",
"X-Rule-Type": "type-1",
"X-Rule-Version": "version-1",
"X-Grafana-Org-Id": "1",
}, *sqr.builder.client.(mockClient).lastCalledWithHeaders)
}
func TestSubQueryConnectWhenDatasourceNotFound(t *testing.T) {
sqr := subQueryREST{
builder: &DataSourceAPIBuilder{
client: mockClient{
lastCalledWithHeaders: &map[string]string{},
},
datasources: mockDatasources{},
contextProvider: mockContextProvider{},
log: log.NewNopLogger(),
},
}
mr := mockResponder{}
_, err := sqr.Connect(context.Background(), "dsname-that-does-not-exist", nil, mr)
require.Error(t, err)
var statusErr *k8serrors.StatusError
require.True(t, errors.As(err, &statusErr))
require.Equal(t, int32(404), statusErr.Status().Code)
}
type mockClient struct {
lastCalledWithHeaders *map[string]string
}
func (m mockClient) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
*m.lastCalledWithHeaders = req.Headers
return nil, fmt.Errorf("mock error")
}
func (m mockClient) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
return nil
}
func (m mockClient) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
return nil, nil
}
func (m mockClient) ConvertObjects(ctx context.Context, req *backend.ConversionRequest) (*backend.ConversionResponse, error) {
return nil, nil
}
type mockResponder struct {
}
// Object writes the provided object to the response. Invoking this method multiple times is undefined.
func (m mockResponder) Object(statusCode int, obj runtime.Object) {
}
// Error writes the provided error to the response. This method may only be invoked once.
func (m mockResponder) Error(err error) {
}
type mockDatasources struct {
}
// Get gets a specific datasource (that the user in context can see)
func (m mockDatasources) Get(ctx context.Context, uid string) (*v0alpha1.DataSourceConnection, error) {
return nil, nil
}
// List lists all data sources the user in context can see
func (m mockDatasources) List(ctx context.Context) (*v0alpha1.DataSourceConnectionList, error) {
return nil, nil
}
// Return settings (decrypted!) for a specific plugin
// This will require "query" permission for the user in context
func (m mockDatasources) GetInstanceSettings(ctx context.Context, uid string) (*backend.DataSourceInstanceSettings, error) {
if uid == "dsname" {
return nil, nil
}
return nil, datasources.ErrDataSourceNotFound
}
type mockContextProvider struct {
}
func (m mockContextProvider) PluginContextForDataSource(ctx context.Context, datasourceSettings *backend.DataSourceInstanceSettings) (backend.PluginContext, error) {
return backend.PluginContext{}, nil
}