Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion pkg/devspace/services/sync/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (c *controller) startWithWait(ctx devspacecontext.Context, options *Options
// should wait for initial sync?
if options.SyncConfig.WaitInitialSync == nil || *options.SyncConfig.WaitInitialSync {
ctx.Log().Info("Waiting for initial sync to complete")
defer ctx.Log().Info("Initial sync completed")
defer ctx.Log().Info(InitialSyncCompletedMessage(options.SyncConfig.Path))
var (
uploadDone = false
downloadDone = false
Expand Down Expand Up @@ -320,6 +320,18 @@ func ParseSyncPath(path string) (localPath string, remotePath string, err error)
return splitted[0], splitted[1], nil
}

// InitialSyncCompletedMessage returns the message logged when initial sync completes
func InitialSyncCompletedMessage(path string) string {
localPath, remotePath, err := ParseSyncPath(path)
if err == nil {
return fmt.Sprintf("Initial sync completed for %s <-> %s", localPath, remotePath)
}
if path != "" {
return fmt.Sprintf("Initial sync completed for %s", path)
}
return "Initial sync completed"
}

func (c *controller) initClient(ctx devspacecontext.Context, pod *v1.Pod, arch, container string, syncConfig *latest.SyncConfig, starter sync.DelayedContainerStarter, verbose bool, customLog logpkg.Logger) (*sync.Sync, error) {
localPath, containerPath, err := ParseSyncPath(syncConfig.Path)
if err != nil {
Expand All @@ -345,6 +357,7 @@ func (c *controller) initClient(ctx devspacecontext.Context, pod *v1.Pod, arch,
Log: customLog,
Polling: syncConfig.Polling,
Starter: starter,
RemotePath: containerPath,
ResolveCommand: func(command string, args []string) (string, []string, error) {
return hook.ResolveCommand(ctx.Context(), command, args, ctx.WorkingDir(), ctx.Config(), ctx.Dependencies())
},
Expand Down
34 changes: 34 additions & 0 deletions pkg/devspace/services/sync/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,37 @@ func TestParseSyncPath(t *testing.T) {
assert.Equal(t, remote, testCase.expectedRemote, "Expect remote path in "+testCase.name)
}
}

func TestInitialSyncCompletedMessage(t *testing.T) {
testCases := []struct {
name string
in string
expected string
}{
{
name: "empty path defaults to dot",
in: "",
expected: "Initial sync completed for . <-> .",
},
{
name: "relative directory pair",
in: "../foo:/src/projects/foo",
expected: "Initial sync completed for ../foo <-> /src/projects/foo",
},
{
name: "single path matching issue 3309",
in: "/projects/qux",
expected: "Initial sync completed for /projects/qux <-> /projects/qux",
},
{
name: "windows path with colon",
in: "C:/codeproject:/home/dev/codeproject",
expected: "Initial sync completed for C:/codeproject <-> /home/dev/codeproject",
},
}

for _, tc := range testCases {
msg := InitialSyncCompletedMessage(tc.in)
assert.Equal(t, msg, tc.expected, tc.name)
}
}
29 changes: 27 additions & 2 deletions pkg/devspace/sync/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sync

import (
"context"
"fmt"
"io"
"os"
"path/filepath"
Expand Down Expand Up @@ -45,6 +46,7 @@ type Options struct {
UpstreamLimit int64
DownstreamLimit int64
Verbose bool
RemotePath string

UpstreamDisabled bool
DownstreamDisabled bool
Expand Down Expand Up @@ -135,6 +137,29 @@ func NewSync(ctx context.Context, localPath string, options Options) (*Sync, err
return s, nil
}

// TargetPath returns the target path context for sync logging
func (s *Sync) TargetPath() string {
if s.LocalPath != "" && s.Options.RemotePath != "" {
return fmt.Sprintf("%s <-> %s", s.LocalPath, s.Options.RemotePath)
}
if s.LocalPath != "" {
return s.LocalPath
}
if s.Options.RemotePath != "" {
return s.Options.RemotePath
}
return ""
}

// InitialSyncCompletedMessage returns the message logged when initial sync completes
func (s *Sync) InitialSyncCompletedMessage(prefix string) string {
target := s.TargetPath()
if target != "" {
return fmt.Sprintf("%s - Initial sync completed for %s", prefix, target)
}
return fmt.Sprintf("%s - Initial sync completed", prefix)
}

// Error handles a sync error
func (s *Sync) Error(err error) {
s.log.Errorf("Sync Error on %s: %v", s.LocalPath, err)
Expand Down Expand Up @@ -316,7 +341,7 @@ func (s *Sync) initialSync(onInitUploadDone chan struct{}, onInitDownloadDone ch
if s.Options.InitialSync == latest.InitialSyncStrategyDisabled {
s.log.Info("Upstream - Initial sync disabled")
} else {
s.log.Info("Upstream - Initial sync completed")
s.log.Info(s.InitialSyncCompletedMessage("Upstream"))
}
close(onInitUploadDone)
}
Expand All @@ -326,7 +351,7 @@ func (s *Sync) initialSync(onInitUploadDone chan struct{}, onInitDownloadDone ch
if s.Options.InitialSync == latest.InitialSyncStrategyDisabled {
s.log.Info("Downstream - Initial sync disabled")
} else {
s.log.Info("Downstream - Initial sync completed")
s.log.Info(s.InitialSyncCompletedMessage("Downstream"))
}
close(onInitDownloadDone)
}
Expand Down
99 changes: 99 additions & 0 deletions pkg/devspace/sync/sync_completion_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package sync

import (
"bytes"
"strings"
"testing"

"github.com/loft-sh/devspace/pkg/util/log"
"github.com/sirupsen/logrus"
"gotest.tools/assert"
)

func TestInitialSyncCompletedMessage(t *testing.T) {
testCases := []struct {
name string
localPath string
remotePath string
prefix string
expectedTarget string
expectedLogMessage string
}{
{
name: "both local and remote path set",
localPath: "/workspace/project",
remotePath: "/app",
prefix: "Upstream",
expectedTarget: "/workspace/project <-> /app",
expectedLogMessage: "Upstream - Initial sync completed for /workspace/project <-> /app",
},
{
name: "downstream with both paths set",
localPath: "/workspace/project",
remotePath: "/app",
prefix: "Downstream",
expectedTarget: "/workspace/project <-> /app",
expectedLogMessage: "Downstream - Initial sync completed for /workspace/project <-> /app",
},
{
name: "only local path set",
localPath: "/workspace/project",
remotePath: "",
prefix: "Upstream",
expectedTarget: "/workspace/project",
expectedLogMessage: "Upstream - Initial sync completed for /workspace/project",
},
{
name: "only remote path set",
localPath: "",
remotePath: "/app",
prefix: "Upstream",
expectedTarget: "/app",
expectedLogMessage: "Upstream - Initial sync completed for /app",
},
{
name: "neither path set",
localPath: "",
remotePath: "",
prefix: "Upstream",
expectedTarget: "",
expectedLogMessage: "Upstream - Initial sync completed",
},
}

for _, tc := range testCases {
s := &Sync{
LocalPath: tc.localPath,
Options: Options{
RemotePath: tc.remotePath,
},
}

assert.Equal(t, s.TargetPath(), tc.expectedTarget, tc.name)
assert.Equal(t, s.InitialSyncCompletedMessage(tc.prefix), tc.expectedLogMessage, tc.name)
}
}

func TestInitialSyncLoggingWithTargetContext(t *testing.T) {
var buf bytes.Buffer
testLogger := log.NewStreamLogger(&buf, &buf, logrus.InfoLevel)

s := &Sync{
LocalPath: "/workspace/frontend",
Options: Options{
RemotePath: "/src/frontend",
Log: testLogger,
},
log: testLogger,
}

upstreamMsg := s.InitialSyncCompletedMessage("Upstream")
s.log.Info(upstreamMsg)

downstreamMsg := s.InitialSyncCompletedMessage("Downstream")
s.log.Info(downstreamMsg)

logOutput := buf.String()
assert.Assert(t, strings.Contains(logOutput, "Upstream - Initial sync completed for /workspace/frontend <-> /src/frontend"), "expected upstream completion log with target context")
assert.Assert(t, strings.Contains(logOutput, "Downstream - Initial sync completed for /workspace/frontend <-> /src/frontend"), "expected downstream completion log with target context")
}