Allow to use isos in place
This commit is contained in:
parent
193dad46e6
commit
a19214afeb
@ -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"
|
||||
)
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
3
go.mod
@ -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
2
go.sum
@ -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=
|
||||
|
@ -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")
|
||||
|
24
vendor/github.com/hashicorp/go-getter/.travis.yml
generated
vendored
24
vendor/github.com/hashicorp/go-getter/.travis.yml
generated
vendored
@ -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
|
46
vendor/github.com/hashicorp/go-getter/client_option.go
generated
vendored
46
vendor/github.com/hashicorp/go-getter/client_option.go
generated
vendored
@ -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
|
||||
}
|
||||
}
|
20
vendor/github.com/hashicorp/go-getter/get_base.go
generated
vendored
20
vendor/github.com/hashicorp/go-getter/get_base.go
generated
vendored
@ -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
|
||||
}
|
36
vendor/github.com/hashicorp/go-getter/get_file.go
generated
vendored
36
vendor/github.com/hashicorp/go-getter/get_file.go
generated
vendored
@ -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
|
||||
}
|
103
vendor/github.com/hashicorp/go-getter/get_file_unix.go
generated
vendored
103
vendor/github.com/hashicorp/go-getter/get_file_unix.go
generated
vendored
@ -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
1
vendor/github.com/hashicorp/go-getter/v2/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
vendor
|
354
vendor/github.com/hashicorp/go-getter/v2/LICENSE
generated
vendored
Normal file
354
vendor/github.com/hashicorp/go-getter/v2/LICENSE
generated
vendored
Normal 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 Contributor’s 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 party’s
|
||||
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
|
||||
party’s 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 party’s 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.
|
||||
|
@ -1,10 +1,10 @@
|
||||
# go-getter
|
||||
|
||||
[][travis]
|
||||
[][circleci]
|
||||
[][appveyor]
|
||||
[][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.
|
@ -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
|
@ -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
|
||||
}
|
22
vendor/github.com/hashicorp/go-getter/v2/client_option.go
generated
vendored
Normal file
22
vendor/github.com/hashicorp/go-getter/v2/client_option.go
generated
vendored
Normal 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
|
||||
}
|
@ -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
|
@ -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 {
|
@ -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
|
@ -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
9
vendor/github.com/hashicorp/go-getter/v2/get_base.go
generated
vendored
Normal 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 }
|
@ -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)
|
||||
}
|
10
vendor/github.com/hashicorp/go-getter/v2/get_file_symlink.go
generated
vendored
Normal file
10
vendor/github.com/hashicorp/go-getter/v2/get_file_symlink.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
// +build !windows
|
||||
|
||||
package getter
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
var ErrUnauthorized = os.ErrPermission
|
||||
var SymlinkAny = os.Symlink
|
21
vendor/github.com/hashicorp/go-getter/v2/get_file_symlink_windows.go
generated
vendored
Normal file
21
vendor/github.com/hashicorp/go-getter/v2/get_file_symlink_windows.go
generated
vendored
Normal 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
|
@ -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 {
|
@ -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) {
|
@ -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 {
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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 {
|
@ -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
|
14
vendor/github.com/hashicorp/go-getter/v2/helper/url/url.go
generated
vendored
Normal file
14
vendor/github.com/hashicorp/go-getter/v2/helper/url/url.go
generated
vendored
Normal 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)
|
||||
}
|
11
vendor/github.com/hashicorp/go-getter/v2/helper/url/url_unix.go
generated
vendored
Normal file
11
vendor/github.com/hashicorp/go-getter/v2/helper/url/url_unix.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// +build !windows
|
||||
|
||||
package url
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func parse(rawURL string) (*url.URL, error) {
|
||||
return url.Parse(rawURL)
|
||||
}
|
39
vendor/github.com/hashicorp/go-getter/v2/helper/url/url_windows.go
generated
vendored
Normal file
39
vendor/github.com/hashicorp/go-getter/v2/helper/url/url_windows.go
generated
vendored
Normal 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
|
||||
}
|
@ -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
39
vendor/github.com/hashicorp/go-getter/v2/request.go
generated
vendored
Normal 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
|
||||
}
|
@ -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
4
vendor/modules.txt
vendored
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user