@ -2,12 +2,12 @@ package resources
import (
"context"
"errors"
"fmt"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository"
@ -34,23 +34,17 @@ func (r *DualReadWriter) Read(ctx context.Context, path string, ref string) (*Pa
info , err := r . repo . Read ( ctx , path , ref )
if err != nil {
return nil , err
return nil , fmt . Errorf ( "read file: %w" , err )
}
parsed , err := r . parser . Parse ( ctx , info , true )
parsed , err := r . parser . Parse ( ctx , info )
if err != nil {
return nil , err
return nil , fmt . Errorf ( "parse file: %w" , err )
}
// GVR will exist for anything we can actually save
// TODO: Add known error in parser for unsupported resource
if parsed . GVR == nil {
if parsed . GVK != nil {
//nolint:govet
parsed . Errors = append ( parsed . Errors , fmt . Errorf ( "unknown resource for Kind: %s" , parsed . GVK . Kind ) )
} else {
parsed . Errors = append ( parsed . Errors , fmt . Errorf ( "unknown resource" ) )
}
// Fail as we use the dry run for this response and it's not about updating the resource
if err := parsed . DryRun ( ctx ) ; err != nil {
return nil , fmt . Errorf ( "run dry run: %w" , err )
}
return parsed , nil
@ -68,14 +62,14 @@ func (r *DualReadWriter) Delete(ctx context.Context, path string, ref string, me
file , err := r . repo . Read ( ctx , path , ref )
if err != nil {
return nil , err // unable to read value
return nil , fmt . Errorf ( "read file: %w" , err )
}
// TODO: document in API specification
// We can only delete parsable things
parsed , err := r . parser . Parse ( ctx , file , false )
parsed , err := r . parser . Parse ( ctx , file )
if err != nil {
return nil , err // unable to read value
return nil , fmt . Errorf ( "parse file: %w" , err )
}
parsed . Action = provisioning . ResourceActionDelete
@ -162,25 +156,9 @@ func (r *DualReadWriter) CreateResource(ctx context.Context, path string, ref st
Ref : ref ,
}
// TODO: improve parser to parse out of reader
parsed , err := r . parser . Parse ( ctx , info , true )
parsed , err := r . parser . Parse ( ctx , info )
if err != nil {
if errors . Is ( err , ErrUnableToReadResourceBytes ) {
return nil , apierrors . NewBadRequest ( "unable to read the request as a resource" )
}
return nil , err
}
// GVR will exist for anything we can actually save
// TODO: Add known error in parser for unsupported resource
if parsed . GVR == nil {
return nil , apierrors . NewBadRequest ( "The payload does not map to a known resource" )
}
// Do not write if any errors exist
if len ( parsed . Errors ) > 0 {
return parsed , err
return nil , fmt . Errorf ( "parse file: %w" , err )
}
data , err = parsed . ToSaveBytes ( )
@ -188,8 +166,7 @@ func (r *DualReadWriter) CreateResource(ctx context.Context, path string, ref st
return nil , err
}
err = r . repo . Create ( ctx , path , ref , data , message )
if err != nil {
if err := r . repo . Create ( ctx , path , ref , data , message ) ; err != nil {
return nil , fmt . Errorf ( "create resource in repository: %w" , err )
}
@ -198,12 +175,23 @@ func (r *DualReadWriter) CreateResource(ctx context.Context, path string, ref st
// FIXME: to make sure if behaves in the same way as in sync, we should
// we should refactor the code to use the same function.
if ref == "" {
if err := r . writeParsed ( ctx , path , parsed ) ; err != nil {
parsed . Errors = append ( parsed . Errors , err )
if _ , err := r . folders . EnsureFolderPathExist ( ctx , path ) ; err != nil {
return nil , fmt . Errorf ( "ensure folder path exists: %w" , err )
}
if err := parsed . Run ( ctx ) ; err != nil {
return nil , fmt . Errorf ( "run resource: %w" , err )
}
} else {
if err := parsed . DryRun ( ctx ) ; err != nil {
logger := logging . FromContext ( ctx ) . With ( "path" , path , "name" , parsed . Obj . GetName ( ) , "ref" , ref )
logger . Warn ( "failed to dry run resource on create" , "error" , err )
// Do not fail here as it's purely informational
parsed . Errors = append ( parsed . Errors , err . Error ( ) )
}
}
return parsed , err
return parsed , nil
}
// UpdateResource updates a resource in the repository
@ -219,24 +207,9 @@ func (r *DualReadWriter) UpdateResource(ctx context.Context, path string, ref st
}
// TODO: improve parser to parse out of reader
parsed , err := r . parser . Parse ( ctx , info , true )
parsed , err := r . parser . Parse ( ctx , info )
if err != nil {
if errors . Is ( err , ErrUnableToReadResourceBytes ) {
return nil , apierrors . NewBadRequest ( "unable to read the request as a resource" )
}
return nil , err
}
// GVR will exist for anything we can actually save
// TODO: Add known error in parser for unsupported resource
if parsed . GVR == nil {
return nil , apierrors . NewBadRequest ( "The payload does not map to a known resource" )
}
// Do not write if any errors exist
if len ( parsed . Errors ) > 0 {
return parsed , err
return nil , fmt . Errorf ( "parse file: %w" , err )
}
data , err = parsed . ToSaveBytes ( )
@ -244,8 +217,7 @@ func (r *DualReadWriter) UpdateResource(ctx context.Context, path string, ref st
return nil , err
}
err = r . repo . Update ( ctx , path , ref , data , message )
if err != nil {
if err = r . repo . Update ( ctx , path , ref , data , message ) ; err != nil {
return nil , fmt . Errorf ( "update resource in repository: %w" , err )
}
@ -254,33 +226,21 @@ func (r *DualReadWriter) UpdateResource(ctx context.Context, path string, ref st
// FIXME: to make sure if behaves in the same way as in sync, we should
// we should refactor the code to use the same function.
if ref == "" {
if err := r . writeParsed ( ctx , path , parsed ) ; err != nil {
parsed . Errors = append ( parsed . Errors , err )
if _ , err := r . folders . EnsureFolderPathExist ( ctx , path ) ; err != nil {
return nil , fmt . Errorf ( "ensure folder path exists: %w" , err )
}
}
return parsed , err
}
// writeParsed write parsed resource to the repository and grafana database
func ( r * DualReadWriter ) writeParsed ( ctx context . Context , path string , parsed * ParsedResource ) error {
if _ , err := r . folders . EnsureFolderPathExist ( ctx , path ) ; err != nil {
return fmt . Errorf ( "ensure folder path exists: %w" , err )
}
// FIXME: I don't like this parsed strategy here
var err error
if parsed . Existing == nil {
parsed . Upsert , err = parsed . Client . Create ( ctx , parsed . Obj , metav1 . CreateOptions { } )
if err != nil {
return fmt . Errorf ( "create resource: %w" , err )
if err := parsed . Run ( ctx ) ; err != nil {
return nil , fmt . Errorf ( "run resource: %w" , err )
}
} else {
parsed . Upsert , err = parsed . Client . Update ( ctx , parsed . Obj , metav1 . UpdateOptions { } )
if err != nil {
return fmt . Errorf ( "update resource: %w" , err )
if err := parsed . DryRun ( ctx ) ; err != nil {
// Do not fail here as it's purely informational
logger := logging . FromContext ( ctx ) . With ( "path" , path , "name" , parsed . Obj . GetName ( ) , "ref" , ref )
logger . Warn ( "failed to dry run resource on update" , "error" , err )
parsed . Errors = append ( parsed . Errors , err . Error ( ) )
}
}
return nil
return parsed , nil
}