mirror of
https://github.com/wavetermdev/waveterm
synced 2026-05-21 15:58:30 +00:00
This adds the ability to stream `tar` archives over channels between `wsh` instances. The main use cases for this are remote copy and move operations. It also completes the `wavefs` implementation of the FileShare interface to allow copy/move interoperability between wavefiles and other storage types. The tar streaming functionality has been broken out into the new `tarcopy` package for easy reuse. New `fileshare` functions are added for `CopyInternal`, which allows copying files internal to a filesystem to bypass the expensive interop layer, and `MoveInternal`, which does the same for moving a file within a filesystem. Copying between remotes is now handled by `CopyRemote`, which accepts the source `FileShareClient` as a parameter. `wsh` connections use the same implementation for `CopyInternal` and `CopyRemote` as they need to request the channel on the remote destination, since we don't offer a way to pass channels as a parameter to a remote call. This also adds a recursive `-r` flag to `wsh file rm` to allow for deleting a directory and all its contents. S3 support will be addressed in a future PR. --------- Co-authored-by: sawka <mike@commandline.dev> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
169 lines
6.2 KiB
Go
169 lines
6.2 KiB
Go
package fileshare
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/wavetermdev/waveterm/pkg/remote/connparse"
|
|
"github.com/wavetermdev/waveterm/pkg/remote/fileshare/fstype"
|
|
"github.com/wavetermdev/waveterm/pkg/remote/fileshare/wavefs"
|
|
"github.com/wavetermdev/waveterm/pkg/remote/fileshare/wshfs"
|
|
"github.com/wavetermdev/waveterm/pkg/util/iochan/iochantypes"
|
|
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
|
"github.com/wavetermdev/waveterm/pkg/wshutil"
|
|
)
|
|
|
|
const (
|
|
ErrorParsingConnection = "error creating fileshare client, could not parse connection %s"
|
|
)
|
|
|
|
// CreateFileShareClient creates a fileshare client based on the connection string
|
|
// Returns the client and the parsed connection
|
|
func CreateFileShareClient(ctx context.Context, connection string) (fstype.FileShareClient, *connparse.Connection) {
|
|
conn, err := connparse.ParseURIAndReplaceCurrentHost(ctx, connection)
|
|
if err != nil {
|
|
log.Printf("error parsing connection: %v", err)
|
|
return nil, nil
|
|
}
|
|
conntype := conn.GetType()
|
|
if conntype == connparse.ConnectionTypeS3 {
|
|
// config, err := awsconn.GetConfig(ctx, connection)
|
|
// if err != nil {
|
|
// log.Printf("error getting aws config: %v", err)
|
|
// return nil, nil
|
|
// }
|
|
return nil, nil
|
|
} else if conntype == connparse.ConnectionTypeWave {
|
|
return wavefs.NewWaveClient(), conn
|
|
} else if conntype == connparse.ConnectionTypeWsh {
|
|
return wshfs.NewWshClient(), conn
|
|
} else {
|
|
log.Printf("unsupported connection type: %s", conntype)
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
func Read(ctx context.Context, data wshrpc.FileData) (*wshrpc.FileData, error) {
|
|
client, conn := CreateFileShareClient(ctx, data.Info.Path)
|
|
if conn == nil || client == nil {
|
|
return nil, fmt.Errorf(ErrorParsingConnection, data.Info.Path)
|
|
}
|
|
return client.Read(ctx, conn, data)
|
|
}
|
|
|
|
func ReadStream(ctx context.Context, data wshrpc.FileData) <-chan wshrpc.RespOrErrorUnion[wshrpc.FileData] {
|
|
client, conn := CreateFileShareClient(ctx, data.Info.Path)
|
|
if conn == nil || client == nil {
|
|
return wshutil.SendErrCh[wshrpc.FileData](fmt.Errorf(ErrorParsingConnection, data.Info.Path))
|
|
}
|
|
return client.ReadStream(ctx, conn, data)
|
|
}
|
|
|
|
func ReadTarStream(ctx context.Context, data wshrpc.CommandRemoteStreamTarData) <-chan wshrpc.RespOrErrorUnion[iochantypes.Packet] {
|
|
client, conn := CreateFileShareClient(ctx, data.Path)
|
|
if conn == nil || client == nil {
|
|
return wshutil.SendErrCh[iochantypes.Packet](fmt.Errorf(ErrorParsingConnection, data.Path))
|
|
}
|
|
return client.ReadTarStream(ctx, conn, data.Opts)
|
|
}
|
|
|
|
func ListEntries(ctx context.Context, path string, opts *wshrpc.FileListOpts) ([]*wshrpc.FileInfo, error) {
|
|
client, conn := CreateFileShareClient(ctx, path)
|
|
if conn == nil || client == nil {
|
|
return nil, fmt.Errorf(ErrorParsingConnection, path)
|
|
}
|
|
return client.ListEntries(ctx, conn, opts)
|
|
}
|
|
|
|
func ListEntriesStream(ctx context.Context, path string, opts *wshrpc.FileListOpts) <-chan wshrpc.RespOrErrorUnion[wshrpc.CommandRemoteListEntriesRtnData] {
|
|
client, conn := CreateFileShareClient(ctx, path)
|
|
if conn == nil || client == nil {
|
|
return wshutil.SendErrCh[wshrpc.CommandRemoteListEntriesRtnData](fmt.Errorf(ErrorParsingConnection, path))
|
|
}
|
|
return client.ListEntriesStream(ctx, conn, opts)
|
|
}
|
|
|
|
func Stat(ctx context.Context, path string) (*wshrpc.FileInfo, error) {
|
|
client, conn := CreateFileShareClient(ctx, path)
|
|
if conn == nil || client == nil {
|
|
return nil, fmt.Errorf(ErrorParsingConnection, path)
|
|
}
|
|
return client.Stat(ctx, conn)
|
|
}
|
|
|
|
func PutFile(ctx context.Context, data wshrpc.FileData) error {
|
|
client, conn := CreateFileShareClient(ctx, data.Info.Path)
|
|
if conn == nil || client == nil {
|
|
return fmt.Errorf(ErrorParsingConnection, data.Info.Path)
|
|
}
|
|
return client.PutFile(ctx, conn, data)
|
|
}
|
|
|
|
func Mkdir(ctx context.Context, path string) error {
|
|
client, conn := CreateFileShareClient(ctx, path)
|
|
if conn == nil || client == nil {
|
|
return fmt.Errorf(ErrorParsingConnection, path)
|
|
}
|
|
return client.Mkdir(ctx, conn)
|
|
}
|
|
|
|
func Move(ctx context.Context, data wshrpc.CommandFileCopyData) error {
|
|
srcClient, srcConn := CreateFileShareClient(ctx, data.SrcUri)
|
|
if srcConn == nil || srcClient == nil {
|
|
return fmt.Errorf("error creating fileshare client, could not parse source connection %s", data.SrcUri)
|
|
}
|
|
destClient, destConn := CreateFileShareClient(ctx, data.DestUri)
|
|
if destConn == nil || destClient == nil {
|
|
return fmt.Errorf("error creating fileshare client, could not parse destination connection %s", data.DestUri)
|
|
}
|
|
if srcConn.Host != destConn.Host {
|
|
err := destClient.CopyRemote(ctx, srcConn, destConn, srcClient, data.Opts)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot copy %q to %q: %w", data.SrcUri, data.DestUri, err)
|
|
}
|
|
return srcClient.Delete(ctx, srcConn, data.Opts.Recursive)
|
|
} else {
|
|
return srcClient.MoveInternal(ctx, srcConn, destConn, data.Opts)
|
|
}
|
|
}
|
|
|
|
func Copy(ctx context.Context, data wshrpc.CommandFileCopyData) error {
|
|
srcClient, srcConn := CreateFileShareClient(ctx, data.SrcUri)
|
|
if srcConn == nil || srcClient == nil {
|
|
return fmt.Errorf("error creating fileshare client, could not parse source connection %s", data.SrcUri)
|
|
}
|
|
destClient, destConn := CreateFileShareClient(ctx, data.DestUri)
|
|
if destConn == nil || destClient == nil {
|
|
return fmt.Errorf("error creating fileshare client, could not parse destination connection %s", data.DestUri)
|
|
}
|
|
if srcConn.Host != destConn.Host {
|
|
return destClient.CopyRemote(ctx, srcConn, destConn, srcClient, data.Opts)
|
|
} else {
|
|
return srcClient.CopyInternal(ctx, srcConn, destConn, data.Opts)
|
|
}
|
|
}
|
|
|
|
func Delete(ctx context.Context, data wshrpc.CommandDeleteFileData) error {
|
|
client, conn := CreateFileShareClient(ctx, data.Path)
|
|
if conn == nil || client == nil {
|
|
return fmt.Errorf(ErrorParsingConnection, data.Path)
|
|
}
|
|
return client.Delete(ctx, conn, data.Recursive)
|
|
}
|
|
|
|
func Join(ctx context.Context, path string, parts ...string) (string, error) {
|
|
client, conn := CreateFileShareClient(ctx, path)
|
|
if conn == nil || client == nil {
|
|
return "", fmt.Errorf(ErrorParsingConnection, path)
|
|
}
|
|
return client.Join(ctx, conn, parts...)
|
|
}
|
|
|
|
func Append(ctx context.Context, data wshrpc.FileData) error {
|
|
client, conn := CreateFileShareClient(ctx, data.Info.Path)
|
|
if conn == nil || client == nil {
|
|
return fmt.Errorf(ErrorParsingConnection, data.Info.Path)
|
|
}
|
|
return client.AppendFile(ctx, conn, data)
|
|
}
|