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
This commit is contained in:
Sarah Zinger 2025-02-10 12:07:51 -05:00 committed by GitHub
parent 27ece859e7
commit 0152f414f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 1 deletions

View File

@ -2,6 +2,7 @@ package datasource
import (
"context"
"errors"
"fmt"
"net/http"
@ -9,10 +10,14 @@ import (
data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1"
query "github.com/grafana/grafana/pkg/apis/query/v0alpha1"
query_headers "github.com/grafana/grafana/pkg/registry/apis/query"
"github.com/grafana/grafana/pkg/services/datasources"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana/pkg/web"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
)
type subQueryREST struct {
@ -50,9 +55,21 @@ func (r *subQueryREST) NewConnectOptions() (runtime.Object, bool, string) {
func (r *subQueryREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
pluginCtx, err := r.builder.getPluginContext(ctx, name)
if err != nil {
if errors.Is(err, datasources.ErrDataSourceNotFound) {
return nil, k8serrors.NewNotFound(
schema.GroupResource{
Group: r.builder.connectionResourceInfo.GroupResource().Group,
Resource: r.builder.connectionResourceInfo.GroupResource().Resource,
},
name,
)
}
return nil, err
}
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
dqr := data.QueryDataRequest{}
err := web.Bind(req, &dqr)

View File

@ -2,6 +2,7 @@ package datasource
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
@ -10,8 +11,10 @@ import (
"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"
)
@ -60,6 +63,26 @@ func TestSubQueryConnect(t *testing.T) {
}, *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
}
@ -108,7 +131,10 @@ func (m mockDatasources) List(ctx context.Context) (*v0alpha1.DataSourceConnecti
// 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) {
return nil, nil
if uid == "dsname" {
return nil, nil
}
return nil, datasources.ErrDataSourceNotFound
}
type mockContextProvider struct {