From 8542090ff724fe7dac9d2a3280700b4bdf55d017 Mon Sep 17 00:00:00 2001 From: Denis O Date: Fri, 20 Dec 2024 21:50:35 +0000 Subject: [PATCH] Symbol link issues --- cli/commands/stack/action.go | 143 ++++++++++++++++++++++++++++++++--- 1 file changed, 133 insertions(+), 10 deletions(-) diff --git a/cli/commands/stack/action.go b/cli/commands/stack/action.go index 5692a5087..9f66d6689 100644 --- a/cli/commands/stack/action.go +++ b/cli/commands/stack/action.go @@ -3,11 +3,13 @@ package stack import ( "context" "fmt" + "io" + "net/url" "os" "path/filepath" "github.com/gruntwork-io/terragrunt/config" - getter "github.com/hashicorp/go-getter/v2" + getter "github.com/hashicorp/go-getter" "github.com/gruntwork-io/terragrunt/internal/errors" "github.com/gruntwork-io/terragrunt/options" @@ -53,17 +55,11 @@ func processStackFile(ctx context.Context, opts *options.TerragruntOptions, stac return errors.New(fmt.Errorf("failed to create base directory: %w", err)) } - //client := getter.Client{ - // Getters: getter.Getters, - // Decompressors: getter.Decompressors, - // DisableSymlinks: true, - //} - for _, unit := range stackFile.Units { destPath := filepath.Join(baseDir, unit.Path) dest, err := filepath.Abs(destPath) if err != nil { - return errors.New(fmt.Errorf("failed to get absolute path for destination '%s': %w", destPath, err)) + return errors.New(fmt.Errorf("failed to get absolute path for destination '%s': %w", dest, err)) } src := unit.Source @@ -72,10 +68,137 @@ func processStackFile(ctx context.Context, opts *options.TerragruntOptions, stac opts.Logger.Warnf("failed to get absolute path for source '%s': %v", unit.Source, err) src = unit.Source } + opts.Logger.Infof("Processing unit: %s (%s) to %s", unit.Name, src, dest) - if _, err := getter.GetAny(ctx, dest, src); err != nil { - return fmt.Errorf("failed to fetch source '%s' to destination '%s': %w", unit.Source, destPath, err) + client := &getter.Client{ + Src: src, + Dst: dest, + Mode: getter.ClientModeAny, + Dir: true, + DisableSymlinks: true, + Options: []getter.ClientOption{ + getter.WithInsecure(), + getter.WithContext(ctx), + getter.WithGetters(map[string]getter.Getter{ + "file": &CustomFileProvider{}, + }), + }, + } + if err := client.Get(); err != nil { + return fmt.Errorf("failed to fetch source '%s' to destination '%s': %w", unit.Source, dest, err) + } + } + + return nil +} + +type CustomFileProvider struct { + client *getter.Client +} + +// Get implements downloading functionality +func (p *CustomFileProvider) Get(dst string, u *url.URL) error { + src := u.Path + + // Check if source exists + fi, err := os.Stat(src) + if err != nil { + return err + } + + if fi.IsDir() { + return p.copyDir(src, dst) + } + return p.copyFile(src, dst) +} + +// GetFile implements single file download +func (p *CustomFileProvider) GetFile(dst string, u *url.URL) error { + return p.copyFile(u.Path, dst) +} + +// ClientMode determines if we're getting a directory or single file +func (p *CustomFileProvider) ClientMode(u *url.URL) (getter.ClientMode, error) { + fi, err := os.Stat(u.Path) + if err != nil { + return getter.ClientModeInvalid, err + } + + if fi.IsDir() { + return getter.ClientModeDir, nil + } + return getter.ClientModeFile, nil +} + +// SetClient sets the client for this provider +func (p *CustomFileProvider) SetClient(c *getter.Client) { + p.client = c +} + +func (p *CustomFileProvider) copyFile(src, dst string) error { + // Create destination directory if it doesn't exist + if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { + return fmt.Errorf("failed to create destination directory: %v", err) + } + + // Open source file + srcFile, err := os.Open(src) + if err != nil { + return fmt.Errorf("failed to open source file: %v", err) + } + defer srcFile.Close() + + // Create destination file + dstFile, err := os.Create(dst) + if err != nil { + return fmt.Errorf("failed to create destination file: %v", err) + } + defer dstFile.Close() + + // Copy the contents + if _, err := io.Copy(dstFile, srcFile); err != nil { + return fmt.Errorf("failed to copy file contents: %v", err) + } + + // Copy file mode + srcInfo, err := os.Stat(src) + if err != nil { + return fmt.Errorf("failed to stat source file: %v", err) + } + + return os.Chmod(dst, srcInfo.Mode()) +} + +func (p *CustomFileProvider) copyDir(src, dst string) error { + // Create the destination directory + srcInfo, err := os.Stat(src) + if err != nil { + return fmt.Errorf("failed to stat source directory: %v", err) + } + + if err := os.MkdirAll(dst, srcInfo.Mode()); err != nil { + return fmt.Errorf("failed to create destination directory: %v", err) + } + + // Read directory contents + entries, err := os.ReadDir(src) + if err != nil { + return fmt.Errorf("failed to read source directory: %v", err) + } + + for _, entry := range entries { + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + if entry.IsDir() { + if err := p.copyDir(srcPath, dstPath); err != nil { + return err + } + } else { + if err := p.copyFile(srcPath, dstPath); err != nil { + return err + } } }