POC/Provisioning: Return FileInfo from read (not just bytes) (#96813)

This commit is contained in:
Ryan McKinley authored and GitHub committed 2024-11-21 10:37:32 +03:00
1 parent cd1998e66a
commit 82b47c3e79
9 files changed
+104 -39

No files matched your search

+9 -3
View File
@@ -150,10 +150,16 @@ type ResourceWrapper struct {
Path string `json:"path,omitempty"`
// The commit hash (if exists)
Commit string `json:"commit,omitempty"`
Ref string `json:"ref,omitempty"`
// Resource from the upstream repository
Resource common.Unstructured `json:"resource"`
// The repo hash value
Hash string `json:"hash,omitempty"`
// The modified time in the remote file system
Timestamp *metav1.Time `json:"timestamp,omitempty"`
// The raw resource body
Value common.Unstructured `json:"value"`
}
// Dummy object to return for webhooks
@@ -197,7 +197,11 @@ func (in *RepositoryStatus) DeepCopy() *RepositoryStatus {
func (in *ResourceWrapper) DeepCopyInto(out *ResourceWrapper) {
*out = *in
out.TypeMeta = in.TypeMeta
in.Resource.DeepCopyInto(&out.Resource)
if in.Timestamp != nil {
in, out := &in.Timestamp, &out.Timestamp
*out = (*in).DeepCopy()
}
in.Value.DeepCopyInto(&out.Value)
return
}
@@ -395,25 +395,38 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceWrapper(ref common.ReferenceC
Format: "",
},
},
"commit": {
"ref": {
SchemaProps: spec.SchemaProps{
Description: "The commit hash (if exists)",
Type: []string{"string"},
Format: "",
},
},
"resource": {
"hash": {
SchemaProps: spec.SchemaProps{
Description: "Resource from the upstream repository",
Description: "The repo hash value",
Type: []string{"string"},
Format: "",
},
},
"timestamp": {
SchemaProps: spec.SchemaProps{
Description: "The modified time in the remote file system",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
},
},
"value": {
SchemaProps: spec.SchemaProps{
Description: "The raw resource body",
Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"),
},
},
},
Required: []string{"resource"},
Required: []string{"value"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"},
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"},
}
}
+8
View File
@@ -0,0 +1,8 @@
package provisioning
import "errors"
var (
ErrFileNotFound = errors.New("file not found")
ErrNamespaceMismatch = errors.New("the file namespace does not match target namespace")
)
+7 -6
View File
@@ -130,20 +130,21 @@ func (s *readConnector) getValidatedBody(_ context.Context, data []byte) (*unstr
}
func (s *readConnector) doRead(ctx context.Context, repo Repository, path string, commit string) (*provisioning.ResourceWrapper, error) {
data, err := repo.Read(ctx, path, commit)
info, err := repo.Read(ctx, path, commit)
if err != nil {
return nil, err
}
obj, _, err := s.getValidatedBody(ctx, data)
obj, _, err := s.getValidatedBody(ctx, info.Data)
if err != nil {
return nil, err
}
return &provisioning.ResourceWrapper{
Path: path,
Commit: commit,
Resource: v0alpha1.Unstructured{
Path: info.Path,
Ref: info.Ref,
Hash: info.Hash,
Timestamp: info.Modified,
Value: v0alpha1.Unstructured{
Object: obj.Object,
},
}, nil
+14 -11
View File
@@ -91,9 +91,8 @@ func (r *githubRepository) Test(ctx context.Context) error {
}
// ReadResource implements provisioning.Repository.
func (r *githubRepository) Read(ctx context.Context, filePath string, commit string) ([]byte, error) {
ref := commit
if commit == "" {
func (r *githubRepository) Read(ctx context.Context, filePath string, ref string) (*FileInfo, error) {
if ref == "" {
ref = r.config.Spec.GitHub.Branch
}
@@ -118,8 +117,12 @@ func (r *githubRepository) Read(ctx context.Context, filePath string, commit str
if err != nil {
return nil, fmt.Errorf("get content: %w", err)
}
return []byte(data), nil
return &FileInfo{
Path: filePath,
Ref: ref,
Data: []byte(data),
Hash: content.GetSHA(),
}, nil
}
func (r *githubRepository) Create(ctx context.Context, path string, data []byte, comment string) error {
@@ -255,30 +258,30 @@ func (r *githubRepository) onPushEvent(ctx context.Context, event *github.PushEv
r.logger.Info("process commit", "commit", commit.GetID(), "message", commit.GetMessage())
for _, file := range commit.Added {
resource, err := r.Read(ctx, file, commit.GetID())
info, err := r.Read(ctx, file, commit.GetID())
if err != nil {
return fmt.Errorf("read added resource: %w", err)
}
r.logger.Info("added file", "file", file, "resource", string(resource), "commit", commit.GetID())
r.logger.Info("added file", "file", file, "resource", string(info.Data), "commit", commit.GetID())
}
for _, file := range commit.Modified {
resource, err := r.Read(ctx, file, commit.GetID())
info, err := r.Read(ctx, file, commit.GetID())
if err != nil {
return fmt.Errorf("read modified resource: %w", err)
}
r.logger.Info("modified file", "file", file, "resource", string(resource), "commit", commit.GetID())
r.logger.Info("modified file", "file", file, "resource", string(info.Data), "commit", commit.GetID())
}
for _, file := range commit.Removed {
resource, err := r.Read(ctx, file, beforeRef)
info, err := r.Read(ctx, file, beforeRef)
if err != nil {
return fmt.Errorf("read removed resource: %w", err)
}
r.logger.Info("removed file", "file", file, "resource", string(resource), "commit", commit.GetID())
r.logger.Info("removed file", "file", file, "resource", string(info.Data), "commit", commit.GetID())
}
beforeRef = commit.GetID()
+27 -10
View File
@@ -10,7 +10,7 @@ import (
"strings"
apierrors "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/registry/rest"
@@ -108,7 +108,7 @@ func (r *localRepository) Validate() (fields field.ErrorList) {
// Test implements provisioning.Repository.
func (r *localRepository) Test(ctx context.Context) error {
return &apierrors.StatusError{
ErrStatus: v1.Status{
ErrStatus: metav1.Status{
Message: "test is not yet implemented",
Code: http.StatusNotImplemented,
},
@@ -116,27 +116,44 @@ func (r *localRepository) Test(ctx context.Context) error {
}
// ReadResource implements provisioning.Repository.
func (r *localRepository) Read(ctx context.Context, path string, commit string) ([]byte, error) {
if commit != "" {
return nil, apierrors.NewBadRequest("local repository does not support commits")
func (r *localRepository) Read(ctx context.Context, path string, ref string) (*FileInfo, error) {
if ref != "" {
return nil, apierrors.NewBadRequest("local repository does not support ref")
}
if r.path == "" {
return nil, &apierrors.StatusError{
ErrStatus: v1.Status{
ErrStatus: metav1.Status{
Message: "the service is missing a root path",
Code: http.StatusFailedDependency,
},
}
}
fullpath := filepath.Join(r.path, path)
info, err := os.Stat(fullpath)
if errors.Is(err, os.ErrNotExist) {
return nil, ErrFileNotFound
}
//nolint:gosec
return os.ReadFile(filepath.Join(r.path, path))
data, err := os.ReadFile(fullpath)
if err != nil {
return nil, err
}
return &FileInfo{
Path: path,
Data: data,
Modified: &metav1.Time{
Time: info.ModTime(),
},
}, nil
}
func (r *localRepository) Create(ctx context.Context, path string, data []byte, comment string) error {
if r.path == "" {
return &apierrors.StatusError{
ErrStatus: v1.Status{
ErrStatus: metav1.Status{
Message: "the service is missing a root path",
Code: http.StatusFailedDependency,
},
@@ -153,7 +170,7 @@ func (r *localRepository) Create(ctx context.Context, path string, data []byte,
func (r *localRepository) Update(ctx context.Context, path string, data []byte, comment string) error {
if r.path == "" {
return &apierrors.StatusError{
ErrStatus: v1.Status{
ErrStatus: metav1.Status{
Message: "the service is missing a root path",
Code: http.StatusFailedDependency,
},
@@ -170,7 +187,7 @@ func (r *localRepository) Update(ctx context.Context, path string, data []byte,
func (r *localRepository) Delete(ctx context.Context, path string, comment string) error {
if r.path == "" {
return &apierrors.StatusError{
ErrStatus: v1.Status{
ErrStatus: metav1.Status{
Message: "the service is missing a root path",
Code: http.StatusFailedDependency,
},
+1 -1
View File
@@ -51,7 +51,7 @@ func (r *s3Repository) Test(ctx context.Context) error {
}
// ReadResource implements provisioning.Repository.
func (r *s3Repository) Read(ctx context.Context, path string, commit string) ([]byte, error) {
func (r *s3Repository) Read(ctx context.Context, path string, ref string) (*FileInfo, error) {
return nil, &errors.StatusError{
ErrStatus: metav1.Status{
Message: "read resource is not yet implemented",
+15 -2
View File
@@ -29,6 +29,19 @@ func (f UndoFunc) Chain(ctx context.Context, next UndoFunc) UndoFunc {
}
}
type FileInfo struct {
// Path to the file on disk
Path string
// The raw bytes
Data []byte
// The git branch or reference commit
Ref string
// The git hash for a given file
Hash string
// When was the file changed (if known)
Modified *metav1.Time
}
type Repository interface {
// The saved object
Config() *provisioning.Repository
@@ -43,7 +56,7 @@ type Repository interface {
// Read a file from the resource
// This data will be parsed and validated before it is shown to end users
Read(ctx context.Context, path string, commit string) ([]byte, error)
Read(ctx context.Context, path string, ref string) (*FileInfo, error)
// Write a file to the repository.
// The data has already been validated and is ready for save
@@ -98,7 +111,7 @@ func (r *unknownRepository) Test(ctx context.Context) error {
}
// ReadResource implements provisioning.Repository.
func (r *unknownRepository) Read(ctx context.Context, path string, commit string) ([]byte, error) {
func (r *unknownRepository) Read(ctx context.Context, path string, ref string) (*FileInfo, error) {
return nil, &errors.StatusError{
ErrStatus: metav1.Status{
Message: "read resource is not yet implemented",