Files
grafana/pkg/registry/apis/provisioning/resources/fileformat.go
T
Mariell Hoversholm 46640cd882 POC/Provisioning: Resources: Move resource interactions to own package (#97107)
* POC/Provisioning: Resources: Move client to its own package

* POC/Provisioning: Resources: Move parsing to resources package

* POC/Provisioning: Test: Align disco response
2024-11-28 08:06:49 +01:00

100 lines
2.8 KiB
Go

package resources
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"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/pkg/apis/dashboard"
)
var ErrUnableToReadResourceBytes = errors.New("unable to read bytes as a resource")
// Tries to figure out what kind this data is.
//
// The context and logger are both only used for logging purposes. They do not control any logic.
func FallbackResourceLoader(ctx context.Context, logger *slog.Logger, data []byte) (*unstructured.Unstructured, *schema.GroupVersionKind, error) {
// Try parsing as JSON
if data[0] == '{' {
var value map[string]any
err := json.Unmarshal(data, &value)
if err != nil {
return nil, nil, err
}
// regular version headers exist
// TODO: do we intend on this checking Kind or kind? document reasoning.
if value["apiVersion"] != nil && value["Kind"] != nil {
logger.DebugContext(ctx, "found that the object had K8s definitions already",
"apiVersion", value["apiVersion"],
"kind", value["Kind"])
gv, err := schema.ParseGroupVersion(value["apiVersion"].(string))
if err != nil {
return nil, nil, fmt.Errorf("invalid apiVersion")
}
gvk := gv.WithKind(value["Kind"].(string))
return &unstructured.Unstructured{Object: value}, &gvk, nil
}
// If this is a dashboard, convert it
if value["panels"] != nil &&
value["schemaVersion"] != nil &&
value["tags"] != nil {
gvk := &schema.GroupVersionKind{
Group: dashboard.GROUP,
Version: "v0alpha1",
Kind: "Dashboard"}
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": gvk.GroupVersion().String(),
"kind": gvk.Kind,
"metadata": map[string]any{
"name": value["uid"],
},
"spec": value,
},
}, gvk, nil
}
} else {
logger.DebugContext(ctx, "failed to get")
}
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)
if err != nil {
return nil, nil, err
}
obj, gvk, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).
Decode(rawObj.Raw, nil, nil)
if err != nil {
return nil, gvk, err
}
// The decoder should put it directly into an unstructured object
val, ok := obj.(*unstructured.Unstructured)
if ok {
return val, gvk, err
}
unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
return nil, gvk, err
}
return &unstructured.Unstructured{Object: unstructuredMap}, gvk, err
}