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) { - - -
diff --git a/public/app/features/provisioning/api/endpoints.ts b/public/app/features/provisioning/api/endpoints.ts index b48e6a9b774..a6c77a6226b 100644 --- a/public/app/features/provisioning/api/endpoints.ts +++ b/public/app/features/provisioning/api/endpoints.ts @@ -498,7 +498,6 @@ export type RepositorySpec = { github?: GitHubRepositoryConfig; linting?: boolean; local?: LocalRepositoryConfig; - preferYaml?: boolean; s3?: S3RepositoryConfig; title: string; type: 'github' | 'local' | 's3'; diff --git a/public/app/features/provisioning/api/types.ts b/public/app/features/provisioning/api/types.ts index 256f5790c5c..3e268936518 100644 --- a/public/app/features/provisioning/api/types.ts +++ b/public/app/features/provisioning/api/types.ts @@ -30,7 +30,6 @@ export type RepositorySpec = { s3?: S3RepositoryConfig; type: 'github' | 'local' | 's3'; editing: EditingOptions; - preferYaml?: boolean; linting?: boolean; }; diff --git a/public/app/features/provisioning/utils/data.ts b/public/app/features/provisioning/utils/data.ts index cf96380f3bf..0aab79a9659 100644 --- a/public/app/features/provisioning/utils/data.ts +++ b/public/app/features/provisioning/utils/data.ts @@ -8,7 +8,6 @@ export const dataToSpec = (data: RepositoryFormData): RepositorySpec => { title: data.title || '', editing: data.editing, linting: data.linting, - preferYaml: data.preferYaml, }; switch (data.type) { case 'github':