mirror of
https://github.com/wavetermdev/waveterm
synced 2026-05-22 16:28:32 +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>
136 lines
4.3 KiB
Go
136 lines
4.3 KiB
Go
// Copyright 2025, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Description: This package is used to create a connection to AWS services.
|
|
package awsconn
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
"github.com/aws/aws-sdk-go-v2/config"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
|
"github.com/aws/smithy-go"
|
|
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
|
"github.com/wavetermdev/waveterm/pkg/wconfig"
|
|
"gopkg.in/ini.v1"
|
|
)
|
|
|
|
const (
|
|
ProfileConfigKey = "profile:config"
|
|
ProfileCredentialsKey = "profile:credentials"
|
|
ProfilePrefix = "aws:"
|
|
TempFilePattern = "waveterm-awsconfig-%s"
|
|
)
|
|
|
|
var connectionRe = regexp.MustCompile(`^(.*):\w+:\/\/.*$`)
|
|
|
|
var tempfiles map[string]string = make(map[string]string)
|
|
|
|
func GetConfig(ctx context.Context, profile string) (*aws.Config, error) {
|
|
optfns := []func(*config.LoadOptions) error{}
|
|
// If profile is empty, use default config
|
|
if profile != "" {
|
|
connMatch := connectionRe.FindStringSubmatch(profile)
|
|
if connMatch == nil {
|
|
return nil, fmt.Errorf("invalid connection string: %s)", profile)
|
|
}
|
|
profile = connMatch[1]
|
|
log.Printf("GetConfig: profile=%s", profile)
|
|
profiles, cerrs := wconfig.ReadWaveHomeConfigFile(wconfig.ProfilesFile)
|
|
if len(cerrs) > 0 {
|
|
return nil, fmt.Errorf("error reading config file: %v", cerrs[0])
|
|
}
|
|
if profiles[profile] != nil {
|
|
configfilepath, _ := getTempFileFromConfig(profiles, ProfileConfigKey, profile)
|
|
credentialsfilepath, _ := getTempFileFromConfig(profiles, ProfileCredentialsKey, profile)
|
|
if configfilepath != "" {
|
|
log.Printf("configfilepath: %s", configfilepath)
|
|
optfns = append(optfns, config.WithSharedConfigFiles([]string{configfilepath}))
|
|
tempfiles[profile+"_config"] = configfilepath
|
|
}
|
|
if credentialsfilepath != "" {
|
|
log.Printf("credentialsfilepath: %s", credentialsfilepath)
|
|
optfns = append(optfns, config.WithSharedCredentialsFiles([]string{credentialsfilepath}))
|
|
tempfiles[profile+"_credentials"] = credentialsfilepath
|
|
}
|
|
}
|
|
trimmedProfile := strings.TrimPrefix(profile, ProfilePrefix)
|
|
optfns = append(optfns, config.WithSharedConfigProfile(trimmedProfile))
|
|
}
|
|
cfg, err := config.LoadDefaultConfig(ctx, optfns...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error loading config: %v", err)
|
|
}
|
|
return &cfg, nil
|
|
}
|
|
|
|
func getTempFileFromConfig(config waveobj.MetaMapType, key string, profile string) (string, error) {
|
|
connectionconfig := config.GetMap(profile)
|
|
if connectionconfig[key] != "" {
|
|
awsConfig := connectionconfig.GetString(key, "")
|
|
if awsConfig != "" {
|
|
tempfile, err := os.CreateTemp("", fmt.Sprintf(TempFilePattern, profile))
|
|
if err != nil {
|
|
return "", fmt.Errorf("error creating temp file: %v", err)
|
|
}
|
|
_, err = tempfile.WriteString(awsConfig)
|
|
if err != nil {
|
|
return "", fmt.Errorf("error writing to temp file: %v", err)
|
|
}
|
|
return tempfile.Name(), nil
|
|
}
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
func ParseProfiles() map[string]struct{} {
|
|
profiles := make(map[string]struct{})
|
|
fname := config.DefaultSharedConfigFilename() // Get aws.config default shared configuration file name
|
|
f, err := ini.Load(fname) // Load ini file
|
|
if err != nil {
|
|
log.Printf("error reading aws config file: %v", err)
|
|
return nil
|
|
}
|
|
for _, v := range f.Sections() {
|
|
if len(v.Keys()) != 0 { // Get only the sections having Keys
|
|
parts := strings.Split(v.Name(), " ")
|
|
if len(parts) == 2 && parts[0] == "profile" { // skip default
|
|
profiles[ProfilePrefix+parts[1]] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
fname = config.DefaultSharedCredentialsFilename()
|
|
f, err = ini.Load(fname)
|
|
if err != nil {
|
|
log.Printf("error reading aws credentials file: %v", err)
|
|
if profiles == nil {
|
|
profiles = make(map[string]struct{})
|
|
}
|
|
return profiles
|
|
}
|
|
for _, v := range f.Sections() {
|
|
profiles[ProfilePrefix+v.Name()] = struct{}{}
|
|
}
|
|
return profiles
|
|
}
|
|
|
|
func ListBuckets(ctx context.Context, client *s3.Client) ([]types.Bucket, error) {
|
|
output, err := client.ListBuckets(ctx, &s3.ListBucketsInput{})
|
|
if err != nil {
|
|
var apiErr smithy.APIError
|
|
if errors.As(err, &apiErr) {
|
|
return nil, fmt.Errorf("error listing buckets: %v", apiErr)
|
|
}
|
|
return nil, fmt.Errorf("error listing buckets: %v", err)
|
|
}
|
|
return output.Buckets, nil
|
|
}
|