Allow to use isos in place

This commit is contained in:
Adrien Delorme 2020-02-06 17:07:58 +01:00
parent 193dad46e6
commit a19214afeb
63 changed files with 906 additions and 614 deletions

View File

@ -11,7 +11,7 @@ import (
"os"
"strings"
getter "github.com/hashicorp/go-getter"
getter "github.com/hashicorp/go-getter/v2"
"github.com/hashicorp/packer/template/interpolate"
)

View File

@ -11,8 +11,8 @@ import (
"runtime"
"strings"
getter "github.com/hashicorp/go-getter"
urlhelper "github.com/hashicorp/go-getter/helper/url"
getter "github.com/hashicorp/go-getter/v2"
urlhelper "github.com/hashicorp/go-getter/v2/helper/url"
"github.com/hashicorp/packer/common/filelock"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
@ -52,6 +52,8 @@ type StepDownload struct {
Extension string
}
var defaultGetterClient = getter.Client{}
func (s *StepDownload) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if len(s.Url) == 0 {
log.Printf("No URLs were provided to Step Download. Continuing...")
@ -96,24 +98,6 @@ func (s *StepDownload) Run(ctx context.Context, state multistep.StateBag) multis
return multistep.ActionHalt
}
var (
getters = getter.Getters
)
func init() {
if runtime.GOOS == "windows" {
getters["file"] = &getter.FileGetter{
// always copy local files instead of symlinking to fix GH-7534. The
// longer term fix for this would be to change the go-getter so that it
// can leave the source file where it is & tell us where it is.
Copy: true,
}
getters["smb"] = &getter.FileGetter{
Copy: true,
}
}
}
func (s *StepDownload) download(ctx context.Context, ui packer.Ui, source string) (string, error) {
if runtime.GOOS == "windows" {
// Check that the user specified a UNC path, and promote it to an smb:// uri.
@ -208,20 +192,21 @@ func (s *StepDownload) download(ctx context.Context, ui packer.Ui, source string
}
ui.Say(fmt.Sprintf("Trying %s", u.String()))
gc := getter.Client{
Ctx: ctx,
req := &getter.Request{
Dst: targetPath,
Src: src,
ProgressListener: ui,
Pwd: wd,
Dir: false,
Getters: getters,
Mode: getter.ModeFile,
}
if runtime.GOOS == "windows" {
req.Inplace = true
}
switch err := gc.Get(); err.(type) {
switch op, err := defaultGetterClient.Get(ctx, req); err.(type) {
case nil: // success !
ui.Say(fmt.Sprintf("%s => %s", u.String(), targetPath))
return targetPath, nil
ui.Say(fmt.Sprintf("%s => %s", u.String(), op.Dst))
return op.Dst, nil
case *getter.ChecksumError:
ui.Say(fmt.Sprintf("Checksum did not match, removing %s", targetPath))
if err := os.Remove(targetPath); err != nil {

View File

@ -16,7 +16,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
urlhelper "github.com/hashicorp/go-getter/helper/url"
urlhelper "github.com/hashicorp/go-getter/v2/helper/url"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer/tmp"
)

3
go.mod
View File

@ -72,7 +72,8 @@ require (
github.com/hashicorp/go-cty-funcs/encoding v0.0.0-20200203151509-c92509f48b18
github.com/hashicorp/go-cty-funcs/filesystem v0.0.0-20200203151509-c92509f48b18
github.com/hashicorp/go-cty-funcs/uuid v0.0.0-20200203151509-c92509f48b18
github.com/hashicorp/go-getter v1.3.1-0.20190906090232-a0f878cb75da
github.com/hashicorp/go-getter v1.3.1-0.20190906090232-a0f878cb75da // indirect
github.com/hashicorp/go-getter/v2 v2.0.0-20200206160058-e2a28063d6e7
github.com/hashicorp/go-multierror v1.0.0
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79
github.com/hashicorp/go-retryablehttp v0.5.2 // indirect

2
go.sum
View File

@ -239,6 +239,8 @@ github.com/hashicorp/go-cty-funcs/uuid v0.0.0-20200203151509-c92509f48b18 h1:CxY
github.com/hashicorp/go-cty-funcs/uuid v0.0.0-20200203151509-c92509f48b18/go.mod h1:QFbv9KeSic7KIgfOYbUW02G4LxOf3Fh9Ylm4n174LUQ=
github.com/hashicorp/go-getter v1.3.1-0.20190906090232-a0f878cb75da h1:HAasZmyRrb7/paYuww5RfVwY3wkFpsbMNYwBxOSZquY=
github.com/hashicorp/go-getter v1.3.1-0.20190906090232-a0f878cb75da/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
github.com/hashicorp/go-getter/v2 v2.0.0-20200206160058-e2a28063d6e7 h1:ODZKizgWGz4diUEZwCgf8qgIn/D+qVW/JOdVVV/z7k8=
github.com/hashicorp/go-getter/v2 v2.0.0-20200206160058-e2a28063d6e7/go.mod h1:jlmxRRjTpY0KdWrV1Uq38GUVskrjIZUrjOAybo0OArw=
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=

View File

@ -15,7 +15,7 @@ import (
"time"
"unicode"
getter "github.com/hashicorp/go-getter"
getter "github.com/hashicorp/go-getter/v2"
)
var ErrInterrupted = errors.New("interrupted")

View File

@ -1,24 +0,0 @@
sudo: false
addons:
apt:
sources:
- sourceline: 'ppa:git-core/ppa'
packages:
- git
language: go
os:
- linux
- osx
go:
- "1.11.x"
before_script:
- go build ./cmd/go-getter
branches:
only:
- master

View File

@ -1,46 +0,0 @@
package getter
import "context"
// A ClientOption allows to configure a client
type ClientOption func(*Client) error
// Configure configures a client with options.
func (c *Client) Configure(opts ...ClientOption) error {
if c.Ctx == nil {
c.Ctx = context.Background()
}
c.Options = opts
for _, opt := range opts {
err := opt(c)
if err != nil {
return err
}
}
// Default decompressor values
if c.Decompressors == nil {
c.Decompressors = Decompressors
}
// Default detector values
if c.Detectors == nil {
c.Detectors = Detectors
}
// Default getter values
if c.Getters == nil {
c.Getters = Getters
}
for _, getter := range c.Getters {
getter.SetClient(c)
}
return nil
}
// WithContext allows to pass a context to operation
// in order to be able to cancel a download in progress.
func WithContext(ctx context.Context) func(*Client) error {
return func(c *Client) error {
c.Ctx = ctx
return nil
}
}

View File

@ -1,20 +0,0 @@
package getter
import "context"
// getter is our base getter; it regroups
// fields all getters have in common.
type getter struct {
client *Client
}
func (g *getter) SetClient(c *Client) { g.client = c }
// Context tries to returns the Contex from the getter's
// client. otherwise context.Background() is returned.
func (g *getter) Context() context.Context {
if g == nil || g.client == nil {
return context.Background()
}
return g.client.Ctx
}

View File

@ -1,36 +0,0 @@
package getter
import (
"net/url"
"os"
)
// FileGetter is a Getter implementation that will download a module from
// a file scheme.
type FileGetter struct {
getter
// Copy, if set to true, will copy data instead of using a symlink. If
// false, attempts to symlink to speed up the operation and to lower the
// disk space usage. If the symlink fails, may attempt to copy on windows.
Copy bool
}
func (g *FileGetter) ClientMode(u *url.URL) (ClientMode, error) {
path := u.Path
if u.RawPath != "" {
path = u.RawPath
}
fi, err := os.Stat(path)
if err != nil {
return 0, err
}
// Check if the source is a directory.
if fi.IsDir() {
return ClientModeDir, nil
}
return ClientModeFile, nil
}

View File

@ -1,103 +0,0 @@
// +build !windows
package getter
import (
"fmt"
"net/url"
"os"
"path/filepath"
)
func (g *FileGetter) Get(dst string, u *url.URL) error {
path := u.Path
if u.RawPath != "" {
path = u.RawPath
}
// The source path must exist and be a directory to be usable.
if fi, err := os.Stat(path); err != nil {
return fmt.Errorf("source path error: %s", err)
} else if !fi.IsDir() {
return fmt.Errorf("source path must be a directory")
}
fi, err := os.Lstat(dst)
if err != nil && !os.IsNotExist(err) {
return err
}
// If the destination already exists, it must be a symlink
if err == nil {
mode := fi.Mode()
if mode&os.ModeSymlink == 0 {
return fmt.Errorf("destination exists and is not a symlink")
}
// Remove the destination
if err := os.Remove(dst); err != nil {
return err
}
}
// Create all the parent directories
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
return err
}
return os.Symlink(path, dst)
}
func (g *FileGetter) GetFile(dst string, u *url.URL) error {
ctx := g.Context()
path := u.Path
if u.RawPath != "" {
path = u.RawPath
}
// The source path must exist and be a file to be usable.
if fi, err := os.Stat(path); err != nil {
return fmt.Errorf("source path error: %s", err)
} else if fi.IsDir() {
return fmt.Errorf("source path must be a file")
}
_, err := os.Lstat(dst)
if err != nil && !os.IsNotExist(err) {
return err
}
// If the destination already exists, it must be a symlink
if err == nil {
// Remove the destination
if err := os.Remove(dst); err != nil {
return err
}
}
// Create all the parent directories
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
return err
}
// If we're not copying, just symlink and we're done
if !g.Copy {
return os.Symlink(path, dst)
}
// Copy
srcF, err := os.Open(path)
if err != nil {
return err
}
defer srcF.Close()
dstF, err := os.Create(dst)
if err != nil {
return err
}
defer dstF.Close()
_, err = Copy(ctx, dstF, srcF)
return err
}

1
vendor/github.com/hashicorp/go-getter/v2/.gitignore generated vendored Normal file
View File

@ -0,0 +1 @@
vendor

354
vendor/github.com/hashicorp/go-getter/v2/LICENSE generated vendored Normal file
View File

@ -0,0 +1,354 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. “Contributor”
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. “Contributor Version”
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributors Contribution.
1.3. “Contribution”
means Covered Software of a particular Contributor.
1.4. “Covered Software”
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. “Incompatible With Secondary Licenses”
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of version
1.1 or earlier of the License, but not also under the terms of a
Secondary License.
1.6. “Executable Form”
means any form of the work other than Source Code Form.
1.7. “Larger Work”
means a work that combines Covered Software with other material, in a separate
file or files, that is not Covered Software.
1.8. “License”
means this document.
1.9. “Licensable”
means having the right to grant, to the maximum extent possible, whether at the
time of the initial grant or subsequently, any and all of the rights conveyed by
this License.
1.10. “Modifications”
means any of the following:
a. any file in Source Code Form that results from an addition to, deletion
from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. “Patent Claims” of a Contributor
means any patent claim(s), including without limitation, method, process,
and apparatus claims, in any patent Licensable by such Contributor that
would be infringed, but for the grant of the License, by the making,
using, selling, offering for sale, having made, import, or transfer of
either its Contributions or its Contributor Version.
1.12. “Secondary License”
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. “Source Code Form”
means the form of the work preferred for making modifications.
1.14. “You” (or “Your”)
means an individual or a legal entity exercising rights under this
License. For legal entities, “You” includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, “control” means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or as
part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its Contributions
or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution become
effective for each Contribution on the date the Contributor first distributes
such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under this
License. No additional rights or licenses will be implied from the distribution
or licensing of Covered Software under this License. Notwithstanding Section
2.1(b) above, no patent license is granted by a Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third partys
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of its
Contributions.
This License does not grant any rights in the trademarks, service marks, or
logos of any Contributor (except as may be necessary to comply with the
notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this License
(see Section 10.2) or under the terms of a Secondary License (if permitted
under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its Contributions
are its original creation(s) or it has sufficient rights to grant the
rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under applicable
copyright doctrines of fair use, fair dealing, or other equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under the
terms of this License. You must inform recipients that the Source Code Form
of the Covered Software is governed by the terms of this License, and how
they can obtain a copy of this License. You may not attempt to alter or
restrict the recipients rights in the Source Code Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this License,
or sublicense it under different terms, provided that the license for
the Executable Form does not attempt to limit or alter the recipients
rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for the
Covered Software. If the Larger Work is a combination of Covered Software
with a work governed by one or more Secondary Licenses, and the Covered
Software is not Incompatible With Secondary Licenses, this License permits
You to additionally distribute such Covered Software under the terms of
such Secondary License(s), so that the recipient of the Larger Work may, at
their option, further distribute the Covered Software under the terms of
either this License or such Secondary License(s).
3.4. Notices
You may not remove or alter the substance of any license notices (including
copyright notices, patent notices, disclaimers of warranty, or limitations
of liability) contained within the Source Code Form of the Covered
Software, except that You may alter any license notices to the extent
required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on behalf
of any Contributor. You must make it absolutely clear that any such
warranty, support, indemnity, or liability obligation is offered by You
alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute, judicial
order, or regulation then You must: (a) comply with the terms of this License
to the maximum extent possible; and (b) describe the limitations and the code
they affect. Such description must be placed in a text file included with all
distributions of the Covered Software under this License. Except to the
extent prohibited by statute or regulation, such description must be
sufficiently detailed for a recipient of ordinary skill to be able to
understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
if such Contributor fails to notify You of the non-compliance by some
reasonable means prior to 60 days after You have come back into compliance.
Moreover, Your grants from a particular Contributor are reinstated on an
ongoing basis if such Contributor notifies You of the non-compliance by
some reasonable means, this is the first time You have received notice of
non-compliance with this License from such Contributor, and You become
compliant prior to 30 days after Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions, counter-claims,
and cross-claims) alleging that a Contributor Version directly or
indirectly infringes any patent, then the rights granted to You by any and
all Contributors for the Covered Software under Section 2.1 of this License
shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an “as is” basis, without
warranty of any kind, either expressed, implied, or statutory, including,
without limitation, warranties that the Covered Software is free of defects,
merchantable, fit for a particular purpose or non-infringing. The entire
risk as to the quality and performance of the Covered Software is with You.
Should any Covered Software prove defective in any respect, You (not any
Contributor) assume the cost of any necessary servicing, repair, or
correction. This disclaimer of warranty constitutes an essential part of this
License. No use of any Covered Software is authorized under this License
except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from such
partys negligence to the extent applicable law prohibits such limitation.
Some jurisdictions do not allow the exclusion or limitation of incidental or
consequential damages, so this exclusion and limitation may not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts of
a jurisdiction where the defendant maintains its principal place of business
and such litigation shall be governed by laws of that jurisdiction, without
reference to its conflict-of-law provisions. Nothing in this Section shall
prevent a partys ability to bring cross-claims or counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject matter
hereof. If any provision of this License is held to be unenforceable, such
provision shall be reformed only to the extent necessary to make it
enforceable. Any law or regulation which provides that the language of a
contract shall be construed against the drafter shall not be used to construe
this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version of
the License under which You originally received the Covered Software, or
under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a modified
version of this License if you rename the license and remove any
references to the name of the license steward (except to note that such
modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file, then
You may include the notice in a location (such as a LICENSE file in a relevant
directory) where a recipient would be likely to look for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - “Incompatible With Secondary Licenses” Notice
This Source Code Form is “Incompatible
With Secondary Licenses”, as defined by
the Mozilla Public License, v. 2.0.

View File

@ -1,10 +1,10 @@
# go-getter
[![Build Status](http://img.shields.io/travis/hashicorp/go-getter.svg?style=flat-square)][travis]
[![CircleCI](https://circleci.com/gh/hashicorp/go-getter/tree/master.svg?style=svg)][circleci]
[![Build status](https://ci.appveyor.com/api/projects/status/ulq3qr43n62croyq/branch/master?svg=true)][appveyor]
[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs]
[travis]: http://travis-ci.org/hashicorp/go-getter
[circleci]: https://circleci.com/gh/hashicorp/go-getter/tree/master
[godocs]: http://godoc.org/github.com/hashicorp/go-getter
[appveyor]: https://ci.appveyor.com/project/hashicorp/go-getter/branch/master
@ -356,3 +356,7 @@ In order to access to GCS, authentication credentials should be provided. More i
- gcs::https://www.googleapis.com/storage/v1/bucket
- gcs::https://www.googleapis.com/storage/v1/bucket/foo.zip
- www.googleapis.com/storage/v1/bucket/foo
#### GCS Testing
The tests for `get_gcs.go` require you to have GCP credentials set in your environment. These credentials can have any level of permissions to any project, they just need to exist. This means setting `GOOGLE_APPLICATION_CREDENTIALS="~/path/to/credentials.json"` or `GOOGLE_CREDENTIALS="{stringified-credentials-json}"`. Due to this configuration, `get_gcs_test.go` will fail for external contributors in CircleCI.

View File

@ -3,6 +3,7 @@ package getter
import (
"bufio"
"bytes"
"context"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
@ -93,7 +94,7 @@ func (c *FileChecksum) checksum(source string) error {
// <checksum> *file2
//
// see parseChecksumLine for more detail on checksum file parsing
func (c *Client) extractChecksum(u *url.URL) (*FileChecksum, error) {
func (c *Client) extractChecksum(ctx context.Context, u *url.URL) (*FileChecksum, error) {
q := u.Query()
v := q.Get("checksum")
@ -115,7 +116,7 @@ func (c *Client) extractChecksum(u *url.URL) (*FileChecksum, error) {
switch checksumType {
case "file":
return c.ChecksumFromFile(checksumValue, u)
return c.ChecksumFromFile(ctx, checksumValue, u.Path)
default:
return newChecksumFromType(checksumType, checksumValue, filepath.Base(u.EscapedPath()))
}
@ -183,15 +184,16 @@ func newChecksumFromValue(checksumValue, filename string) (*FileChecksum, error)
return c, nil
}
// ChecksumFromFile will return all the FileChecksums found in file
// ChecksumFromFile will return the first file checksum found in the
// `checksumURL` file that corresponds to the `checksummedPath` path.
//
// ChecksumFromFile will try to guess the hashing algorithm based on content
// of checksum file
// ChecksumFromFile will infer the hashing algorithm based on the checksumURL
// file content.
//
// ChecksumFromFile will only return checksums for files that match file
// behind src
func (c *Client) ChecksumFromFile(checksumFile string, src *url.URL) (*FileChecksum, error) {
checksumFileURL, err := urlhelper.Parse(checksumFile)
// ChecksumFromFile will only return checksums for files that match
// checksummedPath, which is the object being checksummed.
func (c *Client) ChecksumFromFile(ctx context.Context, checksumURL, checksummedPath string) (*FileChecksum, error) {
checksumFileURL, err := urlhelper.Parse(checksumURL)
if err != nil {
return nil, err
}
@ -202,24 +204,20 @@ func (c *Client) ChecksumFromFile(checksumFile string, src *url.URL) (*FileCheck
}
defer os.Remove(tempfile)
c2 := &Client{
Ctx: c.Ctx,
Getters: c.Getters,
Decompressors: c.Decompressors,
Detectors: c.Detectors,
Pwd: c.Pwd,
Dir: false,
Src: checksumFile,
Dst: tempfile,
ProgressListener: c.ProgressListener,
req := &Request{
// Pwd: c.Pwd, TODO(adrien): pass pwd ?
Mode: ModeFile,
Src: checksumURL,
Dst: tempfile,
// ProgressListener: c.ProgressListener, TODO(adrien): pass progress bar ?
}
if err = c2.Get(); err != nil {
if _, err = c.Get(ctx, req); err != nil {
return nil, fmt.Errorf(
"Error downloading checksum file: %s", err)
}
filename := filepath.Base(src.Path)
absPath, err := filepath.Abs(src.Path)
filename := filepath.Base(checksummedPath)
absPath, err := filepath.Abs(checksummedPath)
if err != nil {
return nil, err
}
@ -277,7 +275,7 @@ func (c *Client) ChecksumFromFile(checksumFile string, src *url.URL) (*FileCheck
}
}
}
return nil, fmt.Errorf("no checksum found in: %s", checksumFile)
return nil, fmt.Errorf("no checksum found in: %s", checksumURL)
}
// parseChecksumLine takes a line from a checksum file and returns

View File

@ -19,25 +19,6 @@ import (
// Using a client directly allows more fine-grained control over how downloading
// is done, as well as customizing the protocols supported.
type Client struct {
// Ctx for cancellation
Ctx context.Context
// Src is the source URL to get.
//
// Dst is the path to save the downloaded thing as. If Dir is set to
// true, then this should be a directory. If the directory doesn't exist,
// it will be created for you.
//
// Pwd is the working directory for detection. If this isn't set, some
// detection may fail. Client will not default pwd to the current
// working directory for security reasons.
Src string
Dst string
Pwd string
// Mode is the method of download the client will use. See ClientMode
// for documentation.
Mode ClientMode
// Detectors is the list of detectors that are tried on the source.
// If this is nil, then the default Detectors will be used.
@ -50,78 +31,66 @@ type Client struct {
// Getters is the map of protocols supported by this client. If this
// is nil, then the default Getters variable will be used.
Getters map[string]Getter
}
// Dir, if true, tells the Client it is downloading a directory (versus
// a single file). This distinction is necessary since filenames and
// directory names follow the same format so disambiguating is impossible
// without knowing ahead of time.
//
// WARNING: deprecated. If Mode is set, that will take precedence.
Dir bool
// ProgressListener allows to track file downloads.
// By default a no op progress listener is used.
ProgressListener ProgressTracker
Options []ClientOption
// GetResult is the result of a Client.Get
type GetResult struct {
// Local destination of the gotten object.
Dst string
}
// Get downloads the configured source to the destination.
func (c *Client) Get() error {
if err := c.Configure(c.Options...); err != nil {
return err
func (c *Client) Get(ctx context.Context, req *Request) (*GetResult, error) {
if err := c.configure(); err != nil {
return nil, err
}
// Store this locally since there are cases we swap this
mode := c.Mode
if mode == ClientModeInvalid {
if c.Dir {
mode = ClientModeDir
} else {
mode = ClientModeFile
}
if req.Mode == ModeInvalid {
req.Mode = ModeAny
}
src, err := Detect(c.Src, c.Pwd, c.Detectors)
var err error
req.Src, err = Detect(req.Src, req.Pwd, c.Detectors)
if err != nil {
return err
return nil, err
}
var force string
// Determine if we have a forced protocol, i.e. "git::http://..."
force, src := getForcedGetter(src)
force, req.Src = getForcedGetter(req.Src)
// If there is a subdir component, then we download the root separately
// and then copy over the proper subdir.
var realDst string
dst := c.Dst
src, subDir := SourceDirSubdir(src)
var realDst, subDir string
req.Src, subDir = SourceDirSubdir(req.Src)
if subDir != "" {
td, tdcloser, err := safetemp.Dir("", "getter")
if err != nil {
return err
return nil, err
}
defer tdcloser.Close()
realDst = dst
dst = td
realDst = req.Dst
req.Dst = td
}
u, err := urlhelper.Parse(src)
req.u, err = urlhelper.Parse(req.Src)
if err != nil {
return err
return nil, err
}
if force == "" {
force = u.Scheme
force = req.u.Scheme
}
g, ok := c.Getters[force]
if !ok {
return fmt.Errorf(
return nil, fmt.Errorf(
"download not supported for scheme '%s'", force)
}
// We have magic query parameters that we use to signal different features
q := u.Query()
q := req.u.Query()
// Determine if we have an archive type
archiveV := q.Get("archive")
@ -129,7 +98,7 @@ func (c *Client) Get() error {
// Delete the paramter since it is a magic parameter we don't
// want to pass on to the Getter
q.Del("archive")
u.RawQuery = q.Encode()
req.u.RawQuery = q.Encode()
// If we can parse the value as a bool and it is false, then
// set the archive to "-" which should never map to a decompressor
@ -141,7 +110,7 @@ func (c *Client) Get() error {
// We don't appear to... but is it part of the filename?
matchingLen := 0
for k := range c.Decompressors {
if strings.HasSuffix(u.Path, "."+k) && len(k) > matchingLen {
if strings.HasSuffix(req.u.Path, "."+k) && len(k) > matchingLen {
archiveV = k
matchingLen = len(k)
}
@ -159,73 +128,73 @@ func (c *Client) Get() error {
// this at the end of everything.
td, err := ioutil.TempDir("", "getter")
if err != nil {
return fmt.Errorf(
return nil, fmt.Errorf(
"Error creating temporary directory for archive: %s", err)
}
defer os.RemoveAll(td)
// Swap the download directory to be our temporary path and
// store the old values.
decompressDst = dst
decompressDir = mode != ClientModeFile
dst = filepath.Join(td, "archive")
mode = ClientModeFile
decompressDst = req.Dst
decompressDir = req.Mode != ModeFile
req.Dst = filepath.Join(td, "archive")
req.Mode = ModeFile
}
// Determine checksum if we have one
checksum, err := c.extractChecksum(u)
checksum, err := c.extractChecksum(ctx, req.u)
if err != nil {
return fmt.Errorf("invalid checksum: %s", err)
return nil, fmt.Errorf("invalid checksum: %s", err)
}
// Delete the query parameter if we have it.
q.Del("checksum")
u.RawQuery = q.Encode()
req.u.RawQuery = q.Encode()
if mode == ClientModeAny {
if req.Mode == ModeAny {
// Ask the getter which client mode to use
mode, err = g.ClientMode(u)
req.Mode, err = g.Mode(ctx, req.u)
if err != nil {
return err
return nil, err
}
// Destination is the base name of the URL path in "any" mode when
// a file source is detected.
if mode == ClientModeFile {
filename := filepath.Base(u.Path)
if req.Mode == ModeFile {
filename := filepath.Base(req.u.Path)
// Determine if we have a custom file name
if v := q.Get("filename"); v != "" {
// Delete the query parameter if we have it.
q.Del("filename")
u.RawQuery = q.Encode()
req.u.RawQuery = q.Encode()
filename = v
}
dst = filepath.Join(dst, filename)
req.Dst = filepath.Join(req.Dst, filename)
}
}
// If we're not downloading a directory, then just download the file
// and return.
if mode == ClientModeFile {
if req.Mode == ModeFile {
getFile := true
if checksum != nil {
if err := checksum.checksum(dst); err == nil {
if err := checksum.checksum(req.Dst); err == nil {
// don't get the file if the checksum of dst is correct
getFile = false
}
}
if getFile {
err := g.GetFile(dst, u)
err := g.GetFile(ctx, req)
if err != nil {
return err
return nil, err
}
if checksum != nil {
if err := checksum.checksum(dst); err != nil {
return err
if err := checksum.checksum(req.Dst); err != nil {
return nil, err
}
}
}
@ -233,25 +202,25 @@ func (c *Client) Get() error {
if decompressor != nil {
// We have a decompressor, so decompress the current destination
// into the final destination with the proper mode.
err := decompressor.Decompress(decompressDst, dst, decompressDir)
err := decompressor.Decompress(decompressDst, req.Dst, decompressDir)
if err != nil {
return err
return nil, err
}
// Swap the information back
dst = decompressDst
req.Dst = decompressDst
if decompressDir {
mode = ClientModeAny
req.Mode = ModeAny
} else {
mode = ClientModeFile
req.Mode = ModeFile
}
}
// We check the dir value again because it can be switched back
// if we were unarchiving. If we're still only Get-ing a file, then
// we're done.
if mode == ClientModeFile {
return nil
if req.Mode == ModeFile {
return &GetResult{req.Dst}, nil
}
}
@ -263,36 +232,36 @@ func (c *Client) Get() error {
// If we're getting a directory, then this is an error. You cannot
// checksum a directory. TODO: test
if checksum != nil {
return fmt.Errorf(
return nil, fmt.Errorf(
"checksum cannot be specified for directory download")
}
// We're downloading a directory, which might require a bit more work
// if we're specifying a subdir.
err := g.Get(dst, u)
err := g.Get(ctx, req)
if err != nil {
err = fmt.Errorf("error downloading '%s': %s", src, err)
return err
err = fmt.Errorf("error downloading '%s': %s", req.Src, err)
return nil, err
}
}
// If we have a subdir, copy that over
if subDir != "" {
if err := os.RemoveAll(realDst); err != nil {
return err
return nil, err
}
if err := os.MkdirAll(realDst, 0755); err != nil {
return err
return nil, err
}
// Process any globs
subDir, err := SubdirGlob(dst, subDir)
subDir, err := SubdirGlob(req.Dst, subDir)
if err != nil {
return err
return nil, err
}
return copyDir(c.Ctx, realDst, subDir, false)
return &GetResult{realDst}, copyDir(ctx, realDst, subDir, false)
}
return nil
return &GetResult{req.Dst}, nil
}

View File

@ -0,0 +1,22 @@
package getter
// configure configures a client with options.
func (c *Client) configure() error {
// Default decompressor values
if c.Decompressors == nil {
c.Decompressors = Decompressors
}
// Default detector values
if c.Detectors == nil {
c.Detectors = Detectors
}
// Default getter values
if c.Getters == nil {
c.Getters = Getters
}
for _, getter := range c.Getters {
getter.SetClient(c)
}
return nil
}

View File

@ -4,18 +4,6 @@ import (
"io"
)
// WithProgress allows for a user to track
// the progress of a download.
// For example by displaying a progress bar with
// current download.
// Not all getters have progress support yet.
func WithProgress(pl ProgressTracker) func(*Client) error {
return func(c *Client) error {
c.ProgressListener = pl
return nil
}
}
// ProgressTracker allows to track the progress of downloads.
type ProgressTracker interface {
// TrackProgress should be called when

View File

@ -8,8 +8,8 @@ import (
"path/filepath"
)
// ZipDecompressor is an implementation of Decompressor that can
// decompress zip files.
// ZipDecompressor is an implementation of Decompressor that can decompress zip
// files.
type ZipDecompressor struct{}
func (d *ZipDecompressor) Decompress(dst, src string, dir bool) error {

View File

@ -1,6 +1,7 @@
package getter
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
@ -39,7 +40,7 @@ func (s *FolderStorage) Dir(key string) (d string, e bool, err error) {
}
// Get implements Storage.Get
func (s *FolderStorage) Get(key string, source string, update bool) error {
func (s *FolderStorage) Get(ctx context.Context, key string, source string, update bool) error {
dir := s.dir(key)
if !update {
if _, err := os.Stat(dir); err == nil {
@ -54,7 +55,8 @@ func (s *FolderStorage) Get(key string, source string, update bool) error {
}
// Get the source. This always forces an update.
return Get(dir, source)
_, err := Get(ctx, dir, source)
return err
}
// dir returns the directory name internally that we'll use to map to

View File

@ -13,6 +13,7 @@ package getter
import (
"bytes"
"context"
"fmt"
"net/url"
"os/exec"
@ -31,16 +32,16 @@ type Getter interface {
// The directory may already exist (if we're updating). If it is in a
// format that isn't understood, an error should be returned. Get shouldn't
// simply nuke the directory.
Get(string, *url.URL) error
Get(context.Context, *Request) error
// GetFile downloads the give URL into the given path. The URL must
// reference a single file. If possible, the Getter should check if
// the remote end contains the same file and no-op this operation.
GetFile(string, *url.URL) error
GetFile(context.Context, *Request) error
// ClientMode returns the mode based on the given URL. This is used to
// Mode returns the mode based on the given URL. This is used to
// allow clients to let the getters decide which mode to use.
ClientMode(*url.URL) (ClientMode, error)
Mode(context.Context, *url.URL) (Mode, error)
// SetClient allows a getter to know it's client
// in order to access client's Get functions or
@ -59,6 +60,12 @@ var forcedRegexp = regexp.MustCompile(`^([A-Za-z0-9]+)::(.+)$`)
// httpClient is the default client to be used by HttpGetters.
var httpClient = cleanhttp.DefaultClient()
var DefaultClient = &Client{
Getters: Getters,
Detectors: Detectors,
Decompressors: Decompressors,
}
func init() {
httpGetter := &HttpGetter{
Netrc: true,
@ -80,13 +87,13 @@ func init() {
//
// src is a URL, whereas dst is always just a file path to a folder. This
// folder doesn't need to exist. It will be created if it doesn't exist.
func Get(dst, src string, opts ...ClientOption) error {
return (&Client{
Src: src,
Dst: dst,
Dir: true,
Options: opts,
}).Get()
func Get(ctx context.Context, dst, src string) (*GetResult, error) {
req := &Request{
Src: src,
Dst: dst,
Mode: ModeDir,
}
return DefaultClient.Get(ctx, req)
}
// GetAny downloads a URL into the given destination. Unlike Get or
@ -95,24 +102,24 @@ func Get(dst, src string, opts ...ClientOption) error {
// dst must be a directory. If src is a file, it will be downloaded
// into dst with the basename of the URL. If src is a directory or
// archive, it will be unpacked directly into dst.
func GetAny(dst, src string, opts ...ClientOption) error {
return (&Client{
Src: src,
Dst: dst,
Mode: ClientModeAny,
Options: opts,
}).Get()
func GetAny(ctx context.Context, dst, src string) (*GetResult, error) {
req := &Request{
Src: src,
Dst: dst,
Mode: ModeAny,
}
return DefaultClient.Get(ctx, req)
}
// GetFile downloads the file specified by src into the path specified by
// dst.
func GetFile(dst, src string, opts ...ClientOption) error {
return (&Client{
Src: src,
Dst: dst,
Dir: false,
Options: opts,
}).Get()
func GetFile(ctx context.Context, dst, src string) (*GetResult, error) {
req := &Request{
Src: src,
Dst: dst,
Mode: ModeFile,
}
return DefaultClient.Get(ctx, req)
}
// getRunCommand is a helper that will run a command and capture the output

9
vendor/github.com/hashicorp/go-getter/v2/get_base.go generated vendored Normal file
View File

@ -0,0 +1,9 @@
package getter
// getter is our base getter; it regroups
// fields all getters have in common.
type getter struct {
client *Client
}
func (g *getter) SetClient(c *Client) { g.client = c }

View File

@ -1,24 +1,44 @@
// +build windows
package getter
import (
"context"
"fmt"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
)
func (g *FileGetter) Get(dst string, u *url.URL) error {
ctx := g.Context()
// FileGetter is a Getter implementation that will download a module from
// a file scheme.
type FileGetter struct {
getter
}
func (g *FileGetter) Mode(ctx context.Context, u *url.URL) (Mode, error) {
path := u.Path
if u.RawPath != "" {
path = u.RawPath
}
fi, err := os.Stat(path)
if err != nil {
return 0, err
}
// Check if the source is a directory.
if fi.IsDir() {
return ModeDir, nil
}
return ModeFile, nil
}
func (g *FileGetter) Get(ctx context.Context, req *Request) error {
path := req.u.Path
if req.u.RawPath != "" {
path = req.u.RawPath
}
// The source path must exist and be a directory to be usable.
if fi, err := os.Stat(path); err != nil {
return fmt.Errorf("source path error: %s", err)
@ -26,11 +46,16 @@ func (g *FileGetter) Get(dst string, u *url.URL) error {
return fmt.Errorf("source path must be a directory")
}
fi, err := os.Lstat(dst)
fi, err := os.Lstat(req.Dst)
if err != nil && !os.IsNotExist(err) {
return err
}
if req.Inplace {
req.Dst = path
return nil
}
// If the destination already exists, it must be a symlink
if err == nil {
mode := fi.Mode()
@ -39,42 +64,38 @@ func (g *FileGetter) Get(dst string, u *url.URL) error {
}
// Remove the destination
if err := os.Remove(dst); err != nil {
if err := os.Remove(req.Dst); err != nil {
return err
}
}
// Create all the parent directories
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
if err := os.MkdirAll(filepath.Dir(req.Dst), 0755); err != nil {
return err
}
sourcePath := toBackslash(path)
// Use mklink to create a junction point
output, err := exec.CommandContext(ctx, "cmd", "/c", "mklink", "/J", dst, sourcePath).CombinedOutput()
if err != nil {
return fmt.Errorf("failed to run mklink %v %v: %v %q", dst, sourcePath, err, output)
}
return nil
return SymlinkAny(path, req.Dst)
}
func (g *FileGetter) GetFile(dst string, u *url.URL) error {
ctx := g.Context()
path := u.Path
if u.RawPath != "" {
path = u.RawPath
func (g *FileGetter) GetFile(ctx context.Context, req *Request) error {
path := req.u.Path
if req.u.RawPath != "" {
path = req.u.RawPath
}
// The source path must exist and be a directory to be usable.
// The source path must exist and be a file to be usable.
if fi, err := os.Stat(path); err != nil {
return fmt.Errorf("source path error: %s", err)
} else if fi.IsDir() {
return fmt.Errorf("source path must be a file")
}
_, err := os.Lstat(dst)
if req.Inplace {
req.Dst = path
return nil
}
_, err := os.Lstat(req.Dst)
if err != nil && !os.IsNotExist(err) {
return err
}
@ -82,19 +103,19 @@ func (g *FileGetter) GetFile(dst string, u *url.URL) error {
// If the destination already exists, it must be a symlink
if err == nil {
// Remove the destination
if err := os.Remove(dst); err != nil {
if err := os.Remove(req.Dst); err != nil {
return err
}
}
// Create all the parent directories
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
if err := os.MkdirAll(filepath.Dir(req.Dst), 0755); err != nil {
return err
}
// If we're not copying, just symlink and we're done
if !g.Copy {
if err = os.Symlink(path, dst); err == nil {
if !req.Copy {
if err = os.Symlink(path, req.Dst); err == nil {
return err
}
lerr, ok := err.(*os.LinkError)
@ -102,8 +123,9 @@ func (g *FileGetter) GetFile(dst string, u *url.URL) error {
return err
}
switch lerr.Err {
case syscall.ERROR_PRIVILEGE_NOT_HELD:
// no symlink privilege, let's
case ErrUnauthorized:
// On windows this means we don't have
// symlink privilege, let's
// fallback to a copy to avoid an error.
break
default:
@ -118,7 +140,7 @@ func (g *FileGetter) GetFile(dst string, u *url.URL) error {
}
defer srcF.Close()
dstF, err := os.Create(dst)
dstF, err := os.Create(req.Dst)
if err != nil {
return err
}
@ -127,10 +149,3 @@ func (g *FileGetter) GetFile(dst string, u *url.URL) error {
_, err = Copy(ctx, dstF, srcF)
return err
}
// toBackslash returns the result of replacing each slash character
// in path with a backslash ('\') character. Multiple separators are
// replaced by multiple backslashes.
func toBackslash(path string) string {
return strings.Replace(path, "/", "\\", -1)
}

View File

@ -0,0 +1,10 @@
// +build !windows
package getter
import (
"os"
)
var ErrUnauthorized = os.ErrPermission
var SymlinkAny = os.Symlink

View File

@ -0,0 +1,21 @@
package getter
import (
"fmt"
"os/exec"
"path/filepath"
"syscall"
)
func SymlinkAny(oldname, newname string) error {
sourcePath := filepath.FromSlash(oldname)
// Use mklink to create a junction point
output, err := exec.Command("cmd", "/c", "mklink", "/J", newname, sourcePath).CombinedOutput()
if err != nil {
return fmt.Errorf("failed to run mklink %v %v: %v %q", newname, sourcePath, err, output)
}
return nil
}
var ErrUnauthorized = syscall.ERROR_PRIVILEGE_NOT_HELD

View File

@ -18,8 +18,7 @@ type GCSGetter struct {
getter
}
func (g *GCSGetter) ClientMode(u *url.URL) (ClientMode, error) {
ctx := g.Context()
func (g *GCSGetter) Mode(ctx context.Context, u *url.URL) (Mode, error) {
// Parse URL
bucket, object, err := g.parseURL(u)
@ -43,41 +42,39 @@ func (g *GCSGetter) ClientMode(u *url.URL) (ClientMode, error) {
}
if strings.HasSuffix(obj.Name, "/") {
// A directory matched the prefix search, so this must be a directory
return ClientModeDir, nil
return ModeDir, nil
} else if obj.Name != object {
// A file matched the prefix search and doesn't have the same name
// as the query, so this must be a directory
return ClientModeDir, nil
return ModeDir, nil
}
}
// There are no directories or subdirectories, and if a match was returned,
// it was exactly equal to the prefix search. So return File mode
return ClientModeFile, nil
return ModeFile, nil
}
func (g *GCSGetter) Get(dst string, u *url.URL) error {
ctx := g.Context()
func (g *GCSGetter) Get(ctx context.Context, req *Request) error {
// Parse URL
bucket, object, err := g.parseURL(u)
bucket, object, err := g.parseURL(req.u)
if err != nil {
return err
}
// Remove destination if it already exists
_, err = os.Stat(dst)
_, err = os.Stat(req.Dst)
if err != nil && !os.IsNotExist(err) {
return err
}
if err == nil {
// Remove the destination
if err := os.RemoveAll(dst); err != nil {
if err := os.RemoveAll(req.Dst); err != nil {
return err
}
}
// Create all the parent directories
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
if err := os.MkdirAll(filepath.Dir(req.Dst), 0755); err != nil {
return err
}
@ -103,7 +100,7 @@ func (g *GCSGetter) Get(dst string, u *url.URL) error {
if err != nil {
return err
}
objDst = filepath.Join(dst, objDst)
objDst = filepath.Join(req.Dst, objDst)
// Download the matching object.
err = g.getObject(ctx, client, objDst, bucket, obj.Name)
if err != nil {
@ -114,11 +111,9 @@ func (g *GCSGetter) Get(dst string, u *url.URL) error {
return nil
}
func (g *GCSGetter) GetFile(dst string, u *url.URL) error {
ctx := g.Context()
func (g *GCSGetter) GetFile(ctx context.Context, req *Request) error {
// Parse URL
bucket, object, err := g.parseURL(u)
bucket, object, err := g.parseURL(req.u)
if err != nil {
return err
}
@ -127,7 +122,7 @@ func (g *GCSGetter) GetFile(dst string, u *url.URL) error {
if err != nil {
return err
}
return g.getObject(ctx, client, dst, bucket, object)
return g.getObject(ctx, client, req.Dst, bucket, object)
}
func (g *GCSGetter) getObject(ctx context.Context, client *storage.Client, dst, bucket, object string) error {

View File

@ -1,6 +1,7 @@
package getter
import (
"bytes"
"context"
"encoding/base64"
"fmt"
@ -9,6 +10,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
@ -24,12 +26,13 @@ type GitGetter struct {
getter
}
func (g *GitGetter) ClientMode(_ *url.URL) (ClientMode, error) {
return ClientModeDir, nil
var defaultBranchRegexp = regexp.MustCompile(`\s->\sorigin/(.*)`)
func (g *GitGetter) Mode(_ context.Context, u *url.URL) (Mode, error) {
return ModeDir, nil
}
func (g *GitGetter) Get(dst string, u *url.URL) error {
ctx := g.Context()
func (g *GitGetter) Get(ctx context.Context, req *Request) error {
if _, err := exec.LookPath("git"); err != nil {
return fmt.Errorf("git must be available and on the PATH")
}
@ -37,7 +40,10 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
// The port number must be parseable as an integer. If not, the user
// was probably trying to use a scp-style address, in which case the
// ssh:// prefix must be removed to indicate that.
if portStr := u.Port(); portStr != "" {
//
// This is not necessary in versions of Go which have patched
// CVE-2019-14809 (e.g. Go 1.12.8+)
if portStr := req.u.Port(); portStr != "" {
if _, err := strconv.ParseUint(portStr, 10, 16); err != nil {
return fmt.Errorf("invalid port number %q; if using the \"scp-like\" git address scheme where a colon introduces the path instead, remove the ssh:// portion and use just the git:: prefix", portStr)
}
@ -46,7 +52,7 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
// Extract some query parameters we use
var ref, sshKey string
var depth int
q := u.Query()
q := req.u.Query()
if len(q) > 0 {
ref = q.Get("ref")
q.Del("ref")
@ -60,9 +66,9 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
q.Del("depth")
// Copy the URL
var newU url.URL = *u
u = &newU
u.RawQuery = q.Encode()
var newU url.URL = *req.u
req.u = &newU
req.u.RawQuery = q.Encode()
}
var sshKeyFile string
@ -100,14 +106,14 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
}
// Clone or update the repository
_, err := os.Stat(dst)
_, err := os.Stat(req.Dst)
if err != nil && !os.IsNotExist(err) {
return err
}
if err == nil {
err = g.update(ctx, dst, sshKeyFile, ref, depth)
err = g.update(ctx, req.Dst, sshKeyFile, ref, depth)
} else {
err = g.clone(ctx, dst, sshKeyFile, u, depth)
err = g.clone(ctx, sshKeyFile, depth, req)
}
if err != nil {
return err
@ -115,18 +121,18 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
// Next: check out the proper tag/branch if it is specified, and checkout
if ref != "" {
if err := g.checkout(dst, ref); err != nil {
if err := g.checkout(req.Dst, ref); err != nil {
return err
}
}
// Lastly, download any/all submodules.
return g.fetchSubmodules(ctx, dst, sshKeyFile, depth)
return g.fetchSubmodules(ctx, req.Dst, sshKeyFile, depth)
}
// GetFile for Git doesn't support updating at this time. It will download
// the file every time.
func (g *GitGetter) GetFile(dst string, u *url.URL) error {
func (g *GitGetter) GetFile(ctx context.Context, req *Request) error {
td, tdcloser, err := safetemp.Dir("", "getter")
if err != nil {
return err
@ -135,22 +141,26 @@ func (g *GitGetter) GetFile(dst string, u *url.URL) error {
// Get the filename, and strip the filename from the URL so we can
// just get the repository directly.
filename := filepath.Base(u.Path)
u.Path = filepath.Dir(u.Path)
filename := filepath.Base(req.u.Path)
req.u.Path = filepath.Dir(req.u.Path)
dst := req.Dst
req.Dst = td
// Get the full repository
if err := g.Get(td, u); err != nil {
if err := g.Get(ctx, req); err != nil {
return err
}
// Copy the single file
u, err = urlhelper.Parse(fmtFileURL(filepath.Join(td, filename)))
req.u, err = urlhelper.Parse(fmtFileURL(filepath.Join(td, filename)))
if err != nil {
return err
}
fg := &FileGetter{Copy: true}
return fg.GetFile(dst, u)
fg := &FileGetter{}
req.Copy = true
req.Dst = dst
return fg.GetFile(ctx, req)
}
func (g *GitGetter) checkout(dst string, ref string) error {
@ -159,14 +169,14 @@ func (g *GitGetter) checkout(dst string, ref string) error {
return getRunCommand(cmd)
}
func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.URL, depth int) error {
func (g *GitGetter) clone(ctx context.Context, sshKeyFile string, depth int, req *Request) error {
args := []string{"clone"}
if depth > 0 {
args = append(args, "--depth", strconv.Itoa(depth))
}
args = append(args, u.String(), dst)
args = append(args, req.u.String(), req.Dst)
cmd := exec.CommandContext(ctx, "git", args...)
setupGitEnv(cmd, sshKeyFile)
return getRunCommand(cmd)
@ -179,10 +189,10 @@ func (g *GitGetter) update(ctx context.Context, dst, sshKeyFile, ref string, dep
cmd.Dir = dst
if getRunCommand(cmd) != nil {
// Not a branch, switch to master. This will also catch non-existent
// branches, in which case we want to switch to master and then
// checkout the proper branch later.
ref = "master"
// Not a branch, switch to default branch. This will also catch
// non-existent branches, in which case we want to switch to default
// and then checkout the proper branch later.
ref = findDefaultBranch(dst)
}
// We have to be on a branch to pull
@ -213,6 +223,22 @@ func (g *GitGetter) fetchSubmodules(ctx context.Context, dst, sshKeyFile string,
return getRunCommand(cmd)
}
// findDefaultBranch checks the repo's origin remote for its default branch
// (generally "master"). "master" is returned if an origin default branch
// can't be determined.
func findDefaultBranch(dst string) string {
var stdoutbuf bytes.Buffer
cmd := exec.Command("git", "branch", "-r", "--points-at", "refs/remotes/origin/HEAD")
cmd.Dir = dst
cmd.Stdout = &stdoutbuf
err := cmd.Run()
matches := defaultBranchRegexp.FindStringSubmatch(stdoutbuf.String())
if err != nil || matches == nil {
return "master"
}
return matches[len(matches)-1]
}
// setupGitEnv sets up the environment for the given command. This is used to
// pass configuration data to git and ssh and enables advanced cloning methods.
func setupGitEnv(cmd *exec.Cmd, sshKeyFile string) {

View File

@ -19,17 +19,16 @@ type HgGetter struct {
getter
}
func (g *HgGetter) ClientMode(_ *url.URL) (ClientMode, error) {
return ClientModeDir, nil
func (g *HgGetter) Mode(ctx context.Context, _ *url.URL) (Mode, error) {
return ModeDir, nil
}
func (g *HgGetter) Get(dst string, u *url.URL) error {
ctx := g.Context()
func (g *HgGetter) Get(ctx context.Context, req *Request) error {
if _, err := exec.LookPath("hg"); err != nil {
return fmt.Errorf("hg must be available and on the PATH")
}
newURL, err := urlhelper.Parse(u.String())
newURL, err := urlhelper.Parse(req.u.String())
if err != nil {
return err
}
@ -48,26 +47,26 @@ func (g *HgGetter) Get(dst string, u *url.URL) error {
newURL.RawQuery = q.Encode()
}
_, err = os.Stat(dst)
_, err = os.Stat(req.Dst)
if err != nil && !os.IsNotExist(err) {
return err
}
if err != nil {
if err := g.clone(dst, newURL); err != nil {
if err := g.clone(req.Dst, newURL); err != nil {
return err
}
}
if err := g.pull(dst, newURL); err != nil {
if err := g.pull(req.Dst, newURL); err != nil {
return err
}
return g.update(ctx, dst, newURL, rev)
return g.update(ctx, req.Dst, newURL, rev)
}
// GetFile for Hg doesn't support updating at this time. It will download
// the file every time.
func (g *HgGetter) GetFile(dst string, u *url.URL) error {
func (g *HgGetter) GetFile(ctx context.Context, req *Request) error {
// Create a temporary directory to store the full source. This has to be
// a non-existent directory.
td, tdcloser, err := safetemp.Dir("", "getter")
@ -78,27 +77,31 @@ func (g *HgGetter) GetFile(dst string, u *url.URL) error {
// Get the filename, and strip the filename from the URL so we can
// just get the repository directly.
filename := filepath.Base(u.Path)
u.Path = filepath.ToSlash(filepath.Dir(u.Path))
filename := filepath.Base(req.u.Path)
req.u.Path = filepath.Dir(req.u.Path)
dst := req.Dst
req.Dst = td
// If we're on Windows, we need to set the host to "localhost" for hg
if runtime.GOOS == "windows" {
u.Host = "localhost"
req.u.Host = "localhost"
}
// Get the full repository
if err := g.Get(td, u); err != nil {
if err := g.Get(ctx, req); err != nil {
return err
}
// Copy the single file
u, err = urlhelper.Parse(fmtFileURL(filepath.Join(td, filename)))
req.u, err = urlhelper.Parse(fmtFileURL(filepath.Join(td, filename)))
if err != nil {
return err
}
fg := &FileGetter{Copy: true, getter: g.getter}
return fg.GetFile(dst, u)
fg := &FileGetter{}
req.Copy = true
req.Dst = dst
return fg.GetFile(ctx, req)
}
func (g *HgGetter) clone(dst string, u *url.URL) error {

View File

@ -9,7 +9,6 @@ import (
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
safetemp "github.com/hashicorp/go-safetemp"
@ -53,22 +52,21 @@ type HttpGetter struct {
Header http.Header
}
func (g *HttpGetter) ClientMode(u *url.URL) (ClientMode, error) {
func (g *HttpGetter) Mode(ctx context.Context, u *url.URL) (Mode, error) {
if strings.HasSuffix(u.Path, "/") {
return ClientModeDir, nil
return ModeDir, nil
}
return ClientModeFile, nil
return ModeFile, nil
}
func (g *HttpGetter) Get(dst string, u *url.URL) error {
ctx := g.Context()
func (g *HttpGetter) Get(ctx context.Context, req *Request) error {
// Copy the URL so we can modify it
var newU url.URL = *u
u = &newU
var newU url.URL = *req.u
req.u = &newU
if g.Netrc {
// Add auth from netrc if we can
if err := addAuthFromNetrc(u); err != nil {
if err := addAuthFromNetrc(req.u); err != nil {
return err
}
}
@ -78,18 +76,20 @@ func (g *HttpGetter) Get(dst string, u *url.URL) error {
}
// Add terraform-get to the parameter.
q := u.Query()
q := req.u.Query()
q.Add("terraform-get", "1")
u.RawQuery = q.Encode()
req.u.RawQuery = q.Encode()
// Get the URL
req, err := http.NewRequest("GET", u.String(), nil)
httpReq, err := http.NewRequest("GET", req.u.String(), nil)
if err != nil {
return err
}
req.Header = g.Header
resp, err := g.Client.Do(req)
if g.Header != nil {
httpReq.Header = g.Header.Clone()
}
resp, err := g.Client.Do(httpReq)
if err != nil {
return err
}
@ -116,33 +116,38 @@ func (g *HttpGetter) Get(dst string, u *url.URL) error {
// If there is a subdir component, then we download the root separately
// into a temporary directory, then copy over the proper subdir.
source, subDir := SourceDirSubdir(source)
if subDir == "" {
var opts []ClientOption
if g.client != nil {
opts = g.client.Options
}
return Get(dst, source, opts...)
req = &Request{
Mode: ModeDir,
Src: source,
Dst: req.Dst,
}
if subDir == "" {
_, err = DefaultClient.Get(ctx, req)
return err
}
// We have a subdir, time to jump some hoops
return g.getSubdir(ctx, dst, source, subDir)
return g.getSubdir(ctx, req.Dst, source, subDir)
}
func (g *HttpGetter) GetFile(dst string, src *url.URL) error {
ctx := g.Context()
// GetFile fetches the file from src and stores it at dst.
// If the server supports Accept-Range, HttpGetter will attempt a range
// request. This means it is the caller's responsibility to ensure that an
// older version of the destination file does not exist, else it will be either
// falsely identified as being replaced, or corrupted with extra bytes
// appended.
func (g *HttpGetter) GetFile(ctx context.Context, req *Request) error {
if g.Netrc {
// Add auth from netrc if we can
if err := addAuthFromNetrc(src); err != nil {
if err := addAuthFromNetrc(req.u); err != nil {
return err
}
}
// Create all the parent directories if needed
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
if err := os.MkdirAll(filepath.Dir(req.Dst), 0755); err != nil {
return err
}
f, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE, os.FileMode(0666))
f, err := os.OpenFile(req.Dst, os.O_RDWR|os.O_CREATE, os.FileMode(0666))
if err != nil {
return err
}
@ -157,26 +162,25 @@ func (g *HttpGetter) GetFile(dst string, src *url.URL) error {
// We first make a HEAD request so we can check
// if the server supports range queries. If the server/URL doesn't
// support HEAD requests, we just fall back to GET.
req, err := http.NewRequest("HEAD", src.String(), nil)
httpReq, err := http.NewRequest("HEAD", req.u.String(), nil)
if err != nil {
return err
}
if g.Header != nil {
req.Header = g.Header
httpReq.Header = g.Header.Clone()
}
headResp, err := g.Client.Do(req)
if err == nil && headResp != nil {
headResp, err := g.Client.Do(httpReq)
if err == nil {
headResp.Body.Close()
if headResp.StatusCode == 200 {
// If the HEAD request succeeded, then attempt to set the range
// query if we can.
if headResp.Header.Get("Accept-Ranges") == "bytes" {
if headResp.Header.Get("Accept-Ranges") == "bytes" && headResp.ContentLength >= 0 {
if fi, err := f.Stat(); err == nil {
if _, err = f.Seek(0, os.SEEK_END); err == nil {
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", fi.Size()))
if _, err = f.Seek(0, io.SeekEnd); err == nil {
currentFileSize = fi.Size()
totalFileSize, _ := strconv.ParseInt(headResp.Header.Get("Content-Length"), 10, 64)
if currentFileSize >= totalFileSize {
httpReq.Header.Set("Range", fmt.Sprintf("bytes=%d-", currentFileSize))
if currentFileSize >= headResp.ContentLength {
// file already present
return nil
}
@ -185,9 +189,9 @@ func (g *HttpGetter) GetFile(dst string, src *url.URL) error {
}
}
}
req.Method = "GET"
httpReq.Method = "GET"
resp, err := g.Client.Do(req)
resp, err := g.Client.Do(httpReq)
if err != nil {
return err
}
@ -201,10 +205,10 @@ func (g *HttpGetter) GetFile(dst string, src *url.URL) error {
body := resp.Body
if g.client != nil && g.client.ProgressListener != nil {
if req.ProgressListener != nil {
// track download
fn := filepath.Base(src.EscapedPath())
body = g.client.ProgressListener.TrackProgress(fn, currentFileSize, currentFileSize+resp.ContentLength, resp.Body)
fn := filepath.Base(req.u.EscapedPath())
body = req.ProgressListener.TrackProgress(fn, currentFileSize, currentFileSize+resp.ContentLength, resp.Body)
}
defer body.Close()
@ -226,12 +230,8 @@ func (g *HttpGetter) getSubdir(ctx context.Context, dst, source, subDir string)
}
defer tdcloser.Close()
var opts []ClientOption
if g.client != nil {
opts = g.client.Options
}
// Download that into the given directory
if err := Get(td, source, opts...); err != nil {
if _, err := Get(ctx, td, source); err != nil {
return err
}

View File

@ -1,6 +1,7 @@
package getter
import (
"context"
"net/url"
)
@ -23,32 +24,32 @@ type MockGetter struct {
GetFileErr error
}
func (g *MockGetter) Get(dst string, u *url.URL) error {
func (g *MockGetter) Get(ctx context.Context, req *Request) error {
g.GetCalled = true
g.GetDst = dst
g.GetURL = u
g.GetDst = req.Dst
g.GetURL = req.u
if g.Proxy != nil {
return g.Proxy.Get(dst, u)
return g.Proxy.Get(ctx, req)
}
return g.GetErr
}
func (g *MockGetter) GetFile(dst string, u *url.URL) error {
func (g *MockGetter) GetFile(ctx context.Context, req *Request) error {
g.GetFileCalled = true
g.GetFileDst = dst
g.GetFileURL = u
g.GetFileDst = req.Dst
g.GetFileURL = req.u
if g.Proxy != nil {
return g.Proxy.GetFile(dst, u)
return g.Proxy.GetFile(ctx, req)
}
return g.GetFileErr
}
func (g *MockGetter) ClientMode(u *url.URL) (ClientMode, error) {
func (g *MockGetter) Mode(ctx context.Context, u *url.URL) (Mode, error) {
if l := len(u.Path); l > 0 && u.Path[l-1:] == "/" {
return ClientModeDir, nil
return ModeDir, nil
}
return ClientModeFile, nil
return ModeFile, nil
}

View File

@ -22,7 +22,7 @@ type S3Getter struct {
getter
}
func (g *S3Getter) ClientMode(u *url.URL) (ClientMode, error) {
func (g *S3Getter) Mode(ctx context.Context, u *url.URL) (Mode, error) {
// Parse URL
region, bucket, path, _, creds, err := g.parseUrl(u)
if err != nil {
@ -47,48 +47,47 @@ func (g *S3Getter) ClientMode(u *url.URL) (ClientMode, error) {
for _, o := range resp.Contents {
// Use file mode on exact match.
if *o.Key == path {
return ClientModeFile, nil
return ModeFile, nil
}
// Use dir mode if child keys are found.
if strings.HasPrefix(*o.Key, path+"/") {
return ClientModeDir, nil
return ModeDir, nil
}
}
// There was no match, so just return file mode. The download is going
// to fail but we will let S3 return the proper error later.
return ClientModeFile, nil
return ModeFile, nil
}
func (g *S3Getter) Get(dst string, u *url.URL) error {
ctx := g.Context()
func (g *S3Getter) Get(ctx context.Context, req *Request) error {
// Parse URL
region, bucket, path, _, creds, err := g.parseUrl(u)
region, bucket, path, _, creds, err := g.parseUrl(req.u)
if err != nil {
return err
}
// Remove destination if it already exists
_, err = os.Stat(dst)
_, err = os.Stat(req.Dst)
if err != nil && !os.IsNotExist(err) {
return err
}
if err == nil {
// Remove the destination
if err := os.RemoveAll(dst); err != nil {
if err := os.RemoveAll(req.Dst); err != nil {
return err
}
}
// Create all the parent directories
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
if err := os.MkdirAll(filepath.Dir(req.Dst), 0755); err != nil {
return err
}
config := g.getAWSConfig(region, u, creds)
config := g.getAWSConfig(region, req.u, creds)
sess := session.New(config)
client := s3.New(sess)
@ -96,15 +95,15 @@ func (g *S3Getter) Get(dst string, u *url.URL) error {
lastMarker := ""
hasMore := true
for hasMore {
req := &s3.ListObjectsInput{
s3Req := &s3.ListObjectsInput{
Bucket: aws.String(bucket),
Prefix: aws.String(path),
}
if lastMarker != "" {
req.Marker = aws.String(lastMarker)
s3Req.Marker = aws.String(lastMarker)
}
resp, err := client.ListObjects(req)
resp, err := client.ListObjects(s3Req)
if err != nil {
return err
}
@ -126,7 +125,7 @@ func (g *S3Getter) Get(dst string, u *url.URL) error {
if err != nil {
return err
}
objDst = filepath.Join(dst, objDst)
objDst = filepath.Join(req.Dst, objDst)
if err := g.getObject(ctx, client, objDst, bucket, objPath, ""); err != nil {
return err
@ -137,17 +136,16 @@ func (g *S3Getter) Get(dst string, u *url.URL) error {
return nil
}
func (g *S3Getter) GetFile(dst string, u *url.URL) error {
ctx := g.Context()
region, bucket, path, version, creds, err := g.parseUrl(u)
func (g *S3Getter) GetFile(ctx context.Context, req *Request) error {
region, bucket, path, version, creds, err := g.parseUrl(req.u)
if err != nil {
return err
}
config := g.getAWSConfig(region, u, creds)
config := g.getAWSConfig(region, req.u, creds)
sess := session.New(config)
client := s3.New(sess)
return g.getObject(ctx, client, dst, bucket, path, version)
return g.getObject(ctx, client, req.Dst, bucket, path, version)
}
func (g *S3Getter) getObject(ctx context.Context, client *s3.S3, dst, bucket, key, version string) error {

View File

@ -1,4 +1,4 @@
module github.com/hashicorp/go-getter
module github.com/hashicorp/go-getter/v2
require (
cloud.google.com/go v0.45.1
@ -7,6 +7,7 @@ require (
github.com/cheggaaa/pb v1.0.27
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/google/go-cmp v0.3.0
github.com/hashicorp/go-cleanhttp v0.5.0
github.com/hashicorp/go-safetemp v1.0.0
github.com/hashicorp/go-version v1.1.0
@ -21,3 +22,5 @@ require (
google.golang.org/api v0.9.0
gopkg.in/cheggaaa/pb.v1 v1.0.27 // indirect
)
go 1.13

View File

@ -0,0 +1,14 @@
package url
import (
"net/url"
)
// Parse parses rawURL into a URL structure.
// The rawURL may be relative or absolute.
//
// Parse is a wrapper for the Go stdlib net/url Parse function, but returns
// Windows "safe" URLs on Windows platforms.
func Parse(rawURL string) (*url.URL, error) {
return parse(rawURL)
}

View File

@ -0,0 +1,11 @@
// +build !windows
package url
import (
"net/url"
)
func parse(rawURL string) (*url.URL, error) {
return url.Parse(rawURL)
}

View File

@ -0,0 +1,39 @@
package url
import (
"fmt"
"net/url"
"path/filepath"
"strings"
)
func parse(rawURL string) (*url.URL, error) {
// Make sure we're using "/" since URLs are "/"-based.
rawURL = filepath.ToSlash(rawURL)
if len(rawURL) > 1 && rawURL[1] == ':' {
// Assume we're dealing with a drive letter. In which case we
// force the 'file' scheme to avoid "net/url" URL.String() prepending
// our url with "./".
rawURL = "file://" + rawURL
}
u, err := url.Parse(rawURL)
if err != nil {
return nil, err
}
if len(u.Host) > 1 && u.Host[1] == ':' && strings.HasPrefix(rawURL, "file://") {
// Assume we're dealing with a drive letter file path where the drive
// letter has been parsed into the URL Host.
u.Path = fmt.Sprintf("%s%s", u.Host, u.Path)
u.Host = ""
}
// Remove leading slash for absolute file paths.
if len(u.Path) > 2 && u.Path[0] == '/' && u.Path[2] == ':' {
u.Path = u.Path[1:]
}
return u, err
}

View File

@ -1,24 +1,24 @@
package getter
// ClientMode is the mode that the client operates in.
type ClientMode uint
// Mode is the mode that the client operates in.
type Mode uint
const (
ClientModeInvalid ClientMode = iota
ModeInvalid Mode = iota
// ClientModeAny downloads anything it can. In this mode, dst must
// ModeAny downloads anything it can. In this mode, dst must
// be a directory. If src is a file, it is saved into the directory
// with the basename of the URL. If src is a directory or archive,
// it is unpacked directly into dst.
ClientModeAny
ModeAny
// ClientModeFile downloads a single file. In this mode, dst must
// ModeFile downloads a single file. In this mode, dst must
// be a file path (doesn't have to exist). src must point to a single
// file. It is saved as dst.
ClientModeFile
ModeFile
// ClientModeDir downloads a directory. In this mode, dst must be
// ModeDir downloads a directory. In this mode, dst must be
// a directory path (doesn't have to exist). src must point to an
// archive or directory (such as in s3).
ClientModeDir
ModeDir
)

39
vendor/github.com/hashicorp/go-getter/v2/request.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
package getter
import "net/url"
type Request struct {
// Src is the source URL to get.
//
// Dst is the path to save the downloaded thing as. If Dir is set to
// true, then this should be a directory. If the directory doesn't exist,
// it will be created for you.
//
// Pwd is the working directory for detection. If this isn't set, some
// detection may fail. Client will not default pwd to the current
// working directory for security reasons.
Src string
Dst string
Pwd string
// Mode is the method of download the client will use. See Mode
// for documentation.
Mode Mode
// Copy, in local file mode if set to true, will copy data instead of using
// a symlink. If false, attempts to symlink to speed up the operation and
// to lower the disk space usage. If the symlink fails, may attempt to copy
// on windows.
Copy bool
// Inplace, in local file mode if set to true, do nothing and the returned
// operation will simply contain the source file path. Inplace has precedence
// over Copy.
Inplace bool
// ProgressListener allows to track file downloads.
// By default a no op progress listener is used.
ProgressListener ProgressTracker
u *url.URL
}

View File

@ -1,5 +1,7 @@
package getter
import "context"
// Storage is an interface that knows how to lookup downloaded directories
// as well as download and update directories from their sources into the
// proper location.
@ -9,5 +11,5 @@ type Storage interface {
Dir(string) (string, bool, error)
// Get will download and optionally update the given directory.
Get(string, string, bool) error
Get(context.Context, string, string, bool) error
}

4
vendor/modules.txt vendored
View File

@ -318,8 +318,10 @@ github.com/hashicorp/go-cty-funcs/filesystem
# github.com/hashicorp/go-cty-funcs/uuid v0.0.0-20200203151509-c92509f48b18
github.com/hashicorp/go-cty-funcs/uuid
# github.com/hashicorp/go-getter v1.3.1-0.20190906090232-a0f878cb75da
github.com/hashicorp/go-getter
github.com/hashicorp/go-getter/helper/url
# github.com/hashicorp/go-getter/v2 v2.0.0-20200206160058-e2a28063d6e7
github.com/hashicorp/go-getter/v2
github.com/hashicorp/go-getter/v2/helper/url
# github.com/hashicorp/go-immutable-radix v1.0.0
github.com/hashicorp/go-immutable-radix
# github.com/hashicorp/go-multierror v1.0.0