@@ -10,6 +10,7 @@ import (
10
10
"net/url"
11
11
"os"
12
12
"path/filepath"
13
+ "regexp"
13
14
"strings"
14
15
"time"
15
16
@@ -293,50 +294,79 @@ func (g *HttpGetter) Get(dst string, u *url.URL) error {
293
294
// If there is a subdir component, then we download the root separately
294
295
// into a temporary directory, then copy over the proper subdir.
295
296
source , subDir := SourceDirSubdir (source )
296
- if subDir == "" {
297
- var opts []ClientOption
298
297
299
- // Check if the protocol was switched to one which was not configured.
300
- //
298
+ var opts []ClientOption
299
+
300
+ // Check if the protocol was switched to one which was not configured.
301
+ if g .client != nil && g .client .Getters != nil {
302
+ // We must first use the Detectors provided, because `X-Terraform-Get does
303
+ // not necessarily return a valid URL. We can replace the source string
304
+ // here, since the detectors would have been called immediately during the
305
+ // next Get anyway.
306
+ source , err = Detect (source , g .client .Pwd , g .client .Detectors )
307
+ if err != nil {
308
+ return err
309
+ }
310
+
311
+ protocol := ""
312
+ // X-Terraform-Get allows paths relative to the previous request too,
313
+ // which won't have a protocol.
314
+ if ! relativeGet (source ) {
315
+ protocol = strings .Split (source , ":" )[0 ]
316
+ }
317
+
301
318
// Otherwise, all default getters are allowed.
302
- if g .client != nil && g .client .Getters != nil {
303
- protocol := strings .Split (source , ":" )[0 ]
319
+ if protocol != "" {
304
320
_ , allowed := g .client .Getters [protocol ]
305
321
if ! allowed {
306
322
return fmt .Errorf ("no getter available for X-Terraform-Get source protocol: %q" , protocol )
307
323
}
308
324
}
325
+ }
309
326
310
- // Add any getter client options.
311
- if g .client != nil {
312
- opts = g .client .Options
313
- }
327
+ // Add any getter client options.
328
+ if g .client != nil {
329
+ opts = g .client .Options
330
+ }
314
331
315
- // If the client is nil, we know we're using the HttpGetter directly. In this case,
316
- // we don't know exactly which protocols are configued, but we can make a good guess.
317
- //
318
- // This prevents all default getters from being allowed when only using the
319
- // HttpGetter directly. To enable protocol switching, a client "wrapper" must
320
- // be used.
321
- if g .client == nil {
332
+ // If the client is nil, we know we're using the HttpGetter directly. In
333
+ // this case, we don't know exactly which protocols are configured, but we
334
+ // can make a good guess.
335
+ //
336
+ // This prevents all default getters from being allowed when only using the
337
+ // HttpGetter directly. To enable protocol switching, a client "wrapper" must
338
+ // be used.
339
+ if g .client == nil {
340
+ switch {
341
+ case subDir != "" :
342
+ // If there's a subdirectory, we will also need a file getter to
343
+ // unpack it.
344
+ opts = append (opts , WithGetters (map [string ]Getter {
345
+ "file" : new (FileGetter ),
346
+ "http" : g ,
347
+ "https" : g ,
348
+ }))
349
+ default :
322
350
opts = append (opts , WithGetters (map [string ]Getter {
323
351
"http" : g ,
324
352
"https" : g ,
325
353
}))
326
354
}
355
+ }
327
356
328
- // Ensure we pass along the context we constructed in this function.
329
- //
330
- // This is especially important to enforce a limit on X-Terraform-Get redirects
331
- // which could be setup, if configured, at the top of this function.
332
- opts = append (opts , WithContext (ctx ))
357
+ // Ensure we pass along the context we constructed in this function.
358
+ //
359
+ // This is especially important to enforce a limit on X-Terraform-Get redirects
360
+ // which could be setup, if configured, at the top of this function.
361
+ opts = append (opts , WithContext (ctx ))
333
362
334
- // Note: this allows the protocol to be switched to another configured getters.
335
- return Get (dst , source , opts ... )
363
+ if subDir != "" {
364
+ // We have a subdir, time to jump some hoops
365
+ return g .getSubdir (ctx , dst , source , subDir , opts ... )
336
366
}
337
367
338
- // We have a subdir, time to jump some hoops
339
- return g . getSubdir ( ctx , dst , source , subDir )
368
+ // Note: this allows the protocol to be switched to another configured getters.
369
+ return Get ( dst , source , opts ... )
340
370
}
341
371
342
372
// GetFile fetches the file from src and stores it at dst.
@@ -478,7 +508,7 @@ func (g *HttpGetter) GetFile(dst string, src *url.URL) error {
478
508
479
509
// getSubdir downloads the source into the destination, but with
480
510
// the proper subdir.
481
- func (g * HttpGetter ) getSubdir (ctx context.Context , dst , source , subDir string ) error {
511
+ func (g * HttpGetter ) getSubdir (ctx context.Context , dst , source , subDir string , opts ... ClientOption ) error {
482
512
// Create a temporary directory to store the full source. This has to be
483
513
// a non-existent directory.
484
514
td , tdcloser , err := safetemp .Dir ("" , "getter" )
@@ -487,10 +517,6 @@ func (g *HttpGetter) getSubdir(ctx context.Context, dst, source, subDir string)
487
517
}
488
518
defer tdcloser .Close ()
489
519
490
- var opts []ClientOption
491
- if g .client != nil {
492
- opts = g .client .Options
493
- }
494
520
// Download that into the given directory
495
521
if err := Get (td , source , opts ... ); err != nil {
496
522
return err
@@ -566,6 +592,9 @@ func (g *HttpGetter) parseMeta(ctx context.Context, r io.Reader) (string, error)
566
592
}
567
593
}
568
594
595
+ // X-Terraform-Get allows paths relative to the previous request
596
+ var relativeGet = regexp .MustCompile (`^\.{0,2}/` ).MatchString
597
+
569
598
// attrValue returns the attribute value for the case-insensitive key
570
599
// `name', or the empty string if nothing is found.
571
600
func attrValue (attrs []xml.Attr , name string ) string {
0 commit comments