-
Notifications
You must be signed in to change notification settings - Fork 241
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 4133bae
Showing
112 changed files
with
3,475 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package getter | ||
|
||
import ( | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
// copyDir copies the src directory contents into dst. Both directories | ||
// should already exist. | ||
func copyDir(dst, src string) error { | ||
src, err := filepath.EvalSymlinks(src) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
walkFn := func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
if path == src { | ||
return nil | ||
} | ||
|
||
if strings.HasPrefix(filepath.Base(path), ".") { | ||
// Skip any dot files | ||
if info.IsDir() { | ||
return filepath.SkipDir | ||
} else { | ||
return nil | ||
} | ||
} | ||
|
||
// The "path" has the src prefixed to it. We need to join our | ||
// destination with the path without the src on it. | ||
dstPath := filepath.Join(dst, path[len(src):]) | ||
|
||
// If we have a directory, make that subdirectory, then continue | ||
// the walk. | ||
if info.IsDir() { | ||
if path == filepath.Join(src, dst) { | ||
// dst is in src; don't walk it. | ||
return nil | ||
} | ||
|
||
if err := os.MkdirAll(dstPath, 0755); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// If we have a file, copy the contents. | ||
srcF, err := os.Open(path) | ||
if err != nil { | ||
return err | ||
} | ||
defer srcF.Close() | ||
|
||
dstF, err := os.Create(dstPath) | ||
if err != nil { | ||
return err | ||
} | ||
defer dstF.Close() | ||
|
||
if _, err := io.Copy(dstF, srcF); err != nil { | ||
return err | ||
} | ||
|
||
// Chmod it | ||
return os.Chmod(dstPath, info.Mode()) | ||
} | ||
|
||
return filepath.Walk(src, walkFn) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package getter | ||
|
||
import ( | ||
"fmt" | ||
"path/filepath" | ||
|
||
"github.com/hashicorp/terraform/helper/url" | ||
) | ||
|
||
// Detector defines the interface that an invalid URL or a URL with a blank | ||
// scheme is passed through in order to determine if its shorthand for | ||
// something else well-known. | ||
type Detector interface { | ||
// Detect will detect whether the string matches a known pattern to | ||
// turn it into a proper URL. | ||
Detect(string, string) (string, bool, error) | ||
} | ||
|
||
// Detectors is the list of detectors that are tried on an invalid URL. | ||
// This is also the order they're tried (index 0 is first). | ||
var Detectors []Detector | ||
|
||
func init() { | ||
Detectors = []Detector{ | ||
new(GitHubDetector), | ||
new(BitBucketDetector), | ||
new(FileDetector), | ||
} | ||
} | ||
|
||
// Detect turns a source string into another source string if it is | ||
// detected to be of a known pattern. | ||
// | ||
// This is safe to be called with an already valid source string: Detect | ||
// will just return it. | ||
func Detect(src string, pwd string) (string, error) { | ||
getForce, getSrc := getForcedGetter(src) | ||
|
||
// Separate out the subdir if there is one, we don't pass that to detect | ||
getSrc, subDir := getDirSubdir(getSrc) | ||
|
||
u, err := url.Parse(getSrc) | ||
if err == nil && u.Scheme != "" { | ||
// Valid URL | ||
return src, nil | ||
} | ||
|
||
for _, d := range Detectors { | ||
result, ok, err := d.Detect(getSrc, pwd) | ||
if err != nil { | ||
return "", err | ||
} | ||
if !ok { | ||
continue | ||
} | ||
|
||
var detectForce string | ||
detectForce, result = getForcedGetter(result) | ||
result, detectSubdir := getDirSubdir(result) | ||
|
||
// If we have a subdir from the detection, then prepend it to our | ||
// requested subdir. | ||
if detectSubdir != "" { | ||
if subDir != "" { | ||
subDir = filepath.Join(detectSubdir, subDir) | ||
} else { | ||
subDir = detectSubdir | ||
} | ||
} | ||
if subDir != "" { | ||
u, err := url.Parse(result) | ||
if err != nil { | ||
return "", fmt.Errorf("Error parsing URL: %s", err) | ||
} | ||
u.Path += "//" + subDir | ||
result = u.String() | ||
} | ||
|
||
// Preserve the forced getter if it exists. We try to use the | ||
// original set force first, followed by any force set by the | ||
// detector. | ||
if getForce != "" { | ||
result = fmt.Sprintf("%s::%s", getForce, result) | ||
} else if detectForce != "" { | ||
result = fmt.Sprintf("%s::%s", detectForce, result) | ||
} | ||
|
||
return result, nil | ||
} | ||
|
||
return "", fmt.Errorf("invalid source string: %s", src) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package getter | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
) | ||
|
||
// BitBucketDetector implements Detector to detect BitBucket URLs and turn | ||
// them into URLs that the Git or Hg Getter can understand. | ||
type BitBucketDetector struct{} | ||
|
||
func (d *BitBucketDetector) Detect(src, _ string) (string, bool, error) { | ||
if len(src) == 0 { | ||
return "", false, nil | ||
} | ||
|
||
if strings.HasPrefix(src, "bitbucket.org/") { | ||
return d.detectHTTP(src) | ||
} | ||
|
||
return "", false, nil | ||
} | ||
|
||
func (d *BitBucketDetector) detectHTTP(src string) (string, bool, error) { | ||
u, err := url.Parse("https://" + src) | ||
if err != nil { | ||
return "", true, fmt.Errorf("error parsing BitBucket URL: %s", err) | ||
} | ||
|
||
// We need to get info on this BitBucket repository to determine whether | ||
// it is Git or Hg. | ||
var info struct { | ||
SCM string `json:"scm"` | ||
} | ||
infoUrl := "https://api.bitbucket.org/1.0/repositories" + u.Path | ||
resp, err := http.Get(infoUrl) | ||
if err != nil { | ||
return "", true, fmt.Errorf("error looking up BitBucket URL: %s", err) | ||
} | ||
if resp.StatusCode == 403 { | ||
// A private repo | ||
return "", true, fmt.Errorf( | ||
"shorthand BitBucket URL can't be used for private repos, " + | ||
"please use a full URL") | ||
} | ||
dec := json.NewDecoder(resp.Body) | ||
if err := dec.Decode(&info); err != nil { | ||
return "", true, fmt.Errorf("error looking up BitBucket URL: %s", err) | ||
} | ||
|
||
switch info.SCM { | ||
case "git": | ||
if !strings.HasSuffix(u.Path, ".git") { | ||
u.Path += ".git" | ||
} | ||
|
||
return "git::" + u.String(), true, nil | ||
case "hg": | ||
return "hg::" + u.String(), true, nil | ||
default: | ||
return "", true, fmt.Errorf("unknown BitBucket SCM type: %s", info.SCM) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package getter | ||
|
||
import ( | ||
"net/http" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
const testBBUrl = "https://bitbucket.org/hashicorp/tf-test-git" | ||
|
||
func TestBitBucketDetector(t *testing.T) { | ||
t.Parallel() | ||
|
||
if _, err := http.Get(testBBUrl); err != nil { | ||
t.Log("internet may not be working, skipping BB tests") | ||
t.Skip() | ||
} | ||
|
||
cases := []struct { | ||
Input string | ||
Output string | ||
}{ | ||
// HTTP | ||
{ | ||
"bitbucket.org/hashicorp/tf-test-git", | ||
"git::https://bitbucket.org/hashicorp/tf-test-git.git", | ||
}, | ||
{ | ||
"bitbucket.org/hashicorp/tf-test-git.git", | ||
"git::https://bitbucket.org/hashicorp/tf-test-git.git", | ||
}, | ||
{ | ||
"bitbucket.org/hashicorp/tf-test-hg", | ||
"hg::https://bitbucket.org/hashicorp/tf-test-hg", | ||
}, | ||
} | ||
|
||
pwd := "/pwd" | ||
f := new(BitBucketDetector) | ||
for i, tc := range cases { | ||
var err error | ||
for i := 0; i < 3; i++ { | ||
var output string | ||
var ok bool | ||
output, ok, err = f.Detect(tc.Input, pwd) | ||
if err != nil { | ||
if strings.Contains(err.Error(), "invalid character") { | ||
continue | ||
} | ||
|
||
t.Fatalf("err: %s", err) | ||
} | ||
if !ok { | ||
t.Fatal("not ok") | ||
} | ||
|
||
if output != tc.Output { | ||
t.Fatalf("%d: bad: %#v", i, output) | ||
} | ||
|
||
break | ||
} | ||
if i >= 3 { | ||
t.Fatalf("failure from bitbucket: %s", err) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package getter | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
) | ||
|
||
// FileDetector implements Detector to detect file paths. | ||
type FileDetector struct{} | ||
|
||
func (d *FileDetector) Detect(src, pwd string) (string, bool, error) { | ||
if len(src) == 0 { | ||
return "", false, nil | ||
} | ||
|
||
if !filepath.IsAbs(src) { | ||
if pwd == "" { | ||
return "", true, fmt.Errorf( | ||
"relative paths require a module with a pwd") | ||
} | ||
|
||
// Stat the pwd to determine if its a symbolic link. If it is, | ||
// then the pwd becomes the original directory. Otherwise, | ||
// `filepath.Join` below does some weird stuff. | ||
// | ||
// We just ignore if the pwd doesn't exist. That error will be | ||
// caught later when we try to use the URL. | ||
if fi, err := os.Lstat(pwd); !os.IsNotExist(err) { | ||
if err != nil { | ||
return "", true, err | ||
} | ||
if fi.Mode()&os.ModeSymlink != 0 { | ||
pwd, err = os.Readlink(pwd) | ||
if err != nil { | ||
return "", true, err | ||
} | ||
} | ||
} | ||
|
||
src = filepath.Join(pwd, src) | ||
} | ||
|
||
return fmtFileURL(src), true, nil | ||
} | ||
|
||
func fmtFileURL(path string) string { | ||
if runtime.GOOS == "windows" { | ||
// Make sure we're using "/" on Windows. URLs are "/"-based. | ||
path = filepath.ToSlash(path) | ||
return fmt.Sprintf("file://%s", path) | ||
} | ||
|
||
// Make sure that we don't start with "/" since we add that below. | ||
if path[0] == '/' { | ||
path = path[1:] | ||
} | ||
return fmt.Sprintf("file:///%s", path) | ||
} |
Oops, something went wrong.