Provisioning: Split progress on \r (#105268)

This commit is contained in:
Ryan McKinley
2025-05-13 10:59:08 +03:00
committed by GitHub
parent cf53100f1d
commit 8866f2cfc1
5 changed files with 122 additions and 18 deletions
@@ -4,12 +4,12 @@ import (
"context"
"errors"
"fmt"
"os"
"time"
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository"
gogit "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/go-git"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/resources"
)
@@ -57,9 +57,14 @@ func (r *ExportWorker) Process(ctx context.Context, repo repository.Repository,
return err
}
writer := gogit.Progress(func(line string) {
progress.SetMessage(ctx, line)
}, "finished")
cloneOptions := repository.CloneOptions{
Timeout: 10 * time.Minute,
PushOnWrites: false,
Progress: writer,
BeforeFn: func() error {
progress.SetMessage(ctx, "clone target")
// :( the branch is now baked into the repo
@@ -73,7 +78,7 @@ func (r *ExportWorker) Process(ctx context.Context, repo repository.Repository,
pushOptions := repository.PushOptions{
Timeout: 10 * time.Minute,
Progress: os.Stdout,
Progress: writer,
BeforeFn: func() error {
progress.SetMessage(ctx, "push changes")
return nil
@@ -4,18 +4,18 @@ import (
"context"
"errors"
"fmt"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
mock "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/resources"
"github.com/stretchr/testify/assert"
mock "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestExportWorker_IsSupported(t *testing.T) {
@@ -265,7 +265,7 @@ func TestExportWorker_ProcessCloneAndPushOptions(t *testing.T) {
mockCloneFn.On("Execute", mock.Anything, mockRepo, mock.MatchedBy(func(opts repository.CloneOptions) bool {
return opts.Timeout == 10*time.Minute && !opts.PushOnWrites && opts.BeforeFn != nil
}), mock.MatchedBy(func(opts repository.PushOptions) bool {
return opts.Timeout == 10*time.Minute && opts.Progress == os.Stdout && opts.BeforeFn != nil
return opts.Timeout == 10*time.Minute && opts.Progress != nil && opts.BeforeFn != nil
}), mock.Anything).Return(func(ctx context.Context, repo repository.Repository, cloneOpts repository.CloneOptions, pushOpts repository.PushOptions, fn func(repository.Repository, bool) error) error {
// Execute both BeforeFn functions to verify progress messages
assert.NoError(t, cloneOpts.BeforeFn())
@@ -1,17 +1,16 @@
package migrate
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"time"
"github.com/grafana/grafana-app-sdk/logging"
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository"
gogit "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/go-git"
)
type LegacyMigrator struct {
@@ -38,14 +37,9 @@ func NewLegacyMigrator(
func (m *LegacyMigrator) Migrate(ctx context.Context, rw repository.ReaderWriter, options provisioning.MigrateJobOptions, progress jobs.JobProgressRecorder) error {
namespace := rw.Config().Namespace
reader, writer := io.Pipe()
go func() {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
progress.SetMessage(ctx, scanner.Text())
}
}()
writer := gogit.Progress(func(line string) {
progress.SetMessage(ctx, line)
}, "finished")
cloneOptions := repository.CloneOptions{
PushOnWrites: options.History,
// TODO: make this configurable
@@ -0,0 +1,47 @@
package gogit
import (
"bufio"
"bytes"
"io"
)
func Progress(lines func(line string), final string) io.WriteCloser {
reader, writer := io.Pipe()
scanner := bufio.NewScanner(reader)
scanner.Split(scanLines)
go func() {
for scanner.Scan() {
line := scanner.Text()
if line != "" {
lines(line)
}
}
lines(final)
}()
return writer
}
// Copied from bufio.ScanLines and modifed to accept standalone \r as input
func scanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, '\r'); i >= 0 {
// We have a full newline-terminated line.
return i + 1, data[0:i], nil
}
// Support standalone newlines also
if i := bytes.IndexByte(data, '\n'); i >= 0 {
// We have a full newline-terminated line.
return i + 1, data[0:i], nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), data, nil
}
// Request more data.
return 0, nil, nil
}
@@ -0,0 +1,58 @@
package gogit
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestProgressParsing(t *testing.T) {
tests := []struct {
name string
input string
expect []string
}{
{
name: "no breaks",
input: "some text",
expect: []string{"some text"},
},
{
name: "with cr",
input: "hello\rworld",
expect: []string{"hello", "world"},
},
{
name: "with nl",
input: "hello\nworld",
expect: []string{"hello", "world"},
},
{
name: "with cr+nl",
input: "hello\r\nworld",
expect: []string{"hello", "world"},
},
}
for _, tt := range tests {
lastLine := "***LAST*LINE***"
t.Run(tt.name, func(t *testing.T) {
lines := []string{}
writer := Progress(func(line string) {
lines = append(lines, line)
}, lastLine)
_, _ = writer.Write([]byte(tt.input))
err := writer.Close()
require.NoError(t, err)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
assert.NotEmpty(c, lines)
assert.Equal(c, lastLine, lines[len(lines)-1])
// Compare the results
require.Equal(c, tt.expect, lines[0:len(lines)-1])
}, time.Millisecond*100, time.Microsecond*50)
})
}
}