K8s: Schema v2: Return 406 in /api (#101842)
This commit is contained in:
@@ -85,6 +85,7 @@ func dashboardGuardianResponse(err error) response.Response {
|
||||
// 401: unauthorisedError
|
||||
// 403: forbiddenError
|
||||
// 404: notFoundError
|
||||
// 406: notAcceptableError
|
||||
// 500: internalServerError
|
||||
//
|
||||
//nolint:gocyclo
|
||||
@@ -99,14 +100,10 @@ func (hs *HTTPServer) GetDashboard(c *contextmodel.ReqContext) response.Response
|
||||
return rsp
|
||||
}
|
||||
|
||||
// V2 values should be read from the k8s API
|
||||
// v2 is not supported in /api
|
||||
if strings.HasPrefix(dash.APIVersion, "v2") {
|
||||
root := hs.Cfg.AppSubURL
|
||||
if !strings.HasSuffix(root, "/") {
|
||||
root += "/"
|
||||
}
|
||||
url := fmt.Sprintf("%sapis/dashboard.grafana.app/%s/namespaces/%s/dashboards/%s", root, dash.APIVersion, hs.namespacer(c.OrgID), dash.UID)
|
||||
return response.Redirect(url)
|
||||
url := fmt.Sprintf("/apis/dashboard.grafana.app/%s/namespaces/%s/dashboards/%s", dash.APIVersion, hs.namespacer(c.SignedInUser.GetOrgID()), dash.UID)
|
||||
return response.Error(http.StatusNotAcceptable, "dashboard api version not supported, use "+url+" instead", nil)
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -694,6 +695,46 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
assert.Equal(t, false, dash.Meta.Provisioned)
|
||||
}, mockSQLStore)
|
||||
})
|
||||
|
||||
t.Run("v2 dashboards should not be returned in api", func(t *testing.T) {
|
||||
mockSQLStore := dbtest.NewFakeDB()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
|
||||
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "apiVersion": "v2"}`))
|
||||
require.NoError(t, err)
|
||||
qResult := &dashboards.Dashboard{
|
||||
ID: 1,
|
||||
UID: "dash",
|
||||
OrgID: 1,
|
||||
APIVersion: "v2",
|
||||
Data: dataValue,
|
||||
}
|
||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil)
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true})
|
||||
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", org.RoleEditor, func(sc *scenarioContext) {
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||
SQLStore: mockSQLStore,
|
||||
AccessControl: accesscontrolmock.New(),
|
||||
DashboardService: dashboardService,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
starService: startest.NewStarServiceFake(),
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
dashboardProvisioningService: mockDashboardProvisioningService{},
|
||||
folderService: foldertest.NewFakeService(),
|
||||
log: log.New("test"),
|
||||
namespacer: func(orgID int64) string { return strconv.FormatInt(orgID, 10) },
|
||||
}
|
||||
hs.callGetDashboard(sc)
|
||||
|
||||
assert.Equal(t, http.StatusNotAcceptable, sc.resp.Code)
|
||||
result := sc.ToJSON()
|
||||
assert.Equal(t, "dashboard api version not supported, use /apis/dashboard.grafana.app/v2/namespaces/1/dashboards/dash instead", result.Get("message").MustString())
|
||||
}, mockSQLStore)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDashboardVersionsAPIEndpoint(t *testing.T) {
|
||||
|
||||
@@ -52,6 +52,11 @@ type NotFoundError GenericError
|
||||
// swagger:response badRequestError
|
||||
type BadRequestError GenericError
|
||||
|
||||
// NotAcceptableError is returned when the server cannot produce a response matching the accepted formats.
|
||||
//
|
||||
// swagger:response notAcceptableError
|
||||
type NotAcceptableError GenericError
|
||||
|
||||
// ConflictError
|
||||
//
|
||||
// swagger:response conflictError
|
||||
|
||||
@@ -334,10 +334,10 @@ func TestIntegrationLegacySupport(t *testing.T) {
|
||||
require.Equal(t, 200, rsp.Response.StatusCode)
|
||||
require.Equal(t, "v1alpha1", rsp.Result.Meta.APIVersion)
|
||||
|
||||
// V2 should send a redirect
|
||||
// V2 should send a not acceptable
|
||||
rsp = apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Path: "/api/dashboards/uid/test-v2",
|
||||
}, &dtos.DashboardFullWithMeta{})
|
||||
require.Equal(t, 302, rsp.Response.StatusCode) // redirect
|
||||
require.Equal(t, 406, rsp.Response.StatusCode) // not acceptable
|
||||
}
|
||||
|
||||
@@ -3356,6 +3356,9 @@
|
||||
"404": {
|
||||
"$ref": "#/responses/notFoundError"
|
||||
},
|
||||
"406": {
|
||||
"$ref": "#/responses/notAcceptableError"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/responses/internalServerError"
|
||||
}
|
||||
@@ -24440,6 +24443,12 @@
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"notAcceptableError": {
|
||||
"description": "NotAcceptableError is returned when the server cannot produce a response matching the accepted formats.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ErrorResponseBody"
|
||||
}
|
||||
},
|
||||
"notFoundError": {
|
||||
"description": "NotFoundError is returned when the requested resource was not found.",
|
||||
"schema": {
|
||||
|
||||
@@ -1641,6 +1641,16 @@
|
||||
},
|
||||
"description": "(empty)"
|
||||
},
|
||||
"notAcceptableError": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponseBody"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "NotAcceptableError is returned when the server cannot produce a response matching the accepted formats."
|
||||
},
|
||||
"notFoundError": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
@@ -16997,6 +17007,9 @@
|
||||
"404": {
|
||||
"$ref": "#/components/responses/notFoundError"
|
||||
},
|
||||
"406": {
|
||||
"$ref": "#/components/responses/notAcceptableError"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/internalServerError"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user