1 parent
a07c5b0392
commit
5d4de8d8f3
4 files changed
+49
-32
No files matched your search
@@ -6,6 +6,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFileNotFound = fs.ErrNotExist
|
||||
ErrNamespaceMismatch = errors.New("the file namespace does not match target namespace")
|
||||
ErrFileNotFound = fs.ErrNotExist
|
||||
ErrNamespaceMismatch = errors.New("the file namespace does not match target namespace")
|
||||
ErrUnableToReadResourceBytes = errors.New("unable to read bytes as a resource")
|
||||
)
|
||||
@@ -2,13 +2,15 @@ package provisioning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
@@ -61,18 +63,18 @@ func (s *filesConnector) Connect(ctx context.Context, name string, opts runtime.
|
||||
prefix := fmt.Sprintf("/%s/files/", name)
|
||||
idx := strings.Index(r.URL.Path, prefix)
|
||||
if idx == -1 {
|
||||
responder.Error(errors.NewBadRequest("invalid request path"))
|
||||
responder.Error(apierrors.NewBadRequest("invalid request path"))
|
||||
return
|
||||
}
|
||||
|
||||
filePath := r.URL.Path[idx+len(prefix):]
|
||||
if filePath == "" {
|
||||
responder.Error(errors.NewBadRequest("missing path"))
|
||||
responder.Error(apierrors.NewBadRequest("missing path"))
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(filePath, "..") {
|
||||
responder.Error(errors.NewBadRequest("invalid path navigation"))
|
||||
responder.Error(apierrors.NewBadRequest("invalid path navigation"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,18 +82,18 @@ func (s *filesConnector) Connect(ctx context.Context, name string, opts runtime.
|
||||
case ".json", ".yaml", ".yml":
|
||||
// ok
|
||||
default:
|
||||
responder.Error(errors.NewBadRequest("only yaml and json files supported"))
|
||||
responder.Error(apierrors.NewBadRequest("only yaml and json files supported"))
|
||||
return
|
||||
}
|
||||
|
||||
var obj runtime.Object
|
||||
var obj *provisioning.ResourceWrapper
|
||||
ref := r.URL.Query().Get("ref")
|
||||
message := r.URL.Query().Get("message")
|
||||
|
||||
code := http.StatusOK
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
obj, err, code = s.doRead(r.Context(), repo, filePath, ref)
|
||||
code, obj, err = s.doRead(r.Context(), repo, filePath, ref)
|
||||
case http.MethodPost:
|
||||
obj, err = s.doWrite(r.Context(), false, repo, filePath, ref, message, r)
|
||||
case http.MethodPut:
|
||||
@@ -99,7 +101,7 @@ func (s *filesConnector) Connect(ctx context.Context, name string, opts runtime.
|
||||
case http.MethodDelete:
|
||||
obj, err = s.doDelete(r.Context(), repo, filePath, ref, message)
|
||||
default:
|
||||
err = errors.NewMethodNotSupported(provisioning.RepositoryResourceInfo.GroupResource(), r.Method)
|
||||
err = apierrors.NewMethodNotSupported(provisioning.RepositoryResourceInfo.GroupResource(), r.Method)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -127,35 +129,35 @@ func (s *filesConnector) getParser(repo Repository) (*fileParser, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *filesConnector) doRead(ctx context.Context, repo Repository, path string, ref string) (*provisioning.ResourceWrapper, error, int) {
|
||||
func (s *filesConnector) doRead(ctx context.Context, repo Repository, path string, ref string) (int, *provisioning.ResourceWrapper, error) {
|
||||
info, err := repo.Read(ctx, path, ref)
|
||||
if err != nil {
|
||||
return nil, err, 0
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
parser, err := s.getParser(repo)
|
||||
if err != nil {
|
||||
return nil, err, 0
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
parsed, err := parser.parse(ctx, info, true)
|
||||
if err != nil {
|
||||
return nil, err, 0
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
code := http.StatusOK
|
||||
if len(parsed.errors) > 0 {
|
||||
code = http.StatusNotAcceptable
|
||||
}
|
||||
return parsed.AsResourceWrapper(), nil, code
|
||||
return code, parsed.AsResourceWrapper(), nil
|
||||
}
|
||||
|
||||
func (s *filesConnector) doWrite(ctx context.Context, update bool, repo Repository, path string, ref string, message string, req *http.Request) (runtime.Object, error) {
|
||||
func (s *filesConnector) doWrite(ctx context.Context, update bool, repo Repository, path string, ref string, message string, req *http.Request) (*provisioning.ResourceWrapper, error) {
|
||||
settings := repo.Config().Spec.Editing
|
||||
if update && !settings.Update {
|
||||
return nil, errors.NewForbidden(provisioning.RepositoryResourceInfo.GroupResource(), "updating files not enabled", nil)
|
||||
return nil, apierrors.NewForbidden(provisioning.RepositoryResourceInfo.GroupResource(), "updating files not enabled", nil)
|
||||
} else if !settings.Create {
|
||||
return nil, errors.NewForbidden(provisioning.RepositoryResourceInfo.GroupResource(), "creating files not enabled", nil)
|
||||
return nil, apierrors.NewForbidden(provisioning.RepositoryResourceInfo.GroupResource(), "creating files not enabled", nil)
|
||||
}
|
||||
|
||||
defer func() { _ = req.Body.Close() }()
|
||||
@@ -177,6 +179,9 @@ func (s *filesConnector) doWrite(ctx context.Context, update bool, repo Reposito
|
||||
|
||||
parsed, err := parser.parse(ctx, info, true)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrUnableToReadResourceBytes) {
|
||||
return nil, apierrors.NewBadRequest("unable to read the request as a resource")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -194,15 +199,31 @@ func (s *filesConnector) doWrite(ctx context.Context, update bool, repo Reposito
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("TODO! write the file into real storage %s\n", path)
|
||||
// Which context? request of background???
|
||||
// Behaves the same running sync after writing
|
||||
if parsed.existing == nil {
|
||||
obj, err := parsed.client.Create(ctx, parsed.obj, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
parsed.errors = append(parsed.errors, err)
|
||||
} else {
|
||||
parsed.obj = obj
|
||||
}
|
||||
} else {
|
||||
obj, err := parsed.client.Update(ctx, parsed.obj, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
parsed.errors = append(parsed.errors, err)
|
||||
} else {
|
||||
parsed.obj = obj
|
||||
}
|
||||
}
|
||||
|
||||
return parsed.obj, err
|
||||
return parsed.AsResourceWrapper(), err
|
||||
}
|
||||
|
||||
func (s *filesConnector) doDelete(ctx context.Context, repo Repository, path string, ref string, message string) (runtime.Object, error) {
|
||||
func (s *filesConnector) doDelete(ctx context.Context, repo Repository, path string, ref string, message string) (*provisioning.ResourceWrapper, error) {
|
||||
settings := repo.Config().Spec.Editing
|
||||
if !settings.Delete {
|
||||
return nil, errors.NewForbidden(provisioning.RepositoryResourceInfo.GroupResource(), "deleting is not supported", nil)
|
||||
return nil, apierrors.NewForbidden(provisioning.RepositoryResourceInfo.GroupResource(), "deleting is not supported", nil)
|
||||
}
|
||||
|
||||
err := repo.Delete(ctx, path, message)
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
@@ -124,7 +124,8 @@ func (r *fileParser) parse(ctx context.Context, info *FileInfo, validate bool) (
|
||||
|
||||
func (f *parsedFile) ToSaveBytes() ([]byte, error) {
|
||||
// TODO... should use the validated one?
|
||||
obj := f.obj
|
||||
obj := f.obj.Object
|
||||
delete(obj, "metadata")
|
||||
|
||||
switch filepath.Ext(f.info.Path) {
|
||||
// JSON pretty print
|
||||
@@ -133,13 +134,7 @@ func (f *parsedFile) ToSaveBytes() ([]byte, error) {
|
||||
|
||||
// Write the value as yaml
|
||||
case ".yaml", ".yml":
|
||||
buff := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).
|
||||
Encode(obj, buff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buff.Bytes(), nil
|
||||
return yaml.Marshal(obj)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected format")
|
||||
|
||||
@@ -53,7 +53,7 @@ func FallbackResourceLoader(data []byte) (*unstructured.Unstructured, *schema.Gr
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil, fmt.Errorf("unable to convert")
|
||||
return nil, nil, ErrUnableToReadResourceBytes
|
||||
}
|
||||
|
||||
func LoadYAMLOrJSON(input io.Reader) (*unstructured.Unstructured, *schema.GroupVersionKind, error) {
|
||||
|
||||
Reference in new issue
Block a user