@@ -3,19 +3,23 @@ package stack
3
3
import (
4
4
"context"
5
5
"fmt"
6
- "io"
7
- "net/url"
8
6
"os"
9
7
"path/filepath"
10
8
"strings"
11
9
10
+ "github.com/gruntwork-io/terragrunt/util"
11
+
12
12
"github.com/gruntwork-io/terragrunt/config"
13
- "github.com/hashicorp/go-getter"
13
+ "github.com/hashicorp/go-getter/v2 "
14
14
15
15
"github.com/gruntwork-io/terragrunt/internal/errors"
16
16
"github.com/gruntwork-io/terragrunt/options"
17
17
)
18
18
19
+ const (
20
+ StackManifestName = ".terragrunt-stack-manifest"
21
+ )
22
+
19
23
func generateStack (ctx context.Context , opts * options.TerragruntOptions ) error {
20
24
opts .TerragruntStackConfigPath = filepath .Join (opts .WorkingDir , defaultStackFile )
21
25
opts .Logger .Infof ("Generating stack from %s" , opts .TerragruntStackConfigPath )
@@ -48,172 +52,58 @@ func processStackFile(ctx context.Context, opts *options.TerragruntOptions, stac
48
52
return errors .New (fmt .Errorf ("failed to get absolute path for destination '%s': %w" , dest , err ))
49
53
}
50
54
51
- client := & getter.Client {
52
- Dst : dest ,
53
- Mode : getter .ClientModeAny ,
54
- Dir : true ,
55
- DisableSymlinks : true ,
56
- Options : []getter.ClientOption {
57
- getter .WithContext (ctx ),
58
- },
59
- }
60
-
61
- // setting custom getters
62
- client .Getters = map [string ]getter.Getter {}
63
-
64
- for getterName , getterValue := range getter .Getters {
65
- // setting custom getter for file to not use symlinks
66
- if getterName == "file" {
67
- client .Getters [getterName ] = & stacksFileProvider {}
68
- } else {
69
- client .Getters [getterName ] = getterValue
70
- }
71
- }
72
-
73
- // fetching unit source
74
55
src := unit .Source
56
+ opts .Logger .Debugf ("Processing unit: %s (%s) to %s" , unit .Name , src , dest )
75
57
76
- // set absolute path for source if it's not an absolute path or URL
77
- if ! filepath .IsAbs (unit .Source ) && ! isURL (client , unit .Source ) {
58
+ if isLocal (opts , src ) {
78
59
src = filepath .Join (opts .WorkingDir , unit .Source )
79
60
src , err = filepath .Abs (src )
80
61
81
62
if err != nil {
82
63
opts .Logger .Warnf ("failed to get absolute path for source '%s': %v" , unit .Source , err )
83
64
src = unit .Source
84
65
}
85
- }
86
66
87
- opts .Logger .Debugf ("Processing unit: %s (%s) to %s" , unit .Name , src , dest )
88
-
89
- client .Src = src
67
+ if err := util .CopyFolderContentsWithFilter (opts .Logger , src , dest , StackManifestName , func (absolutePath string ) bool {
68
+ return true
69
+ }); err != nil {
70
+ return errors .New (err )
71
+ }
72
+ } else {
73
+ if err := os .MkdirAll (dest , dirPerm ); err != nil {
74
+ return errors .New (err )
75
+ }
90
76
91
- if err := client .Get (); err != nil {
92
- return errors .New (err )
77
+ if _ , err := getter .GetAny (ctx , dest , src ); err != nil {
78
+ return errors .New (err )
79
+ }
93
80
}
94
81
}
95
82
96
83
return nil
97
84
}
98
85
99
- func isURL (client * getter.Client , str string ) bool {
100
- value , err := getter .Detect (str , client .Dst , getter .Detectors )
101
- if err != nil {
102
- return false
103
- }
104
- // check if starts with file://
105
- if strings .HasPrefix (value , "file://" ) {
106
- return false
107
- }
108
-
109
- return true
110
- }
111
-
112
- // stacksFileProvider is a custom getter for file:// protocol.
113
- type stacksFileProvider struct {
114
- client * getter.Client
115
- }
116
-
117
- // Get implements downloading functionality.
118
- func (p * stacksFileProvider ) Get (dst string , u * url.URL ) error {
119
- src := u .Path
120
- file , err := os .Stat (src )
121
-
122
- if err != nil {
123
- return errors .New (fmt .Errorf ("source path error: %w" , err ))
124
- }
125
-
126
- if file .IsDir () {
127
- return p .copyDir (src , dst )
128
- }
129
-
130
- return p .copyFile (src , dst )
131
- }
132
-
133
- // GetFile implements single file download.
134
- func (p * stacksFileProvider ) GetFile (dst string , u * url.URL ) error {
135
- return p .copyFile (u .Path , dst )
136
- }
137
-
138
- // ClientMode determines if we're getting a directory or single file.
139
- func (p * stacksFileProvider ) ClientMode (u * url.URL ) (getter.ClientMode , error ) {
140
- fi , err := os .Stat (u .Path )
141
- if err != nil {
142
- return getter .ClientModeInvalid , errors .New (err )
143
- }
144
-
145
- if fi .IsDir () {
146
- return getter .ClientModeDir , nil
147
- }
148
-
149
- return getter .ClientModeFile , nil
150
- }
151
-
152
- // SetClient sets the client for this provider.
153
- func (p * stacksFileProvider ) SetClient (c * getter.Client ) {
154
- p .client = c
155
- }
156
-
157
- func (p * stacksFileProvider ) copyFile (src , dst string ) error {
158
- if err := os .MkdirAll (filepath .Dir (dst ), dirPerm ); err != nil {
159
- return errors .New (err )
160
- }
161
-
162
- srcFile , err := os .Open (src )
163
- if err != nil {
164
- return errors .New (err )
165
- }
166
- defer srcFile .Close ()
167
-
168
- srcInfo , err := srcFile .Stat ()
169
- if err != nil {
170
- return errors .New (err )
171
- }
172
-
173
- dstFile , err := os .OpenFile (dst , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , srcInfo .Mode ())
174
- if err != nil {
175
- return errors .New (err )
176
- }
177
- defer dstFile .Close ()
178
-
179
- if _ , err := io .Copy (dstFile , srcFile ); err != nil {
180
- return errors .New (err )
181
- }
182
-
183
- return nil
184
- }
185
-
186
- func (p * stacksFileProvider ) copyDir (src , dst string ) error {
187
- srcInfo , err := os .Stat (src )
188
- if err != nil {
189
- return errors .New (err )
190
- }
191
-
192
- if err := os .MkdirAll (dst , srcInfo .Mode ()); err != nil {
193
- return errors .New (err )
86
+ func isLocal (opts * options.TerragruntOptions , src string ) bool {
87
+ // check initially if the source is a local file
88
+ src = filepath .Join (opts .WorkingDir , src )
89
+ if util .FileExists (src ) {
90
+ return true
194
91
}
195
-
196
- entries , err := os .ReadDir (src )
197
- if err != nil {
198
- return errors .New (err )
92
+ // check path through getters
93
+ req := & getter.Request {
94
+ Src : src ,
199
95
}
200
-
201
- for _ , entry := range entries {
202
- srcPath := filepath .Join (src , entry .Name ())
203
- dstPath := filepath .Join (dst , entry .Name ())
204
-
205
- if entry .IsDir () {
206
- if err := p .copyDir (srcPath , dstPath ); err != nil {
207
- return errors .New (err )
208
- }
209
-
96
+ for _ , g := range getter .Getters {
97
+ recognized , err := getter .Detect (req , g )
98
+ if err != nil {
99
+ opts .Logger .Debugf ("Error detecting getter for %s: %v" , src , err )
210
100
continue
211
101
}
212
102
213
- if err := p . copyFile ( srcPath , dstPath ); err != nil {
214
- return errors . New ( err )
103
+ if recognized {
104
+ break
215
105
}
216
106
}
217
107
218
- return nil
108
+ return strings . HasPrefix ( req . Src , "file://" )
219
109
}
0 commit comments