POC/Provisioning: Return FileInfo from read (not just bytes) (#96813)
This commit is contained in:
1 parent
cd1998e66a
commit
82b47c3e79
9 files changed
+104
-39
No files matched your search
@@ -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"},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user