mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 17:02:20 +08:00
124 lines
3.9 KiB
Go
124 lines
3.9 KiB
Go
package dashboard
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
"k8s.io/apiserver/pkg/admission"
|
|
|
|
dashboardV0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
|
dashboardV1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
|
dashboardV2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
|
"github.com/grafana/grafana/apps/dashboard/pkg/migration"
|
|
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
|
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
|
)
|
|
|
|
func (b *DashboardsAPIBuilder) Mutate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) {
|
|
op := a.GetOperation()
|
|
|
|
// Mutate removes any internal ID set in the spec & adds it as a label
|
|
if op != admission.Create && op != admission.Update {
|
|
return nil
|
|
}
|
|
var internalID int64
|
|
obj := a.GetObject()
|
|
meta, err := utils.MetaAccessor(obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var migrationErr error
|
|
var resourceInfo utils.ResourceInfo
|
|
switch v := obj.(type) {
|
|
case *dashboardV0.Dashboard:
|
|
delete(v.Spec.Object, "uid")
|
|
delete(v.Spec.Object, "version")
|
|
if id, ok := v.Spec.Object["id"].(float64); ok {
|
|
delete(v.Spec.Object, "id")
|
|
internalID = int64(id)
|
|
}
|
|
resourceInfo = dashboardV0.DashboardResourceInfo
|
|
case *dashboardV1.Dashboard:
|
|
delete(v.Spec.Object, "uid")
|
|
delete(v.Spec.Object, "version")
|
|
if id, ok := v.Spec.Object["id"].(float64); ok {
|
|
delete(v.Spec.Object, "id")
|
|
internalID = int64(id)
|
|
}
|
|
resourceInfo = dashboardV1.DashboardResourceInfo
|
|
migrationErr = migration.Migrate(v.Spec.Object, schemaversion.LATEST_VERSION)
|
|
if migrationErr != nil {
|
|
v.Status.Conversion = &dashboardV1.DashboardConversionStatus{
|
|
Failed: true,
|
|
Error: migrationErr.Error(),
|
|
}
|
|
}
|
|
case *dashboardV2.Dashboard:
|
|
// Temporary fix: The generator fails to properly initialize this property, so we'll do it here
|
|
// until the generator is fixed.
|
|
if v.Spec.Layout.GridLayoutKind == nil && v.Spec.Layout.RowsLayoutKind == nil && v.Spec.Layout.AutoGridLayoutKind == nil && v.Spec.Layout.TabsLayoutKind == nil {
|
|
v.Spec.Layout.GridLayoutKind = &dashboardV2.DashboardGridLayoutKind{
|
|
Kind: "GridLayout",
|
|
Spec: dashboardV2.DashboardGridLayoutSpec{},
|
|
}
|
|
}
|
|
|
|
resourceInfo = dashboardV2.DashboardResourceInfo
|
|
|
|
// Noop for V2
|
|
default:
|
|
return fmt.Errorf("mutation error: expected to dashboard, got %T", obj)
|
|
}
|
|
|
|
if internalID != 0 {
|
|
meta.SetDeprecatedInternalID(internalID) // nolint:staticcheck
|
|
}
|
|
|
|
fieldValidationMode := getFieldValidationMode(a)
|
|
|
|
var validationErrorList field.ErrorList
|
|
var validationProcessingError error
|
|
if migrationErr == nil {
|
|
// Migration check passed, validate the spec now - this will respect the field validation mode!
|
|
validationErrorList, validationProcessingError = b.ValidateDashboardSpec(ctx, obj, fieldValidationMode)
|
|
}
|
|
|
|
// Only fail if the field validation mode is strict
|
|
if fieldValidationMode == metav1.FieldValidationStrict {
|
|
if migrationErr != nil {
|
|
return apierrors.NewInvalid(resourceInfo.GroupVersionKind().GroupKind(), meta.GetName(), field.ErrorList{
|
|
field.Invalid(field.NewPath("spec"), meta.GetName(), migrationErr.Error())})
|
|
}
|
|
if validationProcessingError != nil {
|
|
return validationProcessingError
|
|
}
|
|
if len(validationErrorList) > 0 {
|
|
return apierrors.NewInvalid(resourceInfo.GroupVersionKind().GroupKind(), meta.GetName(), validationErrorList)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getFieldValidationMode(a admission.Attributes) string {
|
|
var validation string
|
|
switch opts := a.GetOperationOptions().(type) {
|
|
case *metav1.CreateOptions:
|
|
validation = opts.FieldValidation
|
|
case *metav1.UpdateOptions:
|
|
validation = opts.FieldValidation
|
|
default:
|
|
validation = metav1.FieldValidationStrict
|
|
}
|
|
|
|
if validation == "" {
|
|
validation = metav1.FieldValidationStrict
|
|
}
|
|
|
|
return validation
|
|
}
|