diff --git a/pkg/apis/provisioning/v0alpha1/types.go b/pkg/apis/provisioning/v0alpha1/types.go
index fb36bad3526..567a4daa9b6 100644
--- a/pkg/apis/provisioning/v0alpha1/types.go
+++ b/pkg/apis/provisioning/v0alpha1/types.go
@@ -81,11 +81,6 @@ type RepositorySpec struct {
// The value is a reference to the Kubernetes metadata name of the folder in the same namespace.
Folder string `json:"folder,omitempty"`
- // Should we prefer emitting YAML for this repository, e.g. upon export?
- // Editing existing dashboards will continue to emit the file format used in the repository. (TODO: implement this)
- // If you delete and then recreate a dashboard, it will switch to the preferred format.
- PreferYAML bool `json:"preferYaml,omitempty"`
-
// Edit options within the repository
Editing EditingOptions `json:"editing"`
diff --git a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go b/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go
index 5664d51be5d..c228c7cf95a 100644
--- a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go
+++ b/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go
@@ -880,13 +880,6 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositorySpec(ref common.ReferenceCa
Format: "",
},
},
- "preferYaml": {
- SchemaProps: spec.SchemaProps{
- Description: "Should we prefer emitting YAML for this repository, e.g. upon export? Editing existing dashboards will continue to emit the file format used in the repository. (TODO: implement this) If you delete and then recreate a dashboard, it will switch to the preferred format.",
- Type: []string{"boolean"},
- Format: "",
- },
- },
"editing": {
SchemaProps: spec.SchemaProps{
Description: "Edit options within the repository",
diff --git a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list
index aa74d72dfa4..85dcfb2f20b 100644
--- a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list
+++ b/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list
@@ -8,6 +8,5 @@ API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provis
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,TestResults,Errors
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,WebhookStatus,SubscribedEvents
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositorySpec,GitHub
-API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositorySpec,PreferYAML
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,SyncStatus,JobID
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,WebhookResponse,Message
diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositoryspec.go b/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositoryspec.go
index a1dd9ce6c07..bce22b15bcd 100644
--- a/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositoryspec.go
+++ b/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositoryspec.go
@@ -14,7 +14,6 @@ type RepositorySpecApplyConfiguration struct {
Title *string `json:"title,omitempty"`
Description *string `json:"description,omitempty"`
Folder *string `json:"folder,omitempty"`
- PreferYAML *bool `json:"preferYaml,omitempty"`
Editing *EditingOptionsApplyConfiguration `json:"editing,omitempty"`
Type *provisioningv0alpha1.RepositoryType `json:"type,omitempty"`
Linting *bool `json:"linting,omitempty"`
@@ -53,14 +52,6 @@ func (b *RepositorySpecApplyConfiguration) WithFolder(value string) *RepositoryS
return b
}
-// WithPreferYAML sets the PreferYAML field in the declarative configuration to the given value
-// and returns the receiver, so that objects can be built by chaining "With" function invocations.
-// If called multiple times, the PreferYAML field is set to the value of the last call.
-func (b *RepositorySpecApplyConfiguration) WithPreferYAML(value bool) *RepositorySpecApplyConfiguration {
- b.PreferYAML = &value
- return b
-}
-
// WithEditing sets the Editing field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Editing field is set to the value of the last call.
diff --git a/pkg/registry/apis/provisioning/jobs/exporter.go b/pkg/registry/apis/provisioning/jobs/exporter.go
index d3105d3244c..c8c5be179b2 100644
--- a/pkg/registry/apis/provisioning/jobs/exporter.go
+++ b/pkg/registry/apis/provisioning/jobs/exporter.go
@@ -2,7 +2,6 @@ package jobs
import (
"context"
- "encoding/json"
"errors"
"fmt"
"path/filepath"
@@ -111,11 +110,11 @@ func (r *exporter) Export(ctx context.Context,
}
delete(item.Object, "metadata")
- marshalledBody, baseFileName, err := r.marshalPreferredFormat(item.Object, name, r.repository)
+ marshalledBody, err := yaml.Marshal(item.Object)
if err != nil {
return nil, fmt.Errorf("failed to marshal dashboard %s: %w", name, err)
}
- fileName := filepath.Join(folders.DirPath(folder), baseFileName)
+ fileName := filepath.Join(folders.DirPath(folder), name+".yaml")
logger = logger.With("file", fileName)
var ref string
@@ -145,16 +144,6 @@ func (r *exporter) Export(ctx context.Context,
}, nil
}
-func (r *exporter) marshalPreferredFormat(obj any, name string, repo repository.Repository) (body []byte, fileName string, err error) {
- if repo.Config().Spec.PreferYAML {
- body, err = yaml.Marshal(obj)
- return body, name + ".yaml", err
- } else {
- body, err := json.MarshalIndent(obj, "", " ")
- return body, name + ".json", err
- }
-}
-
func (r *exporter) fetchRepoFolderTree(ctx context.Context) (*folderTree, error) {
iface := r.client.Resource(schema.GroupVersionResource{
Group: "folder.grafana.app",
diff --git a/pkg/registry/apis/provisioning/jobs/pullrequest.go b/pkg/registry/apis/provisioning/jobs/pullrequest.go
index a7e0e8babf0..d222b65e688 100644
--- a/pkg/registry/apis/provisioning/jobs/pullrequest.go
+++ b/pkg/registry/apis/provisioning/jobs/pullrequest.go
@@ -138,7 +138,7 @@ func (c *PullRequestCommenter) Process(ctx context.Context, job provisioning.Job
previews := make([]resourcePreview, 0, len(files))
for _, f := range files {
- if c.parser.ShouldIgnore(f.Path) {
+ if resources.ShouldIgnorePath(f.Path) {
continue
}
diff --git a/pkg/registry/apis/provisioning/jobs/syncer.go b/pkg/registry/apis/provisioning/jobs/syncer.go
index 7fe8c735950..52b8b655069 100644
--- a/pkg/registry/apis/provisioning/jobs/syncer.go
+++ b/pkg/registry/apis/provisioning/jobs/syncer.go
@@ -148,7 +148,7 @@ func (r *Syncer) replicateTree(ctx context.Context, ref string) error {
continue
}
- if r.parser.ShouldIgnore(entry.Path) {
+ if resources.ShouldIgnorePath(entry.Path) {
logger.Debug("ignoring file")
continue
}
@@ -303,7 +303,7 @@ func (r *Syncer) createFolderPath(ctx context.Context, filePath string) (string,
func (r *Syncer) replicateChanges(ctx context.Context, changes []repository.FileChange) error {
for _, change := range changes {
- if r.parser.ShouldIgnore(change.Path) {
+ if resources.ShouldIgnorePath(change.Path) {
continue
}
diff --git a/pkg/registry/apis/provisioning/resources/fileformat.go b/pkg/registry/apis/provisioning/resources/fileformat.go
index 649fd2492c3..3a68b4c5cf7 100644
--- a/pkg/registry/apis/provisioning/resources/fileformat.go
+++ b/pkg/registry/apis/provisioning/resources/fileformat.go
@@ -11,7 +11,6 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
- yamlutil "k8s.io/apimachinery/pkg/util/yaml"
"github.com/grafana/grafana-app-sdk/logging"
dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v1alpha1"
@@ -80,16 +79,16 @@ func ReadClassicResource(ctx context.Context, info *repository.FileInfo) (*unstr
return nil, nil, "", ErrUnableToReadResourceBytes
}
-func LoadYAMLOrJSON(input io.Reader) (*unstructured.Unstructured, *schema.GroupVersionKind, error) {
- decoder := yamlutil.NewYAMLOrJSONDecoder(input, 1024)
- var rawObj runtime.RawExtension
- err := decoder.Decode(&rawObj)
+// DecodeYAMLObject reads the input as YAML and outputs its Kubernetes resource, if it is one.
+// Note that all JSON is also valid YAML, so this can also be used for JSON data.
+func DecodeYAMLObject(input io.Reader) (*unstructured.Unstructured, *schema.GroupVersionKind, error) {
+ data, err := io.ReadAll(input)
if err != nil {
return nil, nil, err
}
obj, gvk, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).
- Decode(rawObj.Raw, nil, nil)
+ Decode(data, nil, nil)
if err != nil {
return nil, gvk, err
}
diff --git a/pkg/registry/apis/provisioning/resources/fileformat_test.go b/pkg/registry/apis/provisioning/resources/fileformat_test.go
index fc55754108b..2d95a62a057 100644
--- a/pkg/registry/apis/provisioning/resources/fileformat_test.go
+++ b/pkg/registry/apis/provisioning/resources/fileformat_test.go
@@ -19,7 +19,7 @@ import (
func TestUtils(t *testing.T) {
t.Run("load playlist json", func(t *testing.T) {
- obj, gvk, err := LoadYAMLOrJSON(bytes.NewReader([]byte(`{
+ obj, gvk, err := DecodeYAMLObject(bytes.NewReader([]byte(`{
"kind": "Playlist",
"apiVersion": "playlist.grafana.app/v0alpha1",
"metadata": {
@@ -40,7 +40,7 @@ func TestUtils(t *testing.T) {
})
t.Run("YAML Parsing", func(t *testing.T) {
- obj, gvk, err := LoadYAMLOrJSON(bytes.NewReader([]byte("kind: xyz\npi: 3.1415")))
+ obj, gvk, err := DecodeYAMLObject(bytes.NewReader([]byte("kind: xyz\npi: 3.1415")))
require.NoError(t, err)
require.NotNil(t, gvk)
require.Equal(t, "xyz", obj.Object["kind"])
@@ -52,7 +52,7 @@ func TestUtils(t *testing.T) {
})
t.Run("load playlist yaml", func(t *testing.T) {
- obj, gvk, err := LoadYAMLOrJSON(bytes.NewReader([]byte(`
+ obj, gvk, err := DecodeYAMLObject(bytes.NewReader([]byte(`
apiVersion: playlist.grafana.app/v0alpha1
kind: Playlist
metadata:
diff --git a/pkg/registry/apis/provisioning/resources/parser.go b/pkg/registry/apis/provisioning/resources/parser.go
index 1cd84134b95..62dd646a417 100644
--- a/pkg/registry/apis/provisioning/resources/parser.go
+++ b/pkg/registry/apis/provisioning/resources/parser.go
@@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"path"
- "path/filepath"
"gopkg.in/yaml.v3"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -118,26 +117,17 @@ func (r *Parser) Client() *DynamicClient {
return r.client
}
-func (r *Parser) ShouldIgnore(p string) bool {
- ext := filepath.Ext(p)
- if ext == ".yaml" || ext == ".json" {
- return false
- }
-
- return true
-}
-
func (r *Parser) Parse(ctx context.Context, info *repository.FileInfo, validate bool) (parsed *ParsedResource, err error) {
logger := logging.FromContext(ctx).With("path", info.Path, "validate", validate)
parsed = &ParsedResource{
Info: info,
}
- if r.ShouldIgnore(info.Path) {
+ if ShouldIgnorePath(info.Path) {
return parsed, ErrUnableToReadResourceBytes
}
- parsed.Obj, parsed.GVK, err = LoadYAMLOrJSON(bytes.NewBuffer(info.Data))
+ parsed.Obj, parsed.GVK, err = DecodeYAMLObject(bytes.NewBuffer(info.Data))
if err != nil {
logger.Debug("failed to find GVK of the input data", "error", err)
parsed.Obj, parsed.GVK, parsed.Classic, err = ReadClassicResource(ctx, info)
@@ -290,3 +280,14 @@ func (f *ParsedResource) AsResourceWrapper() *provisioning.ResourceWrapper {
}
return wrap
}
+
+// ShouldIgnorePath determines if the path given is worth looking at.
+// If this returns true, skip processing it.
+// JSON and YAML files are valid paths. Anything else isn't.
+func ShouldIgnorePath(p string) bool {
+ ext := path.Ext(p)
+ // .yaml is the official extension per the spec, but .yml is widespread, too. (even this repo uses the unofficial one a lot!)
+ return ext != ".yml" && ext != ".yaml" &&
+ // We only support YAML, but JSON is sometimes generated from other tools, and is a valid subset of YAML.
+ ext != ".json"
+}
diff --git a/pkg/tests/apis/provisioning/testdata/openapi.json b/pkg/tests/apis/provisioning/testdata/openapi.json
index 5c4efd7bb07..d3615dd551d 100644
--- a/pkg/tests/apis/provisioning/testdata/openapi.json
+++ b/pkg/tests/apis/provisioning/testdata/openapi.json
@@ -4040,10 +4040,6 @@
}
]
},
- "preferYaml": {
- "description": "Should we prefer emitting YAML for this repository, e.g. upon export? Editing existing dashboards will continue to emit the file format used in the repository. (TODO: implement this) If you delete and then recreate a dashboard, it will switch to the preferred format.",
- "type": "boolean"
- },
"s3": {
"description": "The repository in an S3 bucket. Mutually exclusive with local and github.",
"allOf": [
diff --git a/public/app/features/provisioning/ConfigForm.tsx b/public/app/features/provisioning/ConfigForm.tsx
index 4dbc4cba076..91b139b45a4 100644
--- a/public/app/features/provisioning/ConfigForm.tsx
+++ b/public/app/features/provisioning/ConfigForm.tsx
@@ -206,9 +206,6 @@ export function ConfigForm({ data }: ConfigFormProps) {